Skip to content

Commit

Permalink
- Better error handling
Browse files Browse the repository at this point in the history
- Better compatibility
  • Loading branch information
nobodysu committed Feb 28, 2017
1 parent bd9ec30 commit dca418a
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 36 deletions.
73 changes: 62 additions & 11 deletions Template_App_smartmontools.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<zabbix_export>
<version>2.0</version>
<date>2017-01-24T20:11:49Z</date>
<date>2017-02-28T18:17:40Z</date>
<groups>
<group>
<name>Templates</name>
Expand Down Expand Up @@ -406,6 +406,49 @@ Note: at the moment windows items are sent only on second run (after discovery).
<valuemap/>
<logtimefmt/>
</item_prototype>
<item_prototype>
<name>{#DSMARTSTATUS}: SMART status</name>
<type>2</type>
<snmp_community/>
<multiplier>0</multiplier>
<snmp_oid/>
<key>smartctl.info[{#DSMARTSTATUS},SmartStatus]</key>
<delay>0</delay>
<history>90</history>
<trends>365</trends>
<status>0</status>
<value_type>4</value_type>
<allowed_hosts/>
<units/>
<delta>0</delta>
<snmpv3_contextname/>
<snmpv3_securityname/>
<snmpv3_securitylevel>0</snmpv3_securitylevel>
<snmpv3_authprotocol>0</snmpv3_authprotocol>
<snmpv3_authpassphrase/>
<snmpv3_privprotocol>0</snmpv3_privprotocol>
<snmpv3_privpassphrase/>
<formula>1</formula>
<delay_flex/>
<params/>
<ipmi_sensor/>
<data_type>0</data_type>
<authtype>0</authtype>
<username/>
<password/>
<publickey/>
<privatekey/>
<port/>
<description/>
<inventory_link>0</inventory_link>
<applications>
<application>
<name>SMART info</name>
</application>
</applications>
<valuemap/>
<logtimefmt/>
</item_prototype>
<item_prototype>
<name>{#DVALUE5}: [5]Reallocated_Sector_Count</name>
<type>2</type>
Expand Down Expand Up @@ -710,13 +753,21 @@ Note: at the moment windows items are sent only on second run (after discovery).
</item_prototypes>
<trigger_prototypes>
<trigger_prototype>
<expression>{Template App smartmontools:smartctl.info[{#DFIRMWARE},firmware].diff(0)}&gt;0 and&#13;
{Template App smartmontools:smartctl.info[{#DSERIAL},serial].diff(0)}&gt;1</expression>
<expression>{Template App smartmontools:smartctl.info[{#DFIRMWARE},firmware].diff(0)}&gt;0</expression>
<name>{#DFIRMWARE}: Firmware version had changed on {HOST.NAME}</name>
<url/>
<status>0</status>
<priority>1</priority>
<description>Trigger only if firmware is changes but serial is NOT (means that drive is not repalced).</description>
<description/>
<type>0</type>
</trigger_prototype>
<trigger_prototype>
<expression>{Template App smartmontools:smartctl.info[{#DSMARTSTATUS},SmartStatus].str(NO_SMART_VALUES)}=1</expression>
<name>{#DHAVESMART}: no smart values were discovered</name>
<url/>
<status>0</status>
<priority>1</priority>
<description/>
<type>0</type>
</trigger_prototype>
<trigger_prototype>
Expand Down Expand Up @@ -813,7 +864,7 @@ Note: at the moment windows items are sent only on second run (after discovery).
<triggers>
<trigger>
<expression>{Template App smartmontools:smartctl.info[ConfigStatus].str(NODISKS)}=1</expression>
<name>App smartmontools: no disks were found for SMART test</name>
<name>App smartmontools: no disks were found for SMART test on {HOST.NAME}</name>
<url/>
<status>0</status>
<priority>1</priority>
Expand All @@ -823,7 +874,7 @@ Note: at the moment windows items are sent only on second run (after discovery).
</trigger>
<trigger>
<expression>{Template App smartmontools:smartctl.info[ConfigStatus].str(MISSINGRIGHTS)}=1</expression>
<name>App smartmontools: smartctl does not have sudo/admin access</name>
<name>App smartmontools: smartctl does not have sudo/admin access on {HOST.NAME}</name>
<url/>
<status>0</status>
<priority>1</priority>
Expand All @@ -833,7 +884,7 @@ Note: at the moment windows items are sent only on second run (after discovery).
</trigger>
<trigger>
<expression>{Template App smartmontools:smartctl.info[ConfigStatus].str(NOCMD)}=1</expression>
<name>App smartmontools: smartctl was not found in PATH or manually</name>
<name>App smartmontools: smartctl was not found in PATH or manually on {HOST.NAME}</name>
<url/>
<status>0</status>
<priority>1</priority>
Expand All @@ -843,7 +894,7 @@ Note: at the moment windows items are sent only on second run (after discovery).
</trigger>
<trigger>
<expression>{Template App smartmontools:smartctl.info[ConfigStatus].str(ERROR)}=1</expression>
<name>App smartmontools: something went wrong</name>
<name>App smartmontools: something went wrong on {HOST.NAME}</name>
<url/>
<status>0</status>
<priority>1</priority>
Expand All @@ -852,12 +903,12 @@ Note: at the moment windows items are sent only on second run (after discovery).
<dependencies/>
</trigger>
<trigger>
<expression>{Template App smartmontools:smartctl.info[ConfigStatus].nodata(168h)}=1</expression>
<name>App smartmontools: template is assigned, but no data is recieved</name>
<expression>{Template App smartmontools:smartctl.info[ConfigStatus].nodata(30d)}=1</expression>
<name>App smartmontools: template is assigned, but no data is recieved on {HOST.NAME}</name>
<url/>
<status>0</status>
<priority>1</priority>
<description>If template is assigned to the host, but no data is gathered within one week.</description>
<description>If template is assigned to the host, but no data is gathered within 30 days.</description>
<type>0</type>
<dependencies/>
</trigger>
Expand Down
54 changes: 32 additions & 22 deletions scripts/smartctl-lld.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,15 @@ def replace_all(string, stopChars):

if not diskListManual: # if manual list is not provided
allDisksStdout = subprocess.getoutput(ctlPath + ' --scan') # scan the disks
diskListRe = re.findall(r'^/dev/(\w+)', allDisksStdout, re.M) # and determine short device names
diskListRe = re.findall(r'^/dev/([^ ]+)', allDisksStdout, re.M) # and determine short device names
else:
diskListRe = diskListManual # or just use manually provided settings
#print(diskListRe)

for d in diskListRe: # loop through all found drives
ctlOut = subprocess.getoutput(ctlPath + ' -a /dev/' + d)

if diskListManual:
d = replace_all(d, stopChars) # sanitize the item key
# ! 'd' is statically assigned !
d = replace_all(d, stopChars) # sanitize the item key
#print('disk: ', d)

deviceName = d # save device name before mode selection and after manual substitution
Expand Down Expand Up @@ -101,26 +100,37 @@ def replace_all(string, stopChars):

valuesRe = re.findall(r'^(?:\s+)?(\d+)\s+([\w-]+)\s+[\w-]+\s+\d{3}\s+\d{3}\s+\d{3}\s+[\w-]+\s+[\w-]+\s+[\w-]+\s+(\d+)', ctlOut, re.M | re.I) # catch id, name and value
#print(d + ': valuesRe:\n', valuesRe)
for v in valuesRe:
if v[0] == '5': # semi-hardcoded values for triggers
jsonData.append({'{#DVALUE5}':d})
elif v[0] == '187':
jsonData.append({'{#DVALUE187}':d})
elif v[0] == '188':
jsonData.append({'{#DVALUE188}':d})
elif v[0] == '197':
jsonData.append({'{#DVALUE197}':d})
elif v[0] == '198':
jsonData.append({'{#DVALUE198}':d})
elif v[0] == '199':
jsonData.append({'{#DVALUE199}':d})
else:
jsonData.append({'{#DVALUE}':d, '{#SMARTID}':v[0], '{#SMARTNAME}':v[1]}) # all other possible values

senderData.append(hostname + ' smartctl.value[' + d + ',' + v[0] + '] ' + v[2])


if valuesRe:
jsonData.append({'{#DSMARTSTATUS}':d})
senderData.append(hostname + ' smartctl.info[' + d + ',SmartStatus] "PRESENT"')

for v in valuesRe:
if v[0] == '5': # semi-hardcoded values for triggers
jsonData.append({'{#DVALUE5}':d})
elif v[0] == '187':
jsonData.append({'{#DVALUE187}':d})
elif v[0] == '188':
jsonData.append({'{#DVALUE188}':d})
elif v[0] == '197':
jsonData.append({'{#DVALUE197}':d})
elif v[0] == '198':
jsonData.append({'{#DVALUE198}':d})
elif v[0] == '199':
jsonData.append({'{#DVALUE199}':d})
else:
jsonData.append({'{#DVALUE}':d, '{#SMARTID}':v[0], '{#SMARTNAME}':v[1]}) # all other possible values

senderData.append(hostname + ' smartctl.value[' + d + ',' + v[0] + '] ' + v[2])

else:
jsonData.append({'{#DSMARTSTATUS}':d})
senderData.append(hostname + ' smartctl.info[' + d + ',SmartStatus] "NO_SMART_VALUES"')


if senderData:
senderData.append(hostname + ' smartctl.info[ConfigStatus] "OK"') # signals that client host is configured
senderData.append(hostname + ' smartctl.info[ConfigStatus] "CONFIGURED"') # signals that client host is configured
else:
if ctlOut.find('ermission denied') != -1 or ctlOut.find('missing admin rights') != -1:
senderData.append(hostname + ' smartctl.info[ConfigStatus] "MISSINGRIGHTS"')
Expand Down
7 changes: 4 additions & 3 deletions scripts/smartctl-send.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#agentConf = r'C:\zabbix_agentd.conf' # Win
#agentConf = r'/usr/local/etc/zabbix24/zabbix_agentd.conf' # BSD

senderPath = 'zabbix_sender' # Linux, BSD
senderPath = 'zabbix_sender' # Linux, BSD
#senderPath = r'C:\zabbix-agent\bin\win32\zabbix_sender.exe' # Win

timeout = 60 # how long the script must wait between LLD and sending, increase if data received late (does not affect windows)
Expand All @@ -17,6 +17,7 @@
import sys
import subprocess
from time import sleep
import re

stdin4Sender = sys.stdin.read()

Expand All @@ -25,12 +26,12 @@

if sys.argv[1] == 'get':
sleep(timeout) # wait for LLD to be processed by server
senderProc = subprocess.Popen([senderPath, '-c', agentConf, '-i', '-'], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, universal_newlines=True) # send data gathered from second argument to zabbix server
senderProc = subprocess.Popen([senderPath, '-c', agentConf, '-i', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, universal_newlines=True) # send data gathered from second argument to zabbix server
elif sys.argv[1] == 'getverb':
print('\n Note: the sender will fail if server did not gather LLD previously.')
print('\n Data sent to zabbix sender:\n')
print(stdin4Sender)
senderProc = subprocess.Popen([senderPath, '-vv', '-c', agentConf, '-i', '-'], stdin=subprocess.PIPE, universal_newlines=True) # verbose sender output
senderProc = subprocess.Popen([senderPath, '-vv', '-c', agentConf, '-i', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) # verbose sender output
else:
print(sys.argv[0] + " : Not supported. Use 'get' or 'getverb'.")
sys.exit(1)
Expand Down

0 comments on commit dca418a

Please sign in to comment.