3 # Copyright Hari Sekhon 2007
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 # downloaded 20100228 by weasel from
20 # http://www.monitoringexchange.org/inventory/Check-Plugins/Hardware/Devices/RAID-Controller/LSI-Mega-RAID-plugin-for-32-bit-and-64-bit-systems
22 """Nagios plugin to test the status of all arrays on all Lsi MegaRaid
23 controllers on the local machine. Uses the megarc.bin program written by Lsi to
24 get the status of all arrays on all local Lsi MegaRaid controllers. Expects the
25 megarc.bin program to be in the same directory as this plugin"""
32 from optparse import OptionParser
34 # Standard Nagios return codes
40 SRCDIR = os.path.dirname(sys.argv[0])
41 BIN = "/usr/local/bin/megarc"
42 MEGADEV = "/dev/megadev0"
45 def end(status, message):
46 """exits the plugin with first arg as the return code and the second
47 arg as the message to output"""
50 print "RAID OK: %s" % message
52 elif status == WARNING:
53 print "RAID WARNING: %s" % message
55 elif status == CRITICAL:
56 print "RAID CRITICAL: %s" % message
59 print "UNKNOWN: %s" % message
63 def make_megadev(devicenode):
64 """Creates the device node needed for the Lsi utility to work
65 (usually /dev/megadev0)"""
68 devices = open("/proc/devices", "r")
69 lines = devices.read()
71 except IOError, error:
72 end(UNKNOWN, "Error reading /proc/devices while trying to create " \
73 + "device node '%s' - %s" % (devicenode, error))
75 for line in lines.split("\n"):
78 major_number = line[0]
80 if device == "megadev":
83 if device != "megadev":
84 end(UNKNOWN, "Unable to create device node /dev/megadev0. Megadev " \
85 + "not found in /proc/devices. Please make sure you have " \
86 + "an Lsi MegaRaid card detected by your kernel first")
87 cmd = "mknod /dev/megadev0 c %s 2" % major_number
88 print >> sys.stderr, "running in shell: %s" % cmd
90 result, output = commands.getstatusoutput(cmd)
92 end(UNKNOWN, "Error making device node '%s' - %s" \
93 % (devicenode, output))
94 print >> sys.stderr, "%s" % output
95 print >> sys.stderr, "now continuing with raid checks..."
96 except OSError, error:
97 end(UNKNOWN, "Error making '%s' device node - %s" % (devicenode, error))
100 if not os.path.exists(BIN):
101 end(UNKNOWN, "Lsi MegaRaid utility '%s' was not found" % BIN)
103 if not os.access(BIN, os.X_OK):
104 end(UNKNOWN, "Lsi MegaRaid utility '%s' is not executable" % BIN)
106 if not os.path.exists(MEGADEV):
107 print >> sys.stderr, "Megaraid device node not found (possible first " \
108 + "run?), creating it now..."
109 make_megadev(MEGADEV)
113 """run megarc.bin util with passed in args and return output"""
114 if args == "" or args == None:
115 print "UNKNOWN: internal python error",
116 print "- no cmd supplied for Lsi MegaRaid utility"
118 cmd = "sudo %s %s -nolog" % (BIN, args)
119 result, output = commands.getstatusoutput(cmd)
120 lines = output.split("\n")
122 if lines[0][-25:] == "No such file or directory":
123 end(UNKNOWN, "Cannot find Lsi MegaRaid utility '%s'" % BIN)
124 elif len(lines) == 0:
125 end(UNKNOWN, "No output from Lsi MegaRaid utility")
126 elif len(lines) < 13:
127 print >> sys.stderr, "Error running '%s':" % cmd
128 print >> sys.stderr, "%s" % output
129 end(UNKNOWN, "Output from Lsi MegaRaid utility is too short, "
130 + "please inspect code")
132 end(UNKNOWN, "Error using MegaRaid utility - %s" \
133 % output.replace("\n", "|"))
138 def get_controllers(verbosity):
139 """finds and returns a list of all controllers on the local machine"""
141 lines = run("-AllAdpInfo")
143 if lines[11].strip() == "No Adapters Found":
144 end(WARNING, "No LSI adapters were found on this machine")
147 controller_lines = lines[12:]
148 for line in controller_lines:
150 controller = int(line.split("\t")[1])
151 except OSError,error:
152 end(UNKNOWN, "Exception occurred in code - %s" % str(error))
153 controllers.append(controller)
155 if len(controllers) == 0:
156 end(WARNING, "No LSI controllers were found on this machine")
159 print "Found %s controller(s)" % len(controllers)
164 def test_raid(verbosity, no_summary=False):
165 """tests all raid arrays on all Lsi controllers found on local machine
166 and returns status code"""
171 non_optimal_arrays = 0
172 controllers = get_controllers(verbosity)
173 number_controllers = len(controllers)
174 for controller in controllers:
175 detailed_output = run("-dispCfg -a%s" % controller )
177 for line in detailed_output:
181 for line in detailed_output:
182 if "Status:" in line:
183 state = line.split(":")[-1][1:-1]
184 logical_drive = line.split()[3][:-1]
185 array_details[logical_drive] = [state]
186 if "RaidLevel:" in line:
187 raid_level = line.split()[3]
188 array_details[logical_drive].append(raid_level)
190 if len(array_details) == 0:
191 message += "No arrays found on controller %s. " % controller
196 array_keys = array_details.keys()
198 number_arrays += len(array_keys)
200 for drive in array_keys:
201 state = array_details[drive][0]
202 if state != "OPTIMAL":
203 non_optimal_arrays += 1
204 raid_level = array_details[drive][1]
205 # The Array number here is incremented by one because of the
206 # inconsistent way that the LSI tools count arrays.
207 # This brings it back in line with the view in the bios
208 # and from megamgr.bin where the array counting starts at
210 message += 'Array %s status is "%s"' % (int(drive)+1, state)
211 message += '(Raid-%s on adapter %s), ' \
212 % (raid_level, controller)
216 message = add_status_summary(status, \
220 message = message.rstrip(" ")
221 message = message.rstrip(",")
224 message = add_checked_summary(message, \
227 return status, message
230 def add_status_summary(status, message, non_optimal_arrays):
231 """Add initial summary information on the overall state of the arrays"""
234 message += "All arrays OK"
236 if non_optimal_arrays == 1:
237 message = "%s array not OK - " % non_optimal_arrays \
240 message = "%s arrays not OK - " % non_optimal_arrays \
246 def add_checked_summary(message, number_arrays, number_controllers):
247 """ Adds ending summary information on how many arrays were checked"""
249 message += " [%s array" % number_arrays
250 if number_arrays != 1:
252 message += " checked on %s controller" % number_controllers
254 if number_controllers == 1:
263 """parses args and calls func to test raid arrays"""
265 parser = OptionParser()
266 parser.add_option( "-n",
270 help="Do not display the number of arrays " \
271 + "checked. By default the number of arrays " \
272 + "checked are printed at the end of the " \
273 + "line. This is useful information and helps to " \
274 + "know that they are detected properly")
276 parser.add_option( "-v",
280 help="Verbose mode. Good for testing plugin. By \
281 default only one result line is printed as per Nagios standards")
283 parser.add_option( "-V",
285 action = "store_true",
287 help = "Print version number and exit" )
289 (options, args) = parser.parse_args()
291 no_summary = options.no_summary
292 verbosity = options.verbosity
293 version = options.version
303 result, message = test_raid(verbosity, no_summary)
308 if __name__ == "__main__":
311 except KeyboardInterrupt:
312 print "Caught Control-C..."