2019-04-03 14:39:18 +00:00
#!/usr/bin/python
###############################################################################################################
2022-04-26 14:48:45 +00:00
# Language : Python 3
2019-04-03 14:39:18 +00:00
# Filename : check_nextcloud.py
# Autor : https://github.com/BornToBeRoot
# Description : Nagios/Centreon plugin for nextcloud serverinfo API (https://github.com/nextcloud/serverinfo)
# Repository : https://github.com/BornToBeRoot/check_nextcloud
###############################################################################################################
2022-04-26 14:48:45 +00:00
### Changelog ###
#
# ~~ Version 1.2 ~~
# - Parameter "--ignore-sslcert" added. (Note: If you use an ip address as hostname... you need to add the ip
# address as trusted domain in the config.php)
# - Parameter "--perfdata-format" added [centreon|nagios] (default="centreon")
# ~~ Version 1.3 ~~
# - Check for app updates added (Thanks @thinkl33t)
# ~~ Version 1.4 ~~
# - Parameter "--nc-token" added (Thanks @sblatt)
# ~~ Version 2.0 ~~
# - Migrated from Python 2.7 to 3 (Thanks @waja)
#
#################
import urllib . request , urllib . error , urllib . parse , base64 , xml . etree . ElementTree , sys , traceback , ssl , re
2019-04-03 14:39:18 +00:00
# Some helper functions
def calc_size_suffix ( num , suffix = ' B ' ) :
for unit in [ ' ' , ' Ki ' , ' Mi ' , ' Gi ' , ' Ti ' , ' Pi ' , ' Ei ' , ' Zi ' ] :
if abs ( num ) < 1024.0 :
return " %3.1f %s %s " % ( num , unit , suffix )
num / = 1024.0
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 )
num / = 1000.0
return " %.1f %s %s " % ( num , ' Y ' , suffix )
# Command line parser
from optparse import OptionParser
2022-04-26 14:48:45 +00:00
parser = OptionParser ( usage = ' % prog -u username -p password -H cloud.example.com -c [system|storage|shares|webserver|php|database|activeUsers|uploadFilesize|apps] ' )
2019-04-03 14:39:18 +00:00
parser . add_option ( ' -v ' , ' --version ' , dest = ' version ' , default = False , action = ' store_true ' , help = ' Print the version of this script ' )
parser . add_option ( ' -u ' , ' --username ' , dest = ' username ' , type = ' string ' , help = ' Username of the user with administrative permissions on the nextcloud server ' )
parser . add_option ( ' -p ' , ' --password ' , dest = ' password ' , type = ' string ' , help = ' Password of the user ' )
2022-04-26 14:48:45 +00:00
parser . add_option ( ' -t ' , ' --nc-token ' , dest = ' nc_token ' , type = ' string ' , help = ' Token to access the nextcloud serverinfo api. You can generate the token with " occ config:app:set serverinfo token --value yourtoken " ; replaces username/password ' )
2019-04-03 14:39:18 +00:00
parser . add_option ( ' -H ' , ' --hostname ' , dest = ' hostname ' , type = ' string ' , help = ' Nextcloud server address (make sure that the address is a trusted domain in the config.php) ' )
2022-04-26 14:48:45 +00:00
parser . add_option ( ' -c ' , ' --check ' , dest = ' check ' , choices = [ ' system ' , ' storage ' , ' shares ' , ' webserver ' , ' php ' , ' database ' , ' activeUsers ' , ' uploadFilesize ' , ' apps ' ] , help = ' The thing you want to check [system|storage|shares|webserver|php|database|activeUsers|uploadFilesize|apps] ' )
parser . add_option ( ' --perfdata-format ' , dest = ' perfdata_format ' , default = ' centreon ' , choices = [ ' centreon ' , ' nagios ' ] , help = ' Format for the performance data [centreon|nagios] (default= " centreon " ) ' )
parser . add_option ( ' --upload-filesize ' , dest = ' upload_filesize ' , default = ' 512.0MiB ' , help = ' Filesize in MiB, GiB without spaces (default= " 512.0MiB " ) ' )
2019-04-03 14:39:18 +00:00
parser . add_option ( ' --protocol ' , dest = ' protocol ' , choices = [ ' https ' , ' http ' ] , default = ' https ' , help = ' Protocol you want to use [http|https] (default= " https " ) ' )
2022-04-26 14:48:45 +00:00
parser . add_option ( ' --ignore-proxy ' , dest = ' ignore_proxy ' , default = False , action = ' store_true ' , help = ' Ignore any configured proxy server on this system for this request (default= " false " ) ' )
parser . add_option ( ' --ignore-sslcert ' , dest = ' ignore_sslcert ' , default = False , action = ' store_true ' , help = ' Ignore ssl certificate (default= " false " ) ' )
2019-04-03 14:39:18 +00:00
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 " ) ' )
( options , args ) = parser . parse_args ( )
# Print the version of this script
if options . version :
2022-04-26 14:48:45 +00:00
print ( ' Version 2.0 ' )
sys . exit ( 0 )
2019-04-03 14:39:18 +00:00
# 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 )
2022-04-26 14:48:45 +00:00
if not options . username and not options . nc_token :
parser . error ( ' Username or nc-token is required, use parameter [-u|--username] or [--nc-token]. ' )
2019-04-03 14:39:18 +00:00
sys . exit ( 3 )
2022-04-26 14:48:45 +00:00
if not options . password and not options . nc_token :
parser . error ( ' Password or nc-token is required, use parameter [-p|--password] or [--nc-token]. ' )
2019-04-03 14:39:18 +00:00
sys . exit ( 3 )
if not options . hostname :
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 )
# Re-validate the hostname given by the user (make sure they dont entered a "https://", "http://" or "/")
url_strip = re . compile ( r " https?:// " )
hostname = url_strip . sub ( ' ' , options . hostname ) . split ( ' / ' ) [ 0 ]
# Re-validate the api_url
2022-04-26 14:48:45 +00:00
if options . api_url . startswith ( ' / ' ) :
2019-04-03 14:39:18 +00:00
api_url = options . api_url
else :
api_url = ' / {0} ' . format ( options . api_url )
# Create the url to access the api
url = ' {0} :// {1} {2} ' . format ( options . protocol , hostname , api_url )
2022-04-26 14:48:45 +00:00
# Encode credentials as base64
2019-04-03 14:39:18 +00:00
credential = base64 . b64encode ( bytes ( ' %s : %s ' % ( options . username , options . password ) , ' ascii ' ) )
try :
2022-04-26 14:48:45 +00:00
# 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 ( )
2019-04-03 14:39:18 +00:00
except urllib . error . HTTPError as error : # User is not authorized (401)
2022-04-26 14:48:45 +00:00
print ( ' UNKOWN - [WEBREQUEST] {0} {1} ' . format ( error . code , error . reason ) )
sys . exit ( 3 )
2019-04-03 14:39:18 +00:00
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 )
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 )
# Check if the xml is valid and the api gives usefull informations
try :
# 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 )
# 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 )
2022-04-26 14:48:45 +00:00
# Performance data format
perfdata_format = " " # nagios
2019-04-03 14:39:18 +00:00
2022-04-26 14:48:45 +00:00
if ( options . perfdata_format == ' centreon ' ) : # centreon
perfdata_format = " , "
2019-04-03 14:39:18 +00:00
2022-04-26 14:48:45 +00:00
# Get the nextcloud version...
# [output]
if options . check == ' system ' :
2019-04-03 14:39:18 +00:00
xml_system = xml_root . find ( ' data ' ) . find ( ' nextcloud ' ) . find ( ' system ' )
2022-04-26 14:48:45 +00:00
xml_system_version = str ( xml_system . find ( ' version ' ) . text )
2019-04-03 14:39:18 +00:00
2022-04-26 14:48:45 +00:00
print ( ' OK - Nextcloud version: {0} ' . format ( xml_system_version ) )
2019-04-03 14:39:18 +00:00
sys . exit ( 0 )
# Get informations about the storage
2022-04-26 14:48:45 +00:00
# [output + performance data]
2019-04-03 14:39:18 +00:00
if options . check == ' 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 )
2022-04-26 14:48:45 +00:00
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 ) )
2019-04-03 14:39:18 +00:00
sys . exit ( 0 )
# Get informations about the shares
2022-04-26 14:48:45 +00:00
# [output + performance data]
2019-04-03 14:39:18 +00:00
if options . check == ' 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 )
2022-04-26 14:48:45 +00:00
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 ) )
2019-04-03 14:39:18 +00:00
sys . exit ( 0 )
# Get informations about the webserver
2022-04-26 14:48:45 +00:00
# [output]
2019-04-03 14:39:18 +00:00
if options . check == ' webserver ' :
xml_webserver = str ( xml_root . find ( ' data ' ) . find ( ' server ' ) . find ( ' webserver ' ) . text )
print ( ' OK - Webserver: {0} ' . format ( xml_webserver ) )
sys . exit ( 0 )
# Get informations about php
2022-04-26 14:48:45 +00:00
# [output]
2019-04-03 14:39:18 +00:00
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 )
print ( ' OK - PHP version: {0} , memory limit {1} , max execution time: {2} s, upload max filesize: {3} ' . format ( xml_php_version , calc_size_suffix ( xml_php_memory_limit ) , xml_php_max_execution_time , calc_size_suffix ( xml_php_upload_max_filesize ) ) )
sys . exit ( 0 )
# Get informations about the database
2022-04-26 14:48:45 +00:00
# [output + performance data]
2019-04-03 14:39:18 +00:00
if options . check == ' 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 )
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
2022-04-26 14:48:45 +00:00
# [output + performance data]
2019-04-03 14:39:18 +00:00
if options . check == ' 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 )
2022-04-26 14:48:45 +00:00
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 ) )
2019-04-03 14:39:18 +00:00
sys . exit ( 0 )
if options . check == ' uploadFilesize ' :
xml_php = xml_root . find ( ' data ' ) . find ( ' server ' ) . find ( ' php ' )
2022-04-26 14:48:45 +00:00
2019-04-03 14:39:18 +00:00
# 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 )
if options . upload_filesize == upload_max_filesize :
print ( ' OK - Upload max filesize: {0} ' . format ( upload_max_filesize ) )
2022-04-26 14:48:45 +00:00
sys . exit ( 0 )
2019-04-03 14:39:18 +00:00
else :
print ( ' CRITICAL - Upload max filesize is set to {0} , but should be {1} ' . format ( upload_max_filesize , options . upload_filesize ) )
sys . exit ( 2 )
2022-04-26 14:48:45 +00:00
# Get informations about any app updates
# [output]
if options . check == ' apps ' :
xml_apps = xml_root . find ( ' data ' ) . find ( ' nextcloud ' ) . find ( ' system ' ) . find ( ' apps ' )
xml_apps_num_updates_available = int ( xml_apps . find ( ' num_updates_available ' ) . text )
if xml_apps_num_updates_available == 0 :
print ( ' OK - No apps requiring update ' )
sys . exit ( 0 )
else :
xml_apps_updates = xml_apps . find ( ' app_updates ' )
xml_apps_list = [ ]
for app in xml_apps_updates :
xml_apps_list . append ( ' {0} -> {1} ' . format ( app . tag , app . text ) )
print ( ' WARNING - {0} apps require update: {1} ' . format ( xml_apps_num_updates_available , ' , ' . join ( xml_apps_list ) ) )
sys . exit ( 1 )