Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 98 additions & 83 deletions check_synology.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,65 @@
import sys
import math
import re

import easysnmp

AUTHOR = "Frederic Werner"
VERSION = "1.1.0"

parser = argparse.ArgumentParser()
parser.add_argument("hostname", help="the hostname", type=str)
parser.add_argument("username", help="the snmp user name", type=str)
parser.add_argument("authkey", help="the auth key", type=str)
parser.add_argument("privkey", help="the priv key", type=str)
parser.add_argument("mode", help="the mode", type=str, choices=["load", "memory", "disk", "storage", "update", "status"])
parser.add_argument("-w", help="warning value for selected mode", type=int)
parser.add_argument("-c", help="critical value for selected mode", type=int)
parser.add_argument("-p", help="the snmp port", type=int, dest="port", default=161)
parser.add_argument("-e", help="SNMP privacy protocol encryption", type=str, default="AES128", choices=["AES128", "DES"])
parser.add_argument("-t", help="timeout for snmp connection", type=int, default=10)
parser.add_argument("-r", help="retries for snmp connection if timeout occurs", type=int, default=3)
parser.add_argument("-H", "--hostname", help="the hostname", type=str, required=True)
parser.add_argument("-u", "--security-username", help="the snmp user name", type=str, required=True)
parser.add_argument("-o", "--auth-protocol", help="the authentication protocol", default="MD5", choices=["MD5", "SHA"])
parser.add_argument("-a", "--auth-password", help="the authentication key", type=str, required=True)
parser.add_argument("-s", "--security-level", help="security level type", type=str, default="auth_with_privacy", choices=["auth_with_privacy", "auth_without_privacy", "no_auth_or_privacy"])
parser.add_argument("-e", "--privacy-protocol", help="SNMP privacy protocol encryption", type=str, default="AES128", choices=["AES128", "DES"])
parser.add_argument("-k", "--privacy-password", help="the privacy key", type=str, default="")
parser.add_argument("-m", "--mode", help="the mode", type=str, choices=["load", "memory", "disk", "storage", "update", "status"], required=True)
parser.add_argument("-w", "--warning", help="warning value for selected mode", type=int)
parser.add_argument("-c", "--critical", help="critical value for selected mode", type=int)
parser.add_argument("-p", "--remote-port", help="the snmp port", type=int, default=161)
parser.add_argument("-t", "--timeout", help="timeout for snmp connection", type=int, default=10)
parser.add_argument("-r", "--retries", help="retries for snmp connection if timeout occurs", type=int, default=3)
args = parser.parse_args()

hostname = args.hostname
port = args.port
user_name = args.username
auth_key = args.authkey
priv_key = args.privkey
user_name = args.security_username
auth_prot = args.auth_protocol
auth_key = args.auth_password
priv_protocol = args.privacy_protocol
priv_key = args.privacy_password
mode = args.mode
warning = args.w
critical = args.c
priv_protocol = args.e
snmp_timeout = args.t
snmp_retries = args.r
warning = args.warning
critical = args.critical
port = args.remote_port
snmp_timeout = args.timeout
snmp_retries = args.retries
sec_lvl = args.security_level

state = 'OK'


def exit_code():
if state == 'OK':
sys.exit(0)
if state == 'WARNING':
sys.exit(1)
if state == 'CRITICAL':
sys.exit(2)
if state == 'UNKNOWN':
sys.exit(3)


def croak(message=None):
"""
Exit program with `UNKNOWN` state and error message.
"""
global state
state = 'UNKNOWN'
message = message and str(message) or "unknown error"
print('%s - %s' % (state, message))
exitCode()
print(f"{state} - {message}")
exit_code()


try:
session = easysnmp.Session(
Expand All @@ -55,64 +71,57 @@ def croak(message=None):
version=3,
timeout=snmp_timeout,
retries=snmp_retries,
security_level="auth_with_privacy",
security_level=sec_lvl,
security_username=user_name,
auth_password=auth_key,
auth_protocol="MD5",
auth_protocol=auth_prot,
privacy_password=priv_key,
privacy_protocol=priv_protocol)

privacy_protocol=priv_protocol
)
except Exception as e:
croak(e)

def snmpget(oid):

def snmp_get(oid):
"""
Return value from single OID.
"""
try:
res = session.get(oid)
return res.value
except Exception as e:
croak(e)
except Exception as err:
croak(err)

def snmpwalk(oid):

def snmp_walk(oid):
"""
Walk the given OID and return all child OIDs as a list of tuples of OID and value.
"""
res = []
try:
res = session.walk(oid)
except Exception as e:
croak(e)
except Exception as err:
croak(err)
return res

def exitCode():
if state == 'OK':
sys.exit(0)
if state == 'WARNING':
sys.exit(1)
if state == 'CRITICAL':
sys.exit(2)
if state == 'UNKNOWN':
sys.exit(3)

