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 = SRCDIR + "/megarc.bin"
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 os.geteuid() != 0:
101 end(UNKNOWN, "You must be root to run this plugin")
103 if not os.path.exists(BIN):
104 end(UNKNOWN, "Lsi MegaRaid utility '%s' was not found" % BIN)
106 if not os.access(BIN, os.X_OK):
107 end(UNKNOWN, "Lsi MegaRaid utility '%s' is not executable" % BIN)
109 if not os.path.exists(MEGADEV):
110 print >> sys.stderr, "Megaraid device node not found (possible first " \
111 + "run?), creating it now..."
112 make_megadev(MEGADEV)
116 """run megarc.bin util with passed in args and return output"""
117 if args == "" or args == None:
118 print "UNKNOWN: internal python error",
119 print "- no cmd supplied for Lsi MegaRaid utility"
121 cmd = "%s %s -nolog" % (BIN, args)
122 result, output = commands.getstatusoutput(cmd)
123 lines = output.split("\n")
125 if lines[0][-25:] == "No such file or directory":
126 end(UNKNOWN, "Cannot find Lsi MegaRaid utility '%s'" % BIN)
127 elif len(lines) == 0:
128 end(UNKNOWN, "No output from Lsi MegaRaid utility")
129 elif len(lines) < 13:
130 print >> sys.stderr, "Error running '%s':" % cmd
131 print >> sys.stderr, "%s" % output
132 end(UNKNOWN, "Output from Lsi MegaRaid utility is too short, "
133 + "please inspect code")
135 end(UNKNOWN, "Error using MegaRaid utility - %s" \
136 % output.replace("\n", "|"))
141 def get_controllers(verbosity):
142 """finds and returns a list of all controllers on the local machine"""
144 lines = run("-AllAdpInfo")
146 if lines[11].strip() == "No Adapters Found":
147 end(WARNING, "No LSI adapters were found on this machine")
150 controller_lines = lines[12:]
151 for line in controller_lines:
153 controller = int(line.split("\t")[1])
154 except OSError,error:
155 end(UNKNOWN, "Exception occurred in code - %s" % str(error))
156 controllers.append(controller)
158 if len(controllers) == 0:
159 end(WARNING, "No LSI controllers were found on this machine")
162 print "Found %s controller(s)" % len(controllers)
167 def test_raid(verbosity, no_summary=False):
168 """tests all raid arrays on all Lsi controllers found on local machine
169 and returns status code"""
174 non_optimal_arrays = 0
175 controllers = get_controllers(verbosity)
176 number_controllers = len(controllers)
177 for controller in controllers:
178 detailed_output = run("-dispCfg -a%s" % controller )
180 for line in detailed_output:
184 for line in detailed_output:
185 if "Status:" in line:
186 state = line.split(":")[-1][1:-1]
187 logical_drive = line.split()[3][:-1]
188 array_details[logical_drive] = [state]
189 if "RaidLevel:" in line:
190 raid_level = line.split()[3]
191 array_details[logical_drive].append(raid_level)
193 if len(array_details) == 0:
194 message += "No arrays found on controller %s. " % controller
199 array_keys = array_details.keys()
201 number_arrays += len(array_keys)
203 for drive in array_keys:
204 state = array_details[drive][0]
205 if state != "OPTIMAL":
206 non_optimal_arrays += 1
207 raid_level = array_details[drive][1]
208 # The Array number here is incremented by one because of the
209 # inconsistent way that the LSI tools count arrays.
210 # This brings it back in line with the view in the bios
211 # and from megamgr.bin where the array counting starts at
213 message += 'Array %s status is "%s"' % (int(drive)+1, state)
214 message += '(Raid-%s on adapter %s), ' \
215 % (raid_level, controller)
219 message = add_status_summary(status, \
223 message = message.rstrip(" ")
224 message = message.rstrip(",")
227 message = add_checked_summary(message, \
230 return status, message
233 def add_status_summary(status, message, non_optimal_arrays):
234 """Add initial summary information on the overall state of the arrays"""
237 message += "All arrays OK"
239 if non_optimal_arrays == 1:
240 message = "%s array not OK - " % non_optimal_arrays \
243 message = "%s arrays not OK - " % non_optimal_arrays \
249 def add_checked_summary(message, number_arrays, number_controllers):
250 """ Adds ending summary information on how many arrays were checked"""
252 message += " [%s array" % number_arrays
253 if number_arrays != 1:
255 message += " checked on %s controller" % number_controllers
257 if number_controllers == 1:
266 """parses args and calls func to test raid arrays"""
268 parser = OptionParser()
269 parser.add_option( "-n",
273 help="Do not display the number of arrays " \
274 + "checked. By default the number of arrays " \
275 + "checked are printed at the end of the " \
276 + "line. This is useful information and helps to " \
277 + "know that they are detected properly")
279 parser.add_option( "-v",
283 help="Verbose mode. Good for testing plugin. By \
284 default only one result line is printed as per Nagios standards")
286 parser.add_option( "-V",
288 action = "store_true",
290 help = "Print version number and exit" )
292 (options, args) = parser.parse_args()
294 no_summary = options.no_summary
295 verbosity = options.verbosity
296 version = options.version
306 result, message = test_raid(verbosity, no_summary)
311 if __name__ == "__main__":
314 except KeyboardInterrupt:
315 print "Caught Control-C..."