Different bin path
[mirror/dsa-nagios.git] / dsa-nagios-checks / checks / dsa-check-raid-megaraid
1 #!/usr/bin/python
2 #
3 #   Copyright Hari Sekhon 2007
4 #
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.
9 #
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.
14 #
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
18 #
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
21
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"""
26
27 __version__ = 0.8
28
29 import os
30 import sys
31 import commands
32 from optparse import OptionParser
33
34 # Standard Nagios return codes
35 OK       = 0
36 WARNING  = 1
37 CRITICAL = 2
38 UNKNOWN  = 3
39
40 SRCDIR   = os.path.dirname(sys.argv[0])
41 BIN      = "/usr/local/bin/megarc"
42 MEGADEV  = "/dev/megadev0"
43
44
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"""
48
49     if status == OK:
50         print "RAID OK: %s" % message
51         sys.exit(OK)
52     elif status == WARNING:
53         print "RAID WARNING: %s" % message
54         sys.exit(WARNING)
55     elif status == CRITICAL:
56         print "RAID CRITICAL: %s" % message
57         sys.exit(CRITICAL)
58     else:
59         print "UNKNOWN: %s" % message
60         sys.exit(UNKNOWN)
61
62
63 def make_megadev(devicenode):
64     """Creates the device node needed for the Lsi utility to work
65     (usually /dev/megadev0)"""
66
67     try:
68         devices = open("/proc/devices", "r")
69         lines   = devices.read()
70         devices.close()
71     except IOError, error:
72         end(UNKNOWN, "Error reading /proc/devices while trying to create " \
73                    + "device node '%s' - %s" % (devicenode, error))
74     device = ""
75     for line in lines.split("\n"):
76         line = line.split()
77         if len(line) > 1:
78             major_number = line[0]
79             device       = line[1]
80             if device == "megadev":
81                 break
82
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
89     try:
90         result, output = commands.getstatusoutput(cmd)
91         if result != 0:
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))
98
99
100 if os.geteuid() != 0:
101     end(UNKNOWN, "You must be root to run this plugin")
102
103 if not os.path.exists(BIN):
104     end(UNKNOWN, "Lsi MegaRaid utility '%s' was not found" % BIN)
105
106 if not os.access(BIN, os.X_OK):
107     end(UNKNOWN, "Lsi MegaRaid utility '%s' is not executable" % BIN)
108
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)
113
114
115 def run(args):
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"
120         sys.exit(UNKNOWN)
121     cmd = "%s %s -nolog" % (BIN, args)
122     result, output = commands.getstatusoutput(cmd)
123     lines = output.split("\n")
124     if result != 0:
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")
134         else:
135             end(UNKNOWN, "Error using MegaRaid utility - %s" \
136                                                     % output.replace("\n", "|"))
137
138     return lines
139
140
141 def get_controllers(verbosity):
142     """finds and returns a list of all controllers on the local machine"""
143
144     lines = run("-AllAdpInfo")
145
146     if lines[11].strip() == "No Adapters Found":
147         end(WARNING, "No LSI adapters were found on this machine")
148
149     controllers = []
150     controller_lines = lines[12:]
151     for line in controller_lines:
152         try:
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)
157
158     if len(controllers) == 0:
159         end(WARNING, "No LSI controllers were found on this machine")
160
161     if verbosity >= 2:
162         print "Found %s controller(s)" % len(controllers)
163
164     return controllers
165
166
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"""
170
171     status = OK
172     message = ""
173     number_arrays = 0
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 )
179         if verbosity >= 3:
180             for line in detailed_output:
181                 print "%s" % line
182             print
183         array_details = {}
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)
192
193         if len(array_details) == 0:
194             message += "No arrays found on controller %s. " % controller
195             if status == OK:
196                 status = WARNING
197             continue
198
199         array_keys = array_details.keys()
200         array_keys.sort()
201         number_arrays += len(array_keys)
202
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
212                 # 1 instead of 0
213                 message += 'Array %s status is "%s"' % (int(drive)+1, state)
214                 message += '(Raid-%s on adapter %s), ' \
215                                                   % (raid_level, controller)
216                 status = CRITICAL
217
218
219     message = add_status_summary(status, \
220                           message, \
221                           non_optimal_arrays)
222
223     message = message.rstrip(" ")
224     message = message.rstrip(",")
225
226     if not no_summary:
227         message = add_checked_summary(message, \
228                               number_arrays, \
229                               number_controllers)
230     return status, message
231
232
233 def add_status_summary(status, message, non_optimal_arrays):
234     """Add initial summary information on the overall state of the arrays"""
235
236     if status == OK:
237         message += "All arrays OK"
238     else:
239         if non_optimal_arrays == 1:
240             message = "%s array not OK - " % non_optimal_arrays \
241                     + message
242         else:
243             message = "%s arrays not OK - " % non_optimal_arrays \
244                     + message
245
246     return message
247
248
249 def add_checked_summary(message, number_arrays, number_controllers):
250     """ Adds ending summary information on how many arrays were checked"""
251
252     message += " [%s array" % number_arrays
253     if number_arrays != 1:
254         message += "s"
255     message += " checked on %s controller" % number_controllers
256
257     if number_controllers == 1:
258         message += "]"
259     else:
260         message += "s]"
261
262     return message
263
264
265 def main():
266     """parses args and calls func to test raid arrays"""
267
268     parser = OptionParser()
269     parser.add_option( "-n",
270                        "--no-summary",
271                        action="store_true",
272                        dest="no_summary",
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")
278
279     parser.add_option(  "-v",
280                         "--verbose",
281                         action="count",
282                         dest="verbosity",
283                         help="Verbose mode. Good for testing plugin. By \
284 default only one result line is printed as per Nagios standards")
285
286     parser.add_option( "-V",
287                         "--version",
288                         action = "store_true",
289                         dest = "version",
290                         help = "Print version number and exit" )
291
292     (options, args) = parser.parse_args()
293
294     no_summary = options.no_summary
295     verbosity  = options.verbosity
296     version    = options.version
297
298     if args:
299         parser.print_help()
300         sys.exit(UNKNOWN)
301
302     if version:
303         print __version__
304         sys.exit(OK)
305
306     result, message = test_raid(verbosity, no_summary)
307
308     end(result, message)
309
310
311 if __name__ == "__main__":
312     try:
313         main()
314     except KeyboardInterrupt:
315         print "Caught Control-C..."
316         sys.exit(CRITICAL)