if mode == 'load':
load1 = str(float(snmpget('1.3.6.1.4.1.2021.10.1.5.1'))/100)
load5 = str(float(snmpget('1.3.6.1.4.1.2021.10.1.5.2'))/100)
load15 = str(float(snmpget('1.3.6.1.4.1.2021.10.1.5.3'))/100)
load1 = str(float(snmp_get('1.3.6.1.4.1.2021.10.1.5.1')) / 100)
load5 = str(float(snmp_get('1.3.6.1.4.1.2021.10.1.5.2')) / 100)
load15 = str(float(snmp_get('1.3.6.1.4.1.2021.10.1.5.3')) / 100)

if warning and warning < int(math.ceil(float(load1))):
state = 'WARNING'
if critical and critical < int(math.ceil(float(load1))):
state = 'CRITICAL'

print(state + ' - load average: %s, %s, %s' % (load1, load5, load15), '| load1=%sc' % load1, 'load5=%sc' % load5, 'load15=%sc' % load15)
exitCode()
print(f"{state} - load average: {load1}, {load5}, {load15} | load1={load1}c load5={load5}c load15={load15}c")
exit_code()

if mode == 'memory':
memory_total = float(snmpget('1.3.6.1.4.1.2021.4.5.0'))
memory_unused = float(snmpget('1.3.6.1.4.1.2021.4.6.0'))
memory_cached = float(snmpget('1.3.6.1.4.1.2021.4.15.0'))
memory_total = float(snmp_get('1.3.6.1.4.1.2021.4.5.0'))
memory_unused = float(snmp_get('1.3.6.1.4.1.2021.4.6.0'))
memory_cached = float(snmp_get('1.3.6.1.4.1.2021.4.15.0'))
memory_usable = memory_unused + memory_cached
memory_percent = 100 / memory_total * memory_usable

Expand All @@ -121,8 +130,11 @@ def exitCode():
if critical and critical > int(memory_percent):
state = 'CRITICAL'

print(state + ' - {:0.1f}% '.format(memory_percent) + 'usable ({0:0.1f} MB free and {1:0.1f} MB cached out of {2:0.1f} MB)'.format((memory_unused / 1024), (memory_cached / 1024), (memory_total / 1024)), '|memory_total=%dc' % memory_total, 'memory_unused=%dc' % memory_unused, 'memory_cached=%dc' % memory_cached, 'memory_usable=%dc' % memory_usable, 'memory_percent=%d' % memory_percent + '%')
exitCode()
print(f"{state} - {memory_percent:.1f}% usable ({memory_unused/1024:.1f} MB free "
f"and {memory_cached/1024:.1f} MB cached out of {memory_total/1024:.1f} MB) "
f"| memory_total={memory_total}c memory_unused={memory_unused}c memory_cached={memory_cached}c "
f"memory_usable={memory_usable}c memory_percent={memory_percent}c%")
exit_code()

if mode == 'disk':
"""
Expand All @@ -139,13 +151,13 @@ def exitCode():
maxDisk = 0
states = []
output = ''
perfdata = '|'
for item in snmpwalk('1.3.6.1.4.1.6574.2.1.1.2'):
perf_data = '|'
for item in snmp_walk('1.3.6.1.4.1.6574.2.1.1.2'):
i = item.oid_index or item.oid.split('.')[-1]
disk_name = item.value
disk_status_nr = snmpget('1.3.6.1.4.1.6574.2.1.1.5.' + str(i))
disk_health_status_nr = snmpget('1.3.6.1.4.1.6574.2.1.1.13.' + str(i))
disk_temp = snmpget('1.3.6.1.4.1.6574.2.1.1.6.' + str(i))
disk_status_nr = snmp_get('1.3.6.1.4.1.6574.2.1.1.5.' + str(i))
disk_health_status_nr = snmp_get('1.3.6.1.4.1.6574.2.1.1.13.' + str(i))
disk_temp = snmp_get('1.3.6.1.4.1.6574.2.1.1.6.' + str(i))
status_translation = {
'1': "Normal",
'2': "Initialized",
Expand All @@ -164,8 +176,8 @@ def exitCode():
disk_name = disk_name.replace(" ", "")

# 2. Compute textual and perfdata output.
output += ' - ' + disk_name + ': Status: ' + disk_status + ', Temperature: ' + disk_temp + ' C' + ', Health status: ' + disk_health_status
perfdata += 'temperature' + disk_name + '=' + disk_temp + 'c '
output += f" - {disk_name}: Status: {disk_status}, Temperature: {disk_temp} C, Health status: {disk_health_status}"
perf_data += f"temperature {disk_name}={disk_temp}c "

# 3. Collect outcome for individual sensor state.

Expand Down Expand Up @@ -202,19 +214,19 @@ def exitCode():
break

# 5. Respond with textual and perfdata output and propagate exit code.
print('%s%s %s' % (state, output, perfdata))
exitCode()
print(f"{state}{output} {perf_data}")
exit_code()

if mode == 'storage':
output = ''
perfdata = '|'
for item in snmpwalk('1.3.6.1.2.1.25.2.3.1.3'):
perf_data = '|'
for item in snmp_walk('1.3.6.1.2.1.25.2.3.1.3'):
i = item.oid_index or item.oid.split('.')[-1]
storage_name = item.value
if re.match("/volume(?!.+/@docker.*)", storage_name):
allocation_units = snmpget('1.3.6.1.2.1.25.2.3.1.4.' + str(i))
size = snmpget('1.3.6.1.2.1.25.2.3.1.5.' + str(i))
used = snmpget('1.3.6.1.2.1.25.2.3.1.6.' + str(i))
allocation_units = snmp_get('1.3.6.1.2.1.25.2.3.1.4.' + str(i))
size = snmp_get('1.3.6.1.2.1.25.2.3.1.5.' + str(i))
used = snmp_get('1.3.6.1.2.1.25.2.3.1.6.' + str(i))

storage_size = int((int(allocation_units) * int(size)) / 1000000000)
storage_used = int((int(used) * int(allocation_units)) / 1000000000)
Expand All @@ -232,14 +244,14 @@ def exitCode():
if critical and critical < int(storage_used_percent):
state = 'CRITICAL'

output += ' - free space: ' + storage_name + ' ' + str(storage_free) + ' GB (' + str(storage_used) + ' GB of ' + str(storage_size) + ' GB used, ' + str(storage_used_percent) + '%)'
perfdata += storage_name + '=' + str(storage_used) + 'c '
print('%s%s %s' % (state, output, perfdata))
exitCode()
output += f" - free space: {storage_name} {storage_free} GB ({storage_used} GB of {storage_size} GB used, {storage_used_percent}%)"
perf_data += f"{storage_name}={storage_used}c "
print(f"{state}{output} {perf_data}")
exit_code()

if mode == 'update':
update_status_nr = snmpget('1.3.6.1.4.1.6574.1.5.4.0')
update_dsm_version = snmpget('1.3.6.1.4.1.6574.1.5.3.0')
update_status_nr = snmp_get('1.3.6.1.4.1.6574.1.5.4.0')
update_dsm_version = snmp_get('1.3.6.1.4.1.6574.1.5.3.0')
status_translation = {
'1': "Available",
'2': "Unavailable",
Expand All @@ -255,20 +267,20 @@ def exitCode():
update_status = status_translation.get(update_status_nr)
state = state_translation.get(update_status_nr, "UNKNOWN")

print(state + ' - DSM Version: %s, DSM Update: %s' % (update_dsm_version, update_status), '| DSMupdate=%sc' % update_status_nr)
exitCode()
print(f"{state} - DSM Version: {update_dsm_version}, DSM Update: {update_status} | DSMupdate={update_status_nr}c")
exit_code()

if mode == 'status':

# 1. Retrieve and decode system metrics.
status_model = snmpget('1.3.6.1.4.1.6574.1.5.1.0')
status_serial = snmpget('1.3.6.1.4.1.6574.1.5.2.0')
status_temperature = snmpget('1.3.6.1.4.1.6574.1.2.0')
status_model = snmp_get('1.3.6.1.4.1.6574.1.5.1.0')
status_serial = snmp_get('1.3.6.1.4.1.6574.1.5.2.0')
status_temperature = snmp_get('1.3.6.1.4.1.6574.1.2.0')

status_system_nr = snmpget('1.3.6.1.4.1.6574.1.1.0')
status_system_fan_nr = snmpget('1.3.6.1.4.1.6574.1.4.1.0')
status_cpu_fan_nr = snmpget('1.3.6.1.4.1.6574.1.4.2.0')
status_power_nr = snmpget('1.3.6.1.4.1.6574.1.3.0')
status_system_nr = snmp_get('1.3.6.1.4.1.6574.1.1.0')
status_system_fan_nr = snmp_get('1.3.6.1.4.1.6574.1.4.1.0')
status_cpu_fan_nr = snmp_get('1.3.6.1.4.1.6574.1.4.2.0')
status_power_nr = snmp_get('1.3.6.1.4.1.6574.1.3.0')

status_translation = {
'1': "Normal",
Expand Down Expand Up @@ -300,5 +312,8 @@ def exitCode():
state = 'CRITICAL'

# 3. Respond with textual and perfdata output and propagate exit code.
print(state + ' - Model: %s, S/N: %s, System Temperature: %s C, System Status: %s, System Fan: %s, CPU Fan: %s, Powersupply : %s' % (status_model, status_serial, status_temperature, status_system, status_system_fan, status_cpu_fan, status_power) + ' | system_temp=%sc' % status_temperature)
exitCode()
print(f"{state} - Model: {status_model}, S/N: {status_serial}, "
f"System Temperature: {status_temperature} C, System Status: {status_system}, "
f"System Fan: {status_system_fan}, CPU Fan: {status_cpu_fan}, "
f"Powersupply : {status_power} | system_temp={status_temperature}c")
exit_code()