Created
August 27, 2017 19:10
-
-
Save dlangille/963e66630988fbfb697dbe0718f197d2 to your computer and use it in GitHub Desktop.
check_smartmon altered to work with 3ware twa devices on FreeBSD
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/python | |
# -*- coding: iso8859-1 -*- | |
# | |
# $Id: version.py 133 2006-03-24 10:30:20Z fuller $ | |
# | |
# check_smartmon | |
# Copyright (C) 2006 daemogorgon.net | |
# | |
# This program is free software; you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation; either version 2 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License along | |
# with this program; if not, write to the Free Software Foundation, Inc., | |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
# patches for FreeBSD and SCSI disks: wtps0n@bsdserwis.com | |
"""Package versioning | |
""" | |
import os.path | |
import sys, re | |
from subprocess import Popen,PIPE | |
from optparse import OptionParser | |
__author__ = "fuller <fuller@daemogorgon.net>" | |
__version__ = "$Revision$" | |
# path to smartctl | |
_smartctlPath = "/usr/local/sbin/smartctl" | |
# application wide verbosity (can be adjusted with -v [0-3]) | |
_verbosity = 0 | |
def parseCmdLine(args): | |
"""Commandline parsing.""" | |
usage = "usage: %prog [options] device" | |
version = "%%prog %s" % (__version__) | |
parser = OptionParser(usage=usage, version=version) | |
parser.add_option("-d", "--device", action="store", dest="device", default="", metavar="DEVICE", | |
help="device to check") | |
parser.add_option("-v", "--verbosity", action="store", | |
dest="verbosity", type="int", default=0, | |
metavar="LEVEL", help="set verbosity level to LEVEL; defaults to 0 (quiet), \ | |
possible values go up to 3") | |
parser.add_option("-t", "--type", action="store", dest="devtype", default="ata", metavar="DEVTYPE", | |
help="type of device (ata|scsi)") | |
parser.add_option("-w", "--warning-threshold", metavar="TEMP", action="store", | |
type="int", dest="warningThreshold", default=55, | |
help="set temperature warning threshold to given temperature (defaults to 55)") | |
parser.add_option("-c", "--critical-threshold", metavar="TEMP", action="store", | |
type="int", dest="criticalThreshold", default="60", | |
help="set temperature critical threshold to given temperature (defaults to 60)") | |
return parser.parse_args(args) | |
# end | |
def checkDevice(path): | |
"""Check if device exists and permissions are ok. | |
Returns: | |
- 0 ok | |
- 1 no such device | |
- 2 no read permission given | |
""" | |
vprint(3, "Check if %s does exist and can be read" % path) | |
if not os.access(path, os.F_OK): | |
return (1, "UNKNOWN: no such device found") | |
elif not os.access(path, os.R_OK): | |
return (2, "UNKNOWN: no read permission given") | |
else: | |
return (0, "") | |
# fi | |
# end | |
def checkSmartMonTools(path): | |
"""Check if smartctl is available and can be executed. | |
Returns: | |
- 0 ok | |
- 1 no such file | |
- 2 cannot execute file | |
""" | |
vprint(3, "Check if %s does exist and can be read" % path) | |
if not os.access(path, os.F_OK): | |
return (1, "UNKNOWN: cannot find %s" % path) | |
elif not os.access(path, os.X_OK): | |
return (2, "UNKNOWN: cannot execute %s" % path) | |
else: | |
return (0, "") | |
# fi | |
# end | |
def callSmartMonTools(path, device): | |
# get health status | |
cmd = "%s -H /dev/twa0 -d 3ware,%s" % (path, device) | |
vprint(3, "Get device health status: %s" % cmd) | |
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) | |
(child_stdout, child_stderr) = (p.stdout, p.stderr) | |
line = child_stderr.readline() | |
if len(line): | |
return (3, "UNKNOWN: call exits unexpectedly (%s)" % line, "", | |
"") | |
healthStatusOutput = "" | |
for line in child_stdout: | |
healthStatusOutput = healthStatusOutput + line | |
# done | |
# get temperature | |
cmd = "%s -A /dev/twa0 -d 3ware,%s" % (path, device) | |
vprint(3, "Get device temperature: %s" % cmd) | |
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) | |
(child_stdout, child_stderr) = (p.stdout, p.stderr) | |
line = child_stderr.readline() | |
if len(line): | |
return (3, "UNKNOWN: call exits unexpectedly (%s)" % line, "", | |
"") | |
temperatureOutput = "" | |
for line in child_stdout: | |
temperatureOutput = temperatureOutput + line | |
# done | |
return (0 ,"", healthStatusOutput, temperatureOutput) | |
# end | |
def parseOutput(healthMessage, temperatureMessage, devType): | |
"""Parse smartctl output | |
Returns (health status, temperature). | |
""" | |
vprint(3, "parseOutput: Device type is %s" % devType) | |
vprint(3, "parseOutput: healthMessage %s" % healthMessage) | |
if devType == "ata": | |
# parse health status | |
# | |
# look for line '=== START OF READ SMART DATA SECTION ===' | |
statusLine = "" | |
lines = healthMessage.split("\n") | |
getNext = 0 | |
for line in lines: | |
if getNext: | |
statusLine = line | |
break | |
elif line == "=== START OF READ SMART DATA SECTION ===": | |
getNext = 1 | |
# fi | |
# done | |
if getNext: | |
parts = statusLine.split() | |
healthStatus = parts[-1] | |
# fi | |
# parse temperature attribute line | |
temperature = 0 | |
lines = temperatureMessage.split("\n") | |
for line in lines: | |
parts = line.split() | |
if len(parts): | |
# 194 is the temperature value id | |
if parts[0] == "194": | |
temperature = int(parts[9]) | |
break | |
# fi | |
# fi | |
# done | |
# if devType == ata | |
if devType == "scsi": | |
stat_re = re.compile( r'SMART Health Status:' ) | |
lines = healthMessage.split("\n") | |
for line in lines: | |
if stat_re.search( line ): | |
parts = line.split() | |
healthStatus = parts[-1] | |
break | |
# fi | |
# done | |
# get temperature from temperatureMessage | |
stat_re = re.compile( r'Current Drive Temperature:' ) | |
lines = temperatureMessage.split("\n") | |
for line in lines: | |
if stat_re.search( line ): | |
parts = line.split() | |
temperature = int(parts[-2]) | |
break | |
# fi | |
# done | |
# if devType == scsi | |
vprint(3, "Health status: %s" % healthStatus) | |
vprint(3, "Temperature: %d" %temperature) | |
return (healthStatus, temperature) | |
# end | |
def createReturnInfo(healthStatus, temperature, warningThreshold, | |
criticalThreshold): | |
"""Create return information according to given thresholds.""" | |
# this is absolutely critical! | |
if healthStatus not in [ "PASSED", "OK" ]: | |
return (2, "CRITICAL: device does not pass health status") | |
# fi | |
if temperature > criticalThreshold: | |
return (2, "CRITICAL: device temperature (%d) exceeds critical temperature threshold (%s)" % (temperature, criticalThreshold)) | |
elif temperature > warningThreshold: | |
return (1, "WARNING: device temperature (%d) exceeds warning temperature threshold (%s)" % (temperature, warningThreshold)) | |
else: | |
return (0, "OK: device is functional and stable (temperature: %d)" % temperature) | |
# fi | |
# end | |
def exitWithMessage(value, message): | |
"""Exit with given value and status message.""" | |
print message | |
sys.exit(value) | |
# end | |
def vprint(level, message): | |
"""Verbosity print. | |
Decide according to the given verbosity level if the message will be | |
printed to stdout. | |
""" | |
if level <= verbosity: | |
print message | |
# fi | |
# end | |
if __name__ == "__main__": | |
(options, args) = parseCmdLine(sys.argv) | |
verbosity = options.verbosity | |
vprint(2, "Get device name") | |
device = options.device | |
vprint(1, "Device: %s" % device) | |
# check if we can access 'path' | |
# vprint(2, "Check device") | |
# (value, message) = checkDevice(device) | |
# if value != 0: | |
# exitWithMessage(3, message) | |
# # fi | |
# check if we have smartctl available | |
(value, message) = checkSmartMonTools(_smartctlPath) | |
if value != 0: | |
exitWithMessage(3, message) | |
# fi | |
vprint(1, "Path to smartctl: %s" % _smartctlPath) | |
# FreeBSD specific - SCSI disks are /dev/da[0-9] | |
device_re = re.compile( r'/dev/da[0-9]' ) | |
# check device type, ATA is default | |
vprint(2, "Get device type") | |
devtype = options.devtype | |
if not devtype: | |
if device_re.search( device ): | |
devtype = "scsi" | |
else: | |
devtype = "ata" | |
vprint(1, "Device type: %s" % devtype) | |
# call smartctl and parse output | |
vprint(2, "Call smartctl") | |
(value, message, healthStatusOutput, temperatureOutput) = callSmartMonTools(_smartctlPath, device) | |
if value != 0: | |
exitWithMessage(value, message) | |
vprint(2, "Parse smartctl output") | |
(healthStatus, temperature) = parseOutput(healthStatusOutput, temperatureOutput, devtype) | |
vprint(2, "Generate return information") | |
(value, message) = createReturnInfo(healthStatus, temperature, | |
options.warningThreshold, options.criticalThreshold) | |
# exit program | |
exitWithMessage(value, message) | |
# fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- /usr/local/libexec/nagios/check_smartmon 2016-07-27 06:31:10.000000000 +0000 | |
+++ /usr/local/libexec/nagios-custom/check_smartmon_twa 2017-08-27 18:40:29.427903000 +0000 | |
@@ -113,7 +113,7 @@ | |
def callSmartMonTools(path, device): | |
# get health status | |
- cmd = "%s -H %s" % (path, device) | |
+ cmd = "%s -H /dev/twa0 -d 3ware,%s" % (path, device) | |
vprint(3, "Get device health status: %s" % cmd) | |
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) | |
(child_stdout, child_stderr) = (p.stdout, p.stderr) | |
@@ -127,7 +127,7 @@ | |
# done | |
# get temperature | |
- cmd = "%s -A %s" % (path, device) | |
+ cmd = "%s -A /dev/twa0 -d 3ware,%s" % (path, device) | |
vprint(3, "Get device temperature: %s" % cmd) | |
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) | |
(child_stdout, child_stderr) = (p.stdout, p.stderr) | |
@@ -153,6 +153,8 @@ | |
vprint(3, "parseOutput: Device type is %s" % devType) | |
+ vprint(3, "parseOutput: healthMessage %s" % healthMessage) | |
+ | |
if devType == "ata": | |
# parse health status | |
# | |
@@ -268,11 +270,11 @@ | |
vprint(1, "Device: %s" % device) | |
# check if we can access 'path' | |
- vprint(2, "Check device") | |
- (value, message) = checkDevice(device) | |
- if value != 0: | |
- exitWithMessage(3, message) | |
- # fi | |
+# vprint(2, "Check device") | |
+# (value, message) = checkDevice(device) | |
+# if value != 0: | |
+# exitWithMessage(3, message) | |
+# # fi | |
# check if we have smartctl available | |
(value, message) = checkSmartMonTools(_smartctlPath) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The use case for 3Ware and smartctl is:
In the patch code, I have hardcoded twa0, and commented out verification that the device exists.
Here is the patched code in use: