#!/usr/bin/env python3 import requests import argparse status = {'OK': 0, 'WARNING': 1, 'CRITICAL': 2, 'UNKNOWN': 3} class APIError(Exception): pass class Check: def __init__(self, status: str, description: str, data: dict[str,str] | None): self.status = status self.description = description self.data = data def format(self) -> str: if self.data != None: formatted_data: str = ' '.join(map(lambda t: f'{t[0]}={t[1]}', dict_to_list(self.data))) return f"{self.status} - {self.description} | {formatted_data}" else: return f"{self.status} - {self.description}" def exit(self): print(self.format()) exit(status[self.status]) def dict_to_list(dic): return [(k, v) for k,v in dic.items()] parser = argparse.ArgumentParser(description='Check nextcloud status via api') parser.add_argument('-t', dest='token', action='store', type=str, required=True) parser.add_argument('-H', dest='hostname', action='store', type=str, required=True) parser.add_argument('-C', dest='check', choices=['apps','cache','users'], required=True) #TODO is there a naming convention? parser.add_argument('-w', dest='warning', action='store', type=str, default=None) parser.add_argument('-c', dest='critical', action='store', type=str, default=None) args = parser.parse_args() #TODO: Looks like I'm checking the wrong cache value, the nextcloud does not give a warning when the interned strings cache # is over 95% filled def check_interned_strings_cache(data_json, warning: float, critical: float) -> Check: data_opcache = data_json['ocs']['data']['server']['php']['opcache'] cache_max = data_opcache['interned_strings_usage']['buffer_size'] cache_used = data_opcache['interned_strings_usage']['used_memory'] / cache_max data = {'interned_string_cache' : f'{cache_used:.0%};{warning*100:.0f};{critical*100:.0f}'} if cache_used >= critical: msg = "The OPcache interned strings buffer is full." if critical == 1 \ else f"OPcache interned strings buffer is to {cache_used:.0%} filled. " + f"This should not be more than {critical:.0%}!" return Check('CRITICAL', msg, data) if cache_used >= warning: return Check('WARNING', f"OPcache interned strings buffer is to {cache_used:.0%} filled. " + f"This should not be more than {warning:.0%}!", data) return Check('OK', f"OPcache interned strings buffer is to {cache_used:.0%} filled.", data) def check_activer_users(data_json, warning: int, critical: int) -> Check: active_users = data_json['ocs']['data']['activeUsers']['last5minutes'] if active_users >= critical: status = 'CRITICAL' elif active_users >= warning: status = 'WARNING' else: status = 'OK' data = {'active_users' : f'{active_users};{warning};{critical}'} return Check( status, f'{active_users} {"users were" if active_users != 1 else "user was"} active in the last 5 minutes.', data) def check_app_updates(data_json) -> Check: updates = data_json['ocs']['data']['nextcloud']['system']['apps']['app_updates'] if updates != []: return Check('WARNING', f'The following apps can be updated: {" ".join(updates)}.', None) else: return Check('OK', 'No app updates were found.', None) try: data_json = requests.get(f'https://{args.hostname}/ocs/v2.php/apps/serverinfo/api/v1/info?format=json' , headers={'NC-Token': args.token, 'host': args.hostname}).json() if data_json['ocs']['meta']['status'] == 'failure': raise APIError('API call failed!') if args.check == 'apps': check_app_updates(data_json).exit() elif args.check == 'cache': if args.warning == None or args.critical == None: print('Wrong usage! When using the cache check, provide warning and critical') exit(1) warning, critical = map(float, (args.warning, args.critical)) check_interned_strings_cache(data_json, warning, critical).exit() elif args.check == 'users': if args.warning == None or args.critical == None: print('Wrong usage! When using the users check, provide warning and critical') exit(1) warning, critical = map(int, (args.warning, args.critical)) check_activer_users(data_json, warning, critical).exit() except APIError as ex: Check('UNKNOWN', str(ex), None).exit() except KeyError as ex: Check('UNKNOWN', f'The key {ex} could not be found. Did the API change?', None).exit()