initial upload of version 3.1.1 which uses the new plugin API
This commit is contained in:
312
nextcloud/libexec/agent_nextcloud
Executable file
312
nextcloud/libexec/agent_nextcloud
Executable file
@@ -0,0 +1,312 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=missing-module-docstring, missing-class-docstring, missing-function-docstring
|
||||
# pylint: disable=too-many-branches, line-too-long, too-many-statements
|
||||
# pylint: disable=too-many-arguments, too-many-positional-arguments, too-many-locals
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import sys
|
||||
import requests
|
||||
import urllib3
|
||||
from requests.structures import CaseInsensitiveDict
|
||||
|
||||
NC_API_ENDPOINT = "ocs/v2.php/apps/serverinfo/api/v1/info?format=json"
|
||||
NC_API_ENDPOINT_ALL_USERS = "ocs/v1.php/cloud/users?format=json"
|
||||
NC_API_ENDPOINT_USER = "ocs/v1.php/cloud/users"
|
||||
|
||||
|
||||
def get_args() -> argparse.Namespace:
|
||||
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
||||
description="Nextcloud server parameters"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--hostname", required=True, type=str, help="Hostname or IP of nextcloud server"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--username", required=True, type=str, help="User with admin privileges"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--password", required=True, type=str, help="App password for the user"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
required=False,
|
||||
type=int,
|
||||
help="Port where the server is listening on (if not HTTP/HTTPS)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--folder",
|
||||
required=False,
|
||||
type=str,
|
||||
help="Folder if not installed in web root directory",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-https",
|
||||
required=False,
|
||||
default=False,
|
||||
help="Disable HTTPS",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-cert-check",
|
||||
required=False,
|
||||
default=False,
|
||||
help="Disable certificate checks",
|
||||
action="store_true",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def create_url(endpoint, hostname, protocol, port, folder):
|
||||
# these parameters are needed, otherwise no information about updates regarding apps and Nextcloud itself are reported (since version 28)
|
||||
params = "skipApps=false&skipUpdate=false"
|
||||
if folder == "":
|
||||
url = f"{protocol}://{hostname}:{port}/{endpoint}"
|
||||
else:
|
||||
url = f"{protocol}://{hostname}:{port}/{folder}/{endpoint}"
|
||||
if endpoint == NC_API_ENDPOINT:
|
||||
url = f"{url}&{params}"
|
||||
return url
|
||||
|
||||
|
||||
def create_url_user(user, endpoint, hostname, protocol, port, folder):
|
||||
params = "format=json"
|
||||
if folder == "":
|
||||
url = f"{protocol}://{hostname}:{port}/{endpoint}/{user}?{params}"
|
||||
else:
|
||||
url = f"{protocol}://{hostname}:{port}/{folder}/{endpoint}/{user}?{params}"
|
||||
return url
|
||||
|
||||
|
||||
def get_session(username, secret):
|
||||
session = requests.session()
|
||||
session.cookies.set("SameSite", "Strict")
|
||||
session.auth = (username, secret)
|
||||
session.headers["Accept"] = "application/json"
|
||||
return session
|
||||
|
||||
|
||||
def get_data(session, url, verify):
|
||||
data = {}
|
||||
headers = CaseInsensitiveDict()
|
||||
headers["Accept"] = "application/json"
|
||||
response = session.get(url, headers=headers, verify=verify)
|
||||
status = response.status_code
|
||||
if status == 200:
|
||||
jsdata = response.text
|
||||
data = json.loads(jsdata) # returns a dictionary
|
||||
return data
|
||||
if status == 503:
|
||||
# this code is reported when maintenance mode is on
|
||||
sys.stderr.write(
|
||||
f"Request response code is {response.status_code} with URL {url}, maybe maintenance mode is on?\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.stderr.write(
|
||||
f"Request response code is {response.status_code} with URL {url}\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_data_all_users(session, url, verify):
|
||||
headers = CaseInsensitiveDict()
|
||||
headers["Accept"] = "application/json"
|
||||
headers["OCS-APIRequest"] = "true"
|
||||
response = session.get(url, headers=headers, verify=verify)
|
||||
status = response.status_code
|
||||
if status == 200:
|
||||
jsdata = response.text
|
||||
data = json.loads(jsdata) # returns a dictionary
|
||||
return data
|
||||
sys.stderr.write(
|
||||
f"Request response code is {response.status_code} with URL {url}\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_data_user(session, url, verify):
|
||||
headers = CaseInsensitiveDict()
|
||||
headers["Accept"] = "application/json"
|
||||
headers["OCS-APIRequest"] = "true"
|
||||
response = session.get(url, headers=headers, verify=verify)
|
||||
status = response.status_code
|
||||
if status == 200:
|
||||
jsdata = response.text
|
||||
data = json.loads(jsdata) # returns a dictionary
|
||||
return data
|
||||
|
||||
sys.stderr.write(
|
||||
f"Request response code is {response.status_code} with URL {url}\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def do_cmk_output(data):
|
||||
apps_with_updates_available = {}
|
||||
str_apps_with_updates_available = ""
|
||||
print("<<<nextcloud_info:sep(59)>>>")
|
||||
print(f"NC_Version;{data['ocs']['data']['nextcloud']['system']['version']}")
|
||||
print(f"NC_Freespace;{data['ocs']['data']['nextcloud']['system']['freespace']}")
|
||||
print(f"NC_Status;{data['ocs']['meta']['status']}")
|
||||
# This update info is available only from version 28 onwards, so the key "update" does not exist in all versions before
|
||||
try:
|
||||
print(
|
||||
f"NC_Last_Update;{data['ocs']['data']['nextcloud']['system']['update']['lastupdatedat']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Update_Available;{data['ocs']['data']['nextcloud']['system']['update']['available']}"
|
||||
)
|
||||
except KeyError:
|
||||
# TBD
|
||||
pass
|
||||
print(f"NC_Num_Users;{data['ocs']['data']['nextcloud']['storage']['num_users']}")
|
||||
print(f"NC_Num_Files;{data['ocs']['data']['nextcloud']['storage']['num_files']}")
|
||||
print(f"NC_Num_Shares;{data['ocs']['data']['nextcloud']['shares']['num_shares']}")
|
||||
print(
|
||||
f"NC_Num_Storages;{data['ocs']['data']['nextcloud']['storage']['num_storages']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Num_Storages_Home;{data['ocs']['data']['nextcloud']['storage']['num_storages_home']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Num_Storages_Local;{data['ocs']['data']['nextcloud']['storage']['num_storages_local']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Num_Storages_Other;{data['ocs']['data']['nextcloud']['storage']['num_storages_other']}"
|
||||
)
|
||||
# Workaround for Nextcloud 28.0.1 (KeyError "apps")
|
||||
try:
|
||||
print(
|
||||
f"NC_Num_Apps_Installed;{data['ocs']['data']['nextcloud']['system']['apps']['num_installed']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Num_Apps_Updates_Available;{data['ocs']['data']['nextcloud']['system']['apps']['num_updates_available']}"
|
||||
)
|
||||
apps_with_updates_available = data["ocs"]["data"]["nextcloud"]["system"][
|
||||
"apps"
|
||||
]["app_updates"]
|
||||
if apps_with_updates_available:
|
||||
for app, version in apps_with_updates_available.items():
|
||||
str_apps_with_updates_available = (
|
||||
str_apps_with_updates_available + app + "/" + version + " "
|
||||
)
|
||||
print(
|
||||
f"NC_Apps_With_Updates_Available;{str_apps_with_updates_available}"
|
||||
)
|
||||
except KeyError:
|
||||
# TBD
|
||||
pass
|
||||
print(
|
||||
f"NC_Active_Users_Last_5Min;{data['ocs']['data']['activeUsers']['last5minutes']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Active_Users_Last_1Hour;{data['ocs']['data']['activeUsers']['last1hour']}"
|
||||
)
|
||||
print(
|
||||
f"NC_Active_Users_Last_1Day;{data['ocs']['data']['activeUsers']['last24hours']}"
|
||||
)
|
||||
print(f"NC_Webserver;{data['ocs']['data']['server']['webserver']}")
|
||||
print(f"NC_PHP_Version;{data['ocs']['data']['server']['php']['version']}")
|
||||
|
||||
print("<<<nextcloud_database:sep(59)>>>")
|
||||
print(f"NC_Database_Type;{data['ocs']['data']['server']['database']['type']}")
|
||||
print(f"NC_Database_Version;{data['ocs']['data']['server']['database']['version']}")
|
||||
print(f"NC_Database_Size;{data['ocs']['data']['server']['database']['size']}")
|
||||
# opcache entry does not exist if opcache_get_status is disabled by server settings
|
||||
# thanks to Marcus Klein from Iteratio to report (and solve!) this bug
|
||||
if data["ocs"]["data"]["server"]["php"]["opcache"]:
|
||||
print(
|
||||
f"NC_OPCache_Hit_Rate;{data['ocs']['data']['server']['php']['opcache']['opcache_statistics']['opcache_hit_rate']}"
|
||||
)
|
||||
else:
|
||||
print("NC_OPCache_Hit_Rate;0")
|
||||
|
||||
|
||||
def do_cmk_output_all_users(session, data, verify, hostname, protocol, port, folder):
|
||||
print("<<<nextcloud_users:sep(59)>>>")
|
||||
for user in data["ocs"]["data"]["users"]:
|
||||
nc_url = create_url_user(
|
||||
user, NC_API_ENDPOINT_USER, hostname, protocol, port, folder
|
||||
)
|
||||
user_data = get_data_user(session, nc_url, verify)
|
||||
userid = user_data["ocs"]["data"]["id"]
|
||||
displayname = user_data["ocs"]["data"]["displayname"]
|
||||
lastlogin = int(user_data["ocs"]["data"]["lastLogin"])
|
||||
if lastlogin == 0:
|
||||
# user has never logged in
|
||||
quota_free = -1
|
||||
quota_quota = -1
|
||||
quota_relative = -1
|
||||
quota_total = -1
|
||||
quota_used = -1
|
||||
else:
|
||||
quota_free = user_data["ocs"]["data"]["quota"]["free"]
|
||||
# quota_quota == -3 --> unlimited
|
||||
quota_quota = user_data["ocs"]["data"]["quota"]["quota"]
|
||||
# quota_relative = used * 100 / (free + used)
|
||||
quota_relative = user_data["ocs"]["data"]["quota"]["relative"]
|
||||
quota_total = user_data["ocs"]["data"]["quota"]["total"]
|
||||
quota_used = user_data["ocs"]["data"]["quota"]["used"]
|
||||
print(
|
||||
f"{userid};{displayname};{lastlogin};{quota_free};{quota_quota};{quota_relative};{quota_total};{quota_used}"
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
# get and check parameters
|
||||
params: argparse.Namespace = get_args()
|
||||
if params.hostname is None:
|
||||
sys.stderr.write("No hostname given.\n")
|
||||
sys.exit(1)
|
||||
if params.username is None:
|
||||
sys.stderr.write("No username given.\n")
|
||||
sys.exit(1)
|
||||
if params.password is None:
|
||||
sys.stderr.write("No app password given.\n")
|
||||
sys.exit(1)
|
||||
hostname = params.hostname
|
||||
username = params.username
|
||||
pwd = params.password
|
||||
if params.folder is None:
|
||||
folder = ""
|
||||
else:
|
||||
folder = params.folder
|
||||
if params.no_cert_check:
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
verify = False
|
||||
else:
|
||||
verify = True
|
||||
if params.port is None:
|
||||
if params.no_https:
|
||||
protocol = "http"
|
||||
port = "80"
|
||||
else:
|
||||
protocol = "https"
|
||||
port = "443"
|
||||
else:
|
||||
if params.no_https:
|
||||
protocol = "http"
|
||||
else:
|
||||
protocol = "https"
|
||||
port = params.port
|
||||
if protocol == "http" and port == "443":
|
||||
sys.stderr.write("Combining HTTP with port 443 is not supported.\n")
|
||||
sys.exit(1)
|
||||
if protocol == "https" and port == "80":
|
||||
sys.stderr.write("Combining HTTPS with port 80 is not supported.\n")
|
||||
sys.exit(1)
|
||||
|
||||
# create session
|
||||
session = get_session(username, pwd)
|
||||
nc_url = create_url(NC_API_ENDPOINT, hostname, protocol, port, folder)
|
||||
nc_data = get_data(session, nc_url, verify)
|
||||
do_cmk_output(nc_data)
|
||||
nc_url = create_url(NC_API_ENDPOINT_ALL_USERS, hostname, protocol, port, folder)
|
||||
nc_data = get_data_all_users(session, nc_url, verify)
|
||||
do_cmk_output_all_users(session, nc_data, verify, hostname, protocol, port, folder)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user