retire da-backup checks
[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 not os.path.exists(BIN):
101     end(UNKNOWN, "Lsi MegaRaid utility '%s' was not found" % BIN)
102
103 if not os.access(BIN, os.X_OK):
104     end(UNKNOWN, "Lsi MegaRaid utility '%s' is not executable" % BIN)
105
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)
110
111
112 def run(args):
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"
117         sys.exit(UNKNOWN)
118     cmd = "sudo %s %s -nolog" % (BIN, args)
119     result, output = commands.getstatusoutput(cmd)
120     lines = output.split("\n")
121     if result != 0:
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")
131         else:
132             end(UNKNOWN, "Error using MegaRaid utility - %s" \
133                                                     % output.replace("\n", "|"))
134
135     return lines
136
137
138 def get_controllers(verbosity):
139     """finds and returns a list of all controllers on the local machine"""
140
141     lines = run("-AllAdpInfo")
142
143     if lines[11].strip() == "No Adapters Found":
144         end(WARNING, "No LSI adapters were found on this machine")
145
146     controllers = []
147     controller_lines = lines[12:]
148     for line in controller_lines:
149         try:
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)
154
155     if len(controllers) == 0:
156         end(WARNING, "No LSI controllers were found on this machine")
157
158     if verbosity >= 2:
159         print "Found %s controller(s)" % len(controllers)
160
161     return controllers
162
163
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"""
167
168     status = OK
169     message = ""
170     number_arrays = 0
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 )
176         if verbosity >= 3:
177             for line in detailed_output:
178                 print "%s" % line
179             print
180         array_details = {}
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)
189
190         if len(array_details) == 0:
191             message += "No arrays found on controller %s. " % controller
192             if status == OK:
193                 status = WARNING
194             continue
195
196         array_keys = array_details.keys()
197         array_keys.sort()
198         number_arrays += len(array_keys)
199
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
209                 # 1 instead of 0
210                 message += 'Array %s status is "%s"' % (int(drive)+1, state)
211                 message += '(Raid-%s on adapter %s), ' \
212                                                   % (raid_level, controller)
213                 status = CRITICAL
214
215
216     message = add_status_summary(status, \
217                           message, \
218                           non_optimal_arrays)
219
220     message = message.rstrip(" ")
221     message = message.rstrip(",")
222
223     if not no_summary:
224         message = add_checked_summary(message, \
225                               number_arrays, \
226                               number_controllers)
227     return status, message
228
229
230 def add_status_summary(status, message, non_optimal_arrays):
231     """Add initial summary information on the overall state of the arrays"""
232
233     if status == OK:
234         message += "All arrays OK"
235     else:
236         if non_optimal_arrays == 1:
237             message = "%s array not OK - " % non_optimal_arrays \
238                     + message
239         else:
240             message = "%s arrays not OK - " % non_optimal_arrays \
241                     + message
242
243     return message
244
245
246 def add_checked_summary(message, number_arrays, number_controllers):
247     """ Adds ending summary information on how many arrays were checked"""
248
249     message += " [%s array" % number_arrays
250     if number_arrays != 1:
251         message += "s"
252     message += " checked on %s controller" % number_controllers
253
254     if number_controllers == 1:
255         message += "]"
256     else:
257         message += "s]"
258
259     return message
260
261
262 def main():
263     """parses args and calls func to test raid arrays"""
264
265     parser = OptionParser()
266     parser.add_option( "-n",
267                        "--no-summary",
268                        action="store_true",
269                        dest="no_summary",
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")
275
276     parser.add_option(  "-v",
277                         "--verbose",
278                         action="count",
279                         dest="verbosity",
280                         help="Verbose mode. Good for testing plugin. By \
281 default only one result line is printed as per Nagios standards")
282
283     parser.add_option( "-V",
284                         "--version",
285                         action = "store_true",
286                         dest = "version",
287                         help = "Print version number and exit" )
288
289     (options, args) = parser.parse_args()
290
291     no_summary = options.no_summary
292     verbosity  = options.verbosity
293     version    = options.version
294
295     if args:
296         parser.print_help()
297         sys.exit(UNKNOWN)
298
299     if version:
300         print __version__
301         sys.exit(OK)
302
303     result, message = test_raid(verbosity, no_summary)
304
305     end(result, message)
306
307
308 if __name__ == "__main__":
309     try:
310         main()
311     except KeyboardInterrupt:
312         print "Caught Control-C..."
313         sys.exit(CRITICAL)