313 lines
11 KiB
Python
Executable File
313 lines
11 KiB
Python
Executable File
#!/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()
|