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
20 """Nagios plugin to test the status of all arrays on all Lsi MegaRaid
21 controllers on the local machine. Uses the megarc.bin program written by Lsi to
22 get the status of all arrays on all local Lsi MegaRaid controllers. Expects the
23 megarc.bin program to be in the same directory as this plugin"""
30 from optparse import OptionParser
32 # Standard Nagios return codes
38 SRCDIR = os.path.dirname(sys.argv[0])
39 BIN = SRCDIR + "/megarc.bin"
40 MEGADEV = "/dev/megadev0"
43 def end(status, message):
44 """exits the plugin with first arg as the return code and the second
45 arg as the message to output"""
48 print "RAID OK: %s" % message
50 elif status == WARNING:
51 print "RAID WARNING: %s" % message
53 elif status == CRITICAL:
54 print "RAID CRITICAL: %s" % message
57 print "UNKNOWN: %s" % message
61 def make_megadev(devicenode):
62 """Creates the device node needed for the Lsi utility to work
63 (usually /dev/megadev0)"""
66 devices = open("/proc/devices", "r")
67 lines = devices.read()
69 except IOError, error:
70 end(UNKNOWN, "Error reading /proc/devices while trying to create " \
71 + "device node '%s' - %s" % (devicenode, error))
73 for line in lines.split("\n"):
76 major_number = line[0]
78 if device == "megadev":
81 if device != "megadev":
82 end(UNKNOWN, "Unable to create device node /dev/megadev0. Megadev " \
83 + "not found in /proc/devices. Please make sure you have " \
84 + "an Lsi MegaRaid card detected by your kernel first")
85 cmd = "mknod /dev/megadev0 c %s 2" % major_number
86 print >> sys.stderr, "running in shell: %s" % cmd
88 result, output = commands.getstatusoutput(cmd)
90 end(UNKNOWN, "Error making device node '%s' - %s" \
91 % (devicenode, output))
92 print >> sys.stderr, "%s" % output
93 print >> sys.stderr, "now continuing with raid checks..."
94 except OSError, error:
95 end(UNKNOWN, "Error making '%s' device node - %s" % (devicenode, error))
99 end(UNKNOWN, "You must be root to run this plugin")
101 if not os.path.exists(BIN):
102 end(UNKNOWN, "Lsi MegaRaid utility '%s' was not found" % BIN)
104 if not os.access(BIN, os.X_OK):
105 end(UNKNOWN, "Lsi MegaRaid utility '%s' is not executable" % BIN)
107 if not os.path.exists(MEGADEV):
108 print >> sys.stderr, "Megaraid device node not found (possible first " \
109 + "run?), creating it now..."
110 make_megadev(MEGADEV)
114 """run megarc.bin util with passed in args and return output"""
115 if args == "" or args == None:
116 print "UNKNOWN: internal python error",
117 print "- no cmd supplied for Lsi MegaRaid utility"
119 cmd = "%s %s -nolog" % (BIN, args)
120 result, output = commands.getstatusoutput(cmd)
121 lines = output.split("\n")
123 if lines[0][-25:] == "No such file or directory":
124 end(UNKNOWN, "Cannot find Lsi MegaRaid utility '%s'" % BIN)
125 elif len(lines) == 0:
126 end(UNKNOWN, "No output from Lsi MegaRaid utility")
127 elif len(lines) < 13:
128 print >> sys.stderr, "Error running '%s':" % cmd
129 print >> sys.stderr, "%s" % output
130 end(UNKNOWN, "Output from Lsi MegaRaid utility is too short, "
131 + "please inspect code")
133 end(UNKNOWN, "Error using MegaRaid utility - %s" \
134 % output.replace("\n", "|"))
139 def get_controllers(verbosity):
140 """finds and returns a list of all controllers on the local machine"""
142 lines = run("-AllAdpInfo")
144 if lines[11].strip() == "No Adapters Found":
145 end(WARNING, "No LSI adapters were found on this machine")
148 controller_lines = lines[12:]
149 for line in controller_lines:
151 controller = int(line.split("\t")[1])
152 except OSError,error:
153 end(UNKNOWN, "Exception occurred in code - %s" % str(error))
154 controllers.append(controller)
156 if len(controllers) == 0:
157 end(WARNING, "No LSI controllers were found on this machine")
160 print "Found %s controller(s)" % len(controllers)
165 def test_raid(verbosity, no_summary=False):
166 """tests all raid arrays on all Lsi controllers found on local machine
167 and returns status code"""
172 non_optimal_arrays = 0
173 controllers = get_controllers(verbosity)
174 number_controllers = len(controllers)
175 for controller in controllers:
176 detailed_output = run("-dispCfg -a%s" % controller )
178 for line in detailed_output:
182 for line in detailed_output:
183 if "Status:" in line:
184 state = line.split(":")[-1][1:-1]
185 logical_drive = line.split()[3][:-1]
186 array_details[logical_drive] = [state]
187 if "RaidLevel:" in line:
188 raid_level = line.split()[3]
189 array_details[logical_drive].append(raid_level)
191 if len(array_details) == 0:
192 message += "No arrays found on controller %s. " % controller
197 array_keys = array_details.keys()
199 number_arrays += len(array_keys)
201 for drive in array_keys:
202 state = array_details[drive][0]
203 if state != "OPTIMAL":
204 non_optimal_arrays += 1
205 raid_level = array_details[drive][1]
206 # The Array number here is incremented by one because of the
207 # inconsistent way that the LSI tools count arrays.
208 # This brings it back in line with the view in the bios
209 # and from megamgr.bin where the array counting starts at
211 message += 'Array %s status is "%s"' % (int(drive)+1, state)
212 message += '(Raid-%s on adapter %s), ' \
213 % (raid_level, controller)
217 message = add_status_summary(status, \
221 message = message.rstrip(" ")
222 message = message.rstrip(",")
225 message = add_checked_summary(message, \
228 return status, message
231 def add_status_summary(status, message, non_optimal_arrays):
232 """Add initial summary information on the overall state of the arrays"""
235 message += "All arrays OK"
237 if non_optimal_arrays == 1:
238 message = "%s array not OK - " % non_optimal_arrays \
241 message = "%s arrays not OK - " % non_optimal_arrays \
247 def add_checked_summary(message, number_arrays, number_controllers):
248 """ Adds ending summary information on how many arrays were checked"""
250 message += " [%s array" % number_arrays
251 if number_arrays != 1:
253 message += " checked on %s controller" % number_controllers
255 if number_controllers == 1:
264 """parses args and calls func to test raid arrays"""
266 parser = OptionParser()
267 parser.add_option( "-n",
271 help="Do not display the number of arrays " \
272 + "checked. By default the number of arrays " \
273 + "checked are printed at the end of the " \
274 + "line. This is useful information and helps to " \
275 + "know that they are detected properly")
277 parser.add_option( "-v",
281 help="Verbose mode. Good for testing plugin. By \
282 default only one result line is printed as per Nagios standards")
284 parser.add_option( "-V",
286 action = "store_true",
288 help = "Print version number and exit" )
290 (options, args) = parser.parse_args()
292 no_summary = options.no_summary
293 verbosity = options.verbosity
294 version = options.version
304 result, message = test_raid(verbosity, no_summary)
309 if __name__ == "__main__":
312 except KeyboardInterrupt:
313 print "Caught Control-C..."