From 2549a19c674a9c198a5af4c7a8dc2105a7c3707f Mon Sep 17 00:00:00 2001 From: Ivan Casado Date: Fri, 11 Sep 2026 11:02:18 +0200 Subject: [PATCH 1/2] 2.3 -- added "--tracehttp" parameter 2.3 -- added "--tracehttp" parameter to show http requests/reponses --- check_nextcloud.py | 391 ++++++++++++++++++++++++--------------------- 1 file changed, 206 insertions(+), 185 deletions(-) diff --git a/check_nextcloud.py b/check_nextcloud.py index 8871350..65c84dc 100644 --- a/check_nextcloud.py +++ b/check_nextcloud.py @@ -22,9 +22,11 @@ # - Migrated from Python 2.7 to 3 (Thanks @waja) # ~~ Version 2.1 ~~ # - Parameter "--context" added for deployments where nexcloud is not in the root of the webserver -# - Check for Nextcloud core and app updates added ("-c updates") +# - Check for Nextcloud core and app updates added ("-c updates") (Thanks @casadoi) # ~~ Version 2.2 ~~ # - Bugfix for uploadFilesize check +# ~~ Version 2.3 ~~ +# - Parameter "--tracehttp" added (Thanks @casadoi) # ################# @@ -41,13 +43,13 @@ def calc_size_suffix(num, suffix='B'): return "%.1f%s%s" % (num, 'Yi', suffix) def calc_size_nagios(num, suffix='B'): - for unit in ['','K','M','G','T','P','E','Z']: - if abs(num) < 1000.0: - return "%3.1f%s%s" % (num, unit, suffix) + for unit in ['','K','M','G','T','P','E','Z']: + if abs(num) < 1000.0: + return "%3.1f%s%s" % (num, unit, suffix) - num /= 1000.0 + num /= 1000.0 - return "%.1f%s%s" % (num, 'Y', suffix) + return "%.1f%s%s" % (num, 'Y', suffix) def convert_size_to_bytes(size_str): """Convert human-readable file sizes to bytes. @@ -114,34 +116,35 @@ def convert_size_to_bytes(size_str): parser.add_option('--ignore-sslcert', dest='ignore_sslcert', default=False, action='store_true', help='Ignore ssl certificate (default="false")') parser.add_option('--api-url', dest='api_url', type='string', default='/ocs/v2.php/apps/serverinfo/api/v1/info', help='Url of the api (default="/ocs/v2.php/apps/serverinfo/api/v1/info")') parser.add_option('--context', dest='context', type='string', help='Webserver context where Nextcloud is running (for example "/mycloud"). It will be prepended to api-url parameter') +parser.add_option('--tracehttp', dest='tracehttp', default=False, action='store_true', help='Show http protocol traces for debugging (default="true")') (options, args) = parser.parse_args() # Print the version of this script if options.version: - print('Version 2.1') - sys.exit(0) + print('Version 2.3') + sys.exit(0) # Validate the user input... if not options.username and not options.password and not options.hostname and not options.check: - parser.print_help() - sys.exit(3) + parser.print_help() + sys.exit(3) if not options.username and not options.nc_token: - parser.error('Username or nc-token is required, use parameter [-u|--username] or [--nc-token].') - sys.exit(3) + parser.error('Username or nc-token is required, use parameter [-u|--username] or [--nc-token].') + sys.exit(3) if not options.password and not options.nc_token: - parser.error('Password or nc-token is required, use parameter [-p|--password] or [--nc-token].') - sys.exit(3) + parser.error('Password or nc-token is required, use parameter [-p|--password] or [--nc-token].') + sys.exit(3) if not options.hostname: - parser.error('Hostname is required, use parameter [-H|--hostname]') - sys.exit(3) + parser.error('Hostname is required, use parameter [-H|--hostname]') + sys.exit(3) if not options.check: - parser.error('Check is required, use parameter [-c|--check]') - sys.exit(3) + parser.error('Check is required, use parameter [-c|--check]') + sys.exit(3) # Re-validate the hostname given by the user (make sure they dont entered a "https://", "http://" or "/") url_strip = re.compile(r"https?://") @@ -149,248 +152,266 @@ def convert_size_to_bytes(size_str): # Re-validate the api_url if options.api_url.startswith('/'): - api_url = options.api_url + api_url = options.api_url else: - api_url = '/{0}'.format(options.api_url) + api_url = '/{0}'.format(options.api_url) # Append update parameters only when needed if options.check in ('apps', 'updates'): - if '?' in api_url: - api_url += '&skipApps=false&skipUpdate=false' - else: - api_url += '?skipApps=false&skipUpdate=false' + if '?' in api_url: + api_url += '&skipApps=false&skipUpdate=false' + else: + api_url += '?skipApps=false&skipUpdate=false' # Create the url to access the api if options.context: - context = '/' + options.context.strip('/') - url = '{0}://{1}{2}{3}'.format(options.protocol, hostname, context, api_url) + context = '/' + options.context.strip('/') + url = '{0}://{1}{2}{3}'.format(options.protocol, hostname, context, api_url) else: - url = '{0}://{1}{2}'.format(options.protocol, hostname, api_url) + url = '{0}://{1}{2}'.format(options.protocol, hostname, api_url) # Encode credentials as base64 credential = base64.b64encode(bytes('%s:%s' % (options.username, options.password), 'ascii')) try: - # Create the request - request = urllib.request.Request(url) - - # Add the token header - if options.nc_token: - request.add_header('NC-Token',"%s" % options.nc_token) - else: - # Add the authentication and api request header - request.add_header("Authorization", "Basic %s" % credential.decode('utf-8')) - request.add_header('OCS-APIRequest','true') - - # SSL/TLS certificate validation (see: https://stackoverflow.com/questions/19268548/python-ignore-certificate-validation-urllib2) - ctx = ssl.create_default_context() - - if(options.ignore_sslcert): - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE - - # Proxy handler - if(options.ignore_proxy): - proxy_handler = urllib.request.ProxyHandler({}) - ctx_handler = urllib.request.HTTPSHandler(context=ctx) - opener = urllib.request.build_opener(proxy_handler, ctx_handler) - - response = opener.open(request) - else: - response = urllib.request.urlopen(request, context=ctx) - - # Read the content - content = response.read() + # Create the request + request = urllib.request.Request(url) + + # Add the token header + if options.nc_token: + request.add_header('NC-Token',"%s" % options.nc_token) + else: + # Add the authentication and api request header + request.add_header("Authorization", "Basic %s" % credential.decode('utf-8')) + request.add_header('OCS-APIRequest','true') + + if(options.tracehttp): + print("=" * 40) + print(f"HTTP method : {request.get_method()}") + print(f"URL Target : {request.full_url}") + print("Headers:") + for key, value in request.headers.items(): + print(f" {key}: {value}") + print("=" * 40) + + # SSL/TLS certificate validation (see: https://stackoverflow.com/questions/19268548/python-ignore-certificate-validation-urllib2) + ctx = ssl.create_default_context() + + if(options.ignore_sslcert): + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + + # Proxy handler + if(options.ignore_proxy): + proxy_handler = urllib.request.ProxyHandler({}) + ctx_handler = urllib.request.HTTPSHandler(context=ctx) + opener = urllib.request.build_opener(proxy_handler, ctx_handler) + + response = opener.open(request) + else: + response = urllib.request.urlopen(request, context=ctx) + + # Read the content + content = response.read() + + if(options.tracehttp): + print("=" * 40) + print(f"HTTP Status : {response.status} {response.reason}") + print("Response Headers:") + for key, value in response.headers.items(): + print(f" {key}: {value}") + print(f"Response Body : {content}") + print("=" * 40) except urllib.error.HTTPError as error: # User is not authorized (401) - print('UNKOWN - [WEBREQUEST] {0} {1}'.format(error.code, error.reason)) - sys.exit(3) + print('UNKOWN - [WEBREQUEST] {0} {1}'.format(error.code, error.reason)) + sys.exit(3) -except urllib.error.URLError as error: # Connection has timed out (wrong url / server down) - print('UNKOWN - [WEBREQUEST] {0}'.format(str(error.reason).split(']')[0].strip())) - sys.exit(3) +except urllib.error.URLError as error: # Connection has timed out (wrong url / server down) + print('UNKOWN - [WEBREQUEST] {0}'.format(str(error.reason).split(']')[0].strip())) + sys.exit(3) try: - # Convert the webrequest response to xml - xml_root = xml.etree.ElementTree.fromstring(content) + # Convert the webrequest response to xml + xml_root = xml.etree.ElementTree.fromstring(content) except xml.etree.ElementTree.ParseError: - print('UNKOWN - [XML] Content contains no or wrong xml data... check the url and if the api is reachable!') - sys.exit(3) + print('UNKOWN - [XML] Content contains no or wrong xml data... check the url and if the api is reachable!') + sys.exit(3) # Check if the xml is valid and the api gives usefull informations try: - # Get the meta informations - xml_meta = xml_root.find('meta') + # Get the meta informations + xml_meta = xml_root.find('meta') - xml_meta_status = str(xml_meta.find('status').text) - xml_meta_statuscode = int(xml_meta.find('statuscode').text) - xml_meta_message = str(xml_meta.find('message').text) + xml_meta_status = str(xml_meta.find('status').text) + xml_meta_statuscode = int(xml_meta.find('statuscode').text) + xml_meta_message = str(xml_meta.find('message').text) - # Check the meta informations - if not (xml_meta_status == 'ok' and xml_meta_statuscode == 200 and xml_meta_message == 'OK'): - print('UNKOWN - [API] invalid meta data... status: {0}, statuscode: {1}, message: {2}'.format(xml_meta_status, xml_meta_statuscode, xml_meta_message)) - sys.exit(3) + # Check the meta informations + if not (xml_meta_status == 'ok' and xml_meta_statuscode == 200 and xml_meta_message == 'OK'): + print('UNKOWN - [API] invalid meta data... status: {0}, statuscode: {1}, message: {2}'.format(xml_meta_status, xml_meta_statuscode, xml_meta_message)) + sys.exit(3) except AttributeError: - print('UNKOWN - [XML] Content contains no or wrong xml data... check the url and if the api is reachable!') - sys.exit(3) + print('UNKOWN - [XML] Content contains no or wrong xml data... check the url and if the api is reachable!') + sys.exit(3) # Performance data format -perfdata_format = "" # nagios +perfdata_format = "" # nagios -if(options.perfdata_format == 'centreon'): # centreon - perfdata_format = "," +if(options.perfdata_format == 'centreon'): # centreon + perfdata_format = "," # Get the nextcloud version... # [output] if options.check == 'system': - xml_system = xml_root.find('data').find('nextcloud').find('system') + xml_system = xml_root.find('data').find('nextcloud').find('system') - xml_system_version = str(xml_system.find('version').text) + xml_system_version = str(xml_system.find('version').text) - print('OK - Nextcloud version: {0}'.format(xml_system_version)) - sys.exit(0) + print('OK - Nextcloud version: {0}'.format(xml_system_version)) + sys.exit(0) # Get informations about the storage # [output + performance data] if options.check == 'storage': - xml_storage = xml_root.find('data').find('nextcloud').find('storage') + xml_storage = xml_root.find('data').find('nextcloud').find('storage') - xml_storage_users = int(xml_storage.find('num_users').text) - xml_storage_files = int(xml_storage.find('num_files').text) - xml_storage_storages = int(xml_storage.find('num_storages').text) - xml_storage_storages_local = int(xml_storage.find('num_storages_local').text) - xml_storage_storages_home = int(xml_storage.find('num_storages_home').text) - xml_storage_storages_other = int(xml_storage.find('num_storages_other').text) + xml_storage_users = int(xml_storage.find('num_users').text) + xml_storage_files = int(xml_storage.find('num_files').text) + xml_storage_storages = int(xml_storage.find('num_storages').text) + xml_storage_storages_local = int(xml_storage.find('num_storages_local').text) + xml_storage_storages_home = int(xml_storage.find('num_storages_home').text) + xml_storage_storages_other = int(xml_storage.find('num_storages_other').text) - print('OK - Users: {1}, files: {2}, storages: {3}, storages local: {4}, storages home: {5}, storages other: {6} | users={1}{0} files={2}{0} storages={3}{0} storages_local={4}{0} storages_home={5}{0} storage_other={6}'.format(perfdata_format, xml_storage_users, xml_storage_files, xml_storage_storages, xml_storage_storages_local, xml_storage_storages_home, xml_storage_storages_other)) - sys.exit(0) + print('OK - Users: {1}, files: {2}, storages: {3}, storages local: {4}, storages home: {5}, storages other: {6} | users={1}{0} files={2}{0} storages={3}{0} storages_local={4}{0} storages_home={5}{0} storage_other={6}'.format(perfdata_format, xml_storage_users, xml_storage_files, xml_storage_storages, xml_storage_storages_local, xml_storage_storages_home, xml_storage_storages_other)) + sys.exit(0) # Get informations about the shares # [output + performance data] if options.check == 'shares': - xml_shares = xml_root.find('data').find('nextcloud').find('shares') + xml_shares = xml_root.find('data').find('nextcloud').find('shares') - xml_shares_shares = int(xml_shares.find('num_shares').text) - xml_shares_shares_user = int(xml_shares.find('num_shares_user').text) - xml_shares_shares_groups = int(xml_shares.find('num_shares_groups').text) - xml_shares_shares_link = int(xml_shares.find('num_shares_link').text) - xml_shares_shares_link_no_password = int(xml_shares.find('num_shares_link_no_password').text) - xml_shares_fed_shares_sent = int(xml_shares.find('num_fed_shares_sent').text) - xml_shares_fed_shares_received = int(xml_shares.find('num_fed_shares_received').text) + xml_shares_shares = int(xml_shares.find('num_shares').text) + xml_shares_shares_user = int(xml_shares.find('num_shares_user').text) + xml_shares_shares_groups = int(xml_shares.find('num_shares_groups').text) + xml_shares_shares_link = int(xml_shares.find('num_shares_link').text) + xml_shares_shares_link_no_password = int(xml_shares.find('num_shares_link_no_password').text) + xml_shares_fed_shares_sent = int(xml_shares.find('num_fed_shares_sent').text) + xml_shares_fed_shares_received = int(xml_shares.find('num_fed_shares_received').text) - print('OK - Shares: {1}, shares user: {2}, shares groups: {3}, shares link: {4}, shares link no password: {5}, shares federation sent: {6}, shares federation received: {7} | shares={1}{0} shares_user={2}{0} shares_groups={3}{0} shares_link={4}{0} shares_link_no_password={5}{0} federation_shares_sent={6}{0} federation_shares_received={7}'.format(perfdata_format, xml_shares_shares, xml_shares_shares_user, xml_shares_shares_groups, xml_shares_shares_link, xml_shares_shares_link_no_password, xml_shares_fed_shares_sent, xml_shares_fed_shares_received)) - sys.exit(0) + print('OK - Shares: {1}, shares user: {2}, shares groups: {3}, shares link: {4}, shares link no password: {5}, shares federation sent: {6}, shares federation received: {7} | shares={1}{0} shares_user={2}{0} shares_groups={3}{0} shares_link={4}{0} shares_link_no_password={5}{0} federation_shares_sent={6}{0} federation_shares_received={7}'.format(perfdata_format, xml_shares_shares, xml_shares_shares_user, xml_shares_shares_groups, xml_shares_shares_link, xml_shares_shares_link_no_password, xml_shares_fed_shares_sent, xml_shares_fed_shares_received)) + sys.exit(0) # Get informations about the webserver # [output] if options.check == 'webserver': - xml_webserver = str(xml_root.find('data').find('server').find('webserver').text) + xml_webserver = str(xml_root.find('data').find('server').find('webserver').text) - print('OK - Webserver: {0}'.format(xml_webserver)) - sys.exit(0) + print('OK - Webserver: {0}'.format(xml_webserver)) + sys.exit(0) # Get informations about php # [output] if options.check == 'php': - xml_php = xml_root.find('data').find('server').find('php') - - xml_php_version = str(xml_php.find('version').text) - xml_php_memory_limit = int(xml_php.find('memory_limit').text) - xml_php_max_execution_time = str(xml_php.find('max_execution_time').text) - xml_php_upload_max_filesize = int(xml_php.find('upload_max_filesize').text) - opcache_elem = xml_php.find('opcache') - if opcache_elem is not None: - opcache_enabled_elem = opcache_elem.find('opcache_enabled') - if opcache_enabled_elem is not None and opcache_enabled_elem.text is not None: - xml_php_opcache_enabled = str(opcache_enabled_elem.text) - else: - xml_php_opcache_enabled = 'unknown' - else: - xml_php_opcache_enabled = 'unknown' - - print('OK - PHP version: {0}, memory limit {1}, max execution time: {2}s, upload max filesize: {3}, opcache enabled: {4}'.format(xml_php_version, calc_size_suffix(xml_php_memory_limit), xml_php_max_execution_time, calc_size_suffix(xml_php_upload_max_filesize), xml_php_opcache_enabled)) - sys.exit(0) + xml_php = xml_root.find('data').find('server').find('php') + + xml_php_version = str(xml_php.find('version').text) + xml_php_memory_limit = int(xml_php.find('memory_limit').text) + xml_php_max_execution_time = str(xml_php.find('max_execution_time').text) + xml_php_upload_max_filesize = int(xml_php.find('upload_max_filesize').text) + opcache_elem = xml_php.find('opcache') + if opcache_elem is not None: + opcache_enabled_elem = opcache_elem.find('opcache_enabled') + if opcache_enabled_elem is not None and opcache_enabled_elem.text is not None: + xml_php_opcache_enabled = str(opcache_enabled_elem.text) + else: + xml_php_opcache_enabled = 'unknown' + else: + xml_php_opcache_enabled = 'unknown' + + print('OK - PHP version: {0}, memory limit {1}, max execution time: {2}s, upload max filesize: {3}, opcache enabled: {4}'.format(xml_php_version, calc_size_suffix(xml_php_memory_limit), xml_php_max_execution_time, calc_size_suffix(xml_php_upload_max_filesize), xml_php_opcache_enabled)) + sys.exit(0) # Get informations about the database # [output + performance data] if options.check == 'database': - xml_database = xml_root.find('data').find('server').find('database') + xml_database = xml_root.find('data').find('server').find('database') - xml_database_type = str(xml_database.find('type').text) - xml_database_version = str(xml_database.find('version').text) - xml_database_size = float(xml_database.find('size').text) + xml_database_type = str(xml_database.find('type').text) + xml_database_version = str(xml_database.find('version').text) + xml_database_size = float(xml_database.find('size').text) - print('OK - Database: {0}, version {1}, size: {2} | database_size={3}'.format(xml_database_type, xml_database_version, calc_size_suffix(xml_database_size), calc_size_nagios(xml_database_size))) - sys.exit(0) + print('OK - Database: {0}, version {1}, size: {2} | database_size={3}'.format(xml_database_type, xml_database_version, calc_size_suffix(xml_database_size), calc_size_nagios(xml_database_size,''))) + sys.exit(0) # Check the active users # [output + performance data] if options.check == 'activeUsers': - xml_activeUsers = xml_root.find('data').find('activeUsers') + xml_activeUsers = xml_root.find('data').find('activeUsers') - xml_activeUsers_last5minutes = int(xml_activeUsers.find('last5minutes').text) - xml_activeUsers_last1hour = int(xml_activeUsers.find('last1hour').text) - xml_activeUsers_last24hours = int(xml_activeUsers.find('last24hours').text) + xml_activeUsers_last5minutes = int(xml_activeUsers.find('last5minutes').text) + xml_activeUsers_last1hour = int(xml_activeUsers.find('last1hour').text) + xml_activeUsers_last24hours = int(xml_activeUsers.find('last24hours').text) - print('OK - Last 5 minutes: {1} user(s), last 1 hour: {2} user(s), last 24 hour: {3} user(s) | users_last_5_minutes={1}{0} users_last_1_hour={2}{0} users_last_24_hours={3}'.format(perfdata_format, xml_activeUsers_last5minutes, xml_activeUsers_last1hour, xml_activeUsers_last24hours)) - sys.exit(0) + print('OK - Last 5 minutes: {1} user(s), last 1 hour: {2} user(s), last 24 hour: {3} user(s) | users_last_5_minutes={1}{0} users_last_1_hour={2}{0} users_last_24_hours={3}'.format(perfdata_format, xml_activeUsers_last5minutes, xml_activeUsers_last1hour, xml_activeUsers_last24hours)) + sys.exit(0) if options.check == 'uploadFilesize': - xml_php = xml_root.find('data').find('server').find('php') + xml_php = xml_root.find('data').find('server').find('php') - # Get upload max filesize - xml_php_upload_max_filesize = int(xml_php.find('upload_max_filesize').text) + # Get upload max filesize + xml_php_upload_max_filesize = int(xml_php.find('upload_max_filesize').text) - # Convert - upload_max_filesize = calc_size_suffix(xml_php_upload_max_filesize) + # Convert + upload_max_filesize = calc_size_suffix(xml_php_upload_max_filesize) - if xml_php_upload_max_filesize >= convert_size_to_bytes(options.upload_filesize): - print('OK - Upload max filesize: {0} >= {1}'.format(upload_max_filesize, options.upload_filesize)) - sys.exit(0) - else: - print('CRITICAL - Upload max filesize is set to {0}, but should be {1}'.format(upload_max_filesize, options.upload_filesize)) - sys.exit(2) + if xml_php_upload_max_filesize >= convert_size_to_bytes(options.upload_filesize): + print('OK - Upload max filesize: {0} >= {1}'.format(upload_max_filesize, options.upload_filesize)) + sys.exit(0) + else: + print('CRITICAL - Upload max filesize is set to {0}, but should be {1}'.format(upload_max_filesize, options.upload_filesize)) + sys.exit(2) # Get information about server updates or app updates # [output] if options.check in ('apps', 'updates'): - xml_apps = xml_root.find('data').find('nextcloud').find('system').find('apps') - - if xml_apps is not None: - xml_apps_num_updates_available = int(xml_apps.find('num_updates_available').text) - else: - xml_apps_num_updates_available = 0 - - if xml_apps_num_updates_available > 0: - xml_apps_updates = xml_apps.find('app_updates') - xml_apps_list = [] - if xml_apps_updates is not None: - for app in xml_apps_updates: - xml_apps_list.append('{0}->{1}'.format(app.tag, app.text)) - apps_updates = '{0} apps require update: {1}'.format(xml_apps_num_updates_available, ', '.join(xml_apps_list)) - - xml_server_update = xml_root.find('data').find('nextcloud').find('system').find('update') - - xml_server_update_available_bool = False - server_update = '' - - if xml_server_update is not None: - xml_server_update_available = xml_server_update.find('available') - if xml_server_update_available is not None and xml_server_update_available.text=='1': - xml_server_update_available_bool = True - server_update = 'New server version available: {0}'.format(xml_server_update.find('available_version').text) - - if xml_server_update_available_bool == True and xml_apps_num_updates_available > 0: - print('WARNING - {0} - {1}'.format(server_update, apps_updates)) - sys.exit(1) - elif xml_server_update_available_bool == True: - print('WARNING - {0}'.format(server_update)) - sys.exit(1) - elif xml_apps_num_updates_available > 0: - print('WARNING - {0}'.format(apps_updates)) - sys.exit(1) - else: - print('OK - No updates available') - sys.exit(0) + xml_apps = xml_root.find('data').find('nextcloud').find('system').find('apps') + + if xml_apps is not None: + xml_apps_num_updates_available = int(xml_apps.find('num_updates_available').text) + else: + xml_apps_num_updates_available = 0 + + if xml_apps_num_updates_available > 0: + xml_apps_updates = xml_apps.find('app_updates') + xml_apps_list = [] + if xml_apps_updates is not None: + for app in xml_apps_updates: + xml_apps_list.append('{0}->{1}'.format(app.tag, app.text)) + apps_updates = '{0} apps require update: {1}'.format(xml_apps_num_updates_available, ', '.join(xml_apps_list)) + + xml_server_update = xml_root.find('data').find('nextcloud').find('system').find('update') + + xml_server_update_available_bool = False + server_update = '' + + if xml_server_update is not None: + xml_server_update_available = xml_server_update.find('available') + if xml_server_update_available is not None and xml_server_update_available.text=='1': + xml_server_update_available_bool = True + server_update = 'New server version available: {0}'.format(xml_server_update.find('available_version').text) + + if xml_server_update_available_bool == True and xml_apps_num_updates_available > 0: + print('WARNING - {0} - {1}'.format(server_update, apps_updates)) + sys.exit(1) + elif xml_server_update_available_bool == True: + print('WARNING - {0}'.format(server_update)) + sys.exit(1) + elif xml_apps_num_updates_available > 0: + print('WARNING - {0}'.format(apps_updates)) + sys.exit(1) + else: + print('OK - No updates available') + sys.exit(0) From a07d106363e5ef33e6c9c905dd0957be064be286 Mon Sep 17 00:00:00 2001 From: BornToBeRoot <16019165+BornToBeRoot@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:11:09 +0200 Subject: [PATCH 2/2] Chore: Copilot / Claude review & fixes --- README.md | 2 ++ check_nextcloud.py | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c888831..0da4fd1 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ Options: --context=CONTEXT Webserver context where Nextcloud is running (for example "/mycloud"). It will be prepended to api-url parameter + --tracehttp Show http protocol traces for debugging + (default="false") ``` diff --git a/check_nextcloud.py b/check_nextcloud.py index 65c84dc..4606a83 100644 --- a/check_nextcloud.py +++ b/check_nextcloud.py @@ -116,7 +116,7 @@ def convert_size_to_bytes(size_str): parser.add_option('--ignore-sslcert', dest='ignore_sslcert', default=False, action='store_true', help='Ignore ssl certificate (default="false")') parser.add_option('--api-url', dest='api_url', type='string', default='/ocs/v2.php/apps/serverinfo/api/v1/info', help='Url of the api (default="/ocs/v2.php/apps/serverinfo/api/v1/info")') parser.add_option('--context', dest='context', type='string', help='Webserver context where Nextcloud is running (for example "/mycloud"). It will be prepended to api-url parameter') -parser.add_option('--tracehttp', dest='tracehttp', default=False, action='store_true', help='Show http protocol traces for debugging (default="true")') +parser.add_option('--tracehttp', dest='tracehttp', default=False, action='store_true', help='Show http protocol traces for debugging (default="false")') (options, args) = parser.parse_args() @@ -173,6 +173,13 @@ def convert_size_to_bytes(size_str): # Encode credentials as base64 credential = base64.b64encode(bytes('%s:%s' % (options.username, options.password), 'ascii')) +def print_headers(headers): + sensitive_headers = ('authorization', 'nc-token') + for key, value in headers.items(): + if key.lower() in sensitive_headers: + value = '***REDACTED***' + print(f" {key}: {value}") + try: # Create the request request = urllib.request.Request(url) @@ -190,8 +197,7 @@ def convert_size_to_bytes(size_str): print(f"HTTP method : {request.get_method()}") print(f"URL Target : {request.full_url}") print("Headers:") - for key, value in request.headers.items(): - print(f" {key}: {value}") + print_headers(request.headers) print("=" * 40) # SSL/TLS certificate validation (see: https://stackoverflow.com/questions/19268548/python-ignore-certificate-validation-urllib2) @@ -218,12 +224,18 @@ def convert_size_to_bytes(size_str): print("=" * 40) print(f"HTTP Status : {response.status} {response.reason}") print("Response Headers:") - for key, value in response.headers.items(): - print(f" {key}: {value}") + print_headers(response.headers) print(f"Response Body : {content}") print("=" * 40) except urllib.error.HTTPError as error: # User is not authorized (401) + if(options.tracehttp): + print("=" * 40) + print(f"HTTP Status : {error.code} {error.reason}") + print("Response Headers:") + print_headers(error.headers) + print(f"Response Body : {error.read()}") + print("=" * 40) print('UNKOWN - [WEBREQUEST] {0} {1}'.format(error.code, error.reason)) sys.exit(3) @@ -342,7 +354,7 @@ def convert_size_to_bytes(size_str): xml_database_version = str(xml_database.find('version').text) xml_database_size = float(xml_database.find('size').text) - print('OK - Database: {0}, version {1}, size: {2} | database_size={3}'.format(xml_database_type, xml_database_version, calc_size_suffix(xml_database_size), calc_size_nagios(xml_database_size,''))) + print('OK - Database: {0}, version {1}, size: {2} | database_size={3}'.format(xml_database_type, xml_database_version, calc_size_suffix(xml_database_size), calc_size_nagios(xml_database_size))) sys.exit(0) # Check the active users