├── .gitignore
├── .project
├── changelog
├── README.md
├── COPYING
└── check_lsi_raid
/.gitignore:
--------------------------------------------------------------------------------
1 | storcli_output
2 | storcli-testing
3 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | check_lsi_raid
4 |
5 |
6 |
7 |
8 |
9 | org.epic.perleditor.perlbuilder
10 |
11 |
12 |
13 |
14 |
15 | org.epic.perleditor.perlnature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/changelog:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | Changelog for check_lsi_raid, a Nagios/Icinga plugin to check LSI RAID
3 | controllers
4 | ###############################################################################
5 |
6 | Version 2.5 20161118
7 | * Increased temperature warning and critical thresholds for the controller
8 | * Check if GasGaugeStatus equals to N/A string
9 | * Check for failure 46, drive not attached
10 |
11 |
12 | Version 2.4 20160519
13 | * Exit with warning if no storage is attached to controller
14 | * Add JBOD to valid states (THX to ddesmet)
15 |
16 | Version 2.3 20151020
17 | * Check for solaris in linux code path
18 | * Remove storcli logs per default, as a log is created on each plugin call
19 |
20 | Version 2.2 20150624
21 | * Dereference hash keys when using key method
22 | * DHS is a valid drive state (dedicated hot spare)
23 | * Add nosudo to help text, only add sudo if not already root
24 | * Fix status string check and setting of Critical level
25 |
26 | Version 2.1 20141120
27 | * Various bug fixes
28 | ** Allow state for global hot spare
29 | ** Skip consistency check for cachecade volumes
30 | ** Dereference status level
31 |
32 | Version 2.0 20141020
33 | * Completed refactoring of all check methods, the plugin now seperates
34 | value gathering and status checking.
35 | * The /c0 show all command is no longer used, as it influences the IO
36 | response time of the controller.
37 | * Take a look at the README file to get a complete list of what is checked.
38 |
39 | Version 1.2 20140304
40 | * Plugin is now working with current storcli 1.07.07
41 | * Fix physical drive check as code did not reach the check
42 | * Check for storcli on more paths
43 | * Replace storli command return status in separate method
44 |
45 | Version 1.1 20131128
46 | * Thanks to Jonas Meurer for the patches
47 | * Minor fix: uninitialized variables for bbu=0
48 | * Typo fix: "other error count" instead of "media error count"
49 | * Output STATUS variable for nagios performance data processing
50 | * Add commandline options to define warning threshold for media errors, other
51 | errors, predictive fail count and shield count
52 | * Set state to critical for non-optimal virtual/logical disks
53 |
54 | Version 1.0 20131028
55 | * First stable release. Improved testing with reading output from simple text
56 | files.
57 | * Check status of BBU and CacheVault
58 | * Fix storcli path checking
59 | * Fix rebuild progress for physical devices
60 | * Fix temperature check for a real integer
61 |
62 | List of contributors:
63 | * Georg Schoenberger (gschoenberger@thomas-krenn.com)
64 | * Grubhofer Martin (s1110239013@students.fh-hagenberg.at)
65 | * Scheipner Alexander (s1110239032@students.fh-hagenberg.at)
66 | * Werner Sebastian (s1110239038@students.fh-hagenberg.at)
67 | * Jonas Meurer (jmeurer@inet.de)
68 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # check_lsi_raid - Nagios/Icinga plugin to check LSI RAID Controllers
2 |
3 | ## WARNING
4 |
5 | Running this plugin at a low time frame frequently (e.g. every 1/5/10 minutes) the controller suffers from
6 | performance issues!
7 | Most of the time is might be sufficient to check the status of controller and disks once a day to not
8 | affect performance but also get a decent monitoring.
9 | Cf. https://github.com/thomas-krenn/check_lsi_raid/issues/42
10 |
11 | ## General
12 |
13 | * The LSI RAID Monitoring Plugin is a Nagios/Icinga to check the LSI RAID Controller for warnings or critical errors.
14 | * It is written in Perl and uses the storage command line tool storcli to interact with the RAID Controller.
15 |
16 | ## Current Version
17 |
18 | * The current version of the check_lsi_raid plugin is available at the Thomas-Krenn github account:
19 | https://github.com/thomas-krenn/check_lsi_raid
20 |
21 | ## Further information
22 |
23 | * A wiki article can be found at:
24 | http://www.thomas-krenn.com/de/wiki/LSI_RAID_Monitoring_Plugin or
25 | http://www.thomas-krenn.com/en/wiki/LSI_RAID_Monitoring_Plugin
26 | * Windows user should read:
27 | http://www.thomas-krenn.com/de/wiki/LSI_RAID_Monitoring_Plugin_unter_Windows_Server_2012_einrichten
28 |
29 | ## Functionalities
30 |
31 | * Controller Status
32 | * ROC temperature
33 | * Degraded drives
34 | * Offline drives
35 | * Critical disks
36 | * Failed Disks
37 | * Memory correctable errors
38 | * Memory uncorrectable errors
39 |
40 | * Logical Device Status
41 | * State
42 | * Consistency
43 | * Initialization
44 |
45 | * Physical Device Status
46 | * State
47 | * Shield counter
48 | * Media, Other, BBM, Predictive error counts
49 | * SMART alert flag
50 | * Drive temperature
51 | * Initialization
52 | * Rebuild
53 |
54 | * If BBU or CV is present (unless disabled)
55 |
56 | * BBU
57 | * Battery State
58 | * Temperature
59 | * Firmware temperature
60 | * Voltage
61 | * I2C errors
62 | * Replacement required
63 | * Capacity low
64 | * Is about to fail
65 | * GasGauge discharged
66 | * GasGauge over temperature
67 | * GasGauge over charged
68 |
69 | * CV
70 | * State
71 | * Temperature
72 | * Replacement required
73 |
74 | ## Used storcli commands
75 |
76 | * storcli /c0 /bbu show
77 | * storcli /c0 /bbu show status
78 | * storcli /c0 /cv show
79 | * storcli /c0 /cv show status
80 | * storcli adpallinfo a0
81 | * storcli /c0 show time
82 | * storcli /c0/vall show all
83 | * storcli /c0/vall show init
84 | * storcli /c0/eall/sall
85 | * storcli /c0/eall/sall
86 | * storcli /c0/eall/sall
87 |
88 | ## Installation Requirements
89 |
90 | * libfile-which-perl
91 | * storcli
92 |
93 | ## Requirements for Icinga/Nagios
94 |
95 | * On the system to be monitored:
96 | * check_lsi_raid plugin
97 | * storcli
98 | * sudoers entry for user nagios and storcli
99 | (example: nagios ALL=(root) NOPASSWD:/usr/sbin/storcli)
100 | * NRPE (optional): command definition
101 | * On the Icinga-server:
102 | * command definition
103 | * service definition
104 |
105 | ## Installation hints for CentOS
106 |
107 | * It is possible that the NRPE daemon runs as user 'nrpe'. If that's the case
108 | you have to change the user in the sudoers entry.
109 | * It may be necessary to specify the perl interpreter explicitly in the NRPE
110 | command definition, e.g.:
111 | command[check_lsi_raid]=/usr/lib/nagios/plugins/check_lsi_raid -C 0 -p /usr/sbin/storcli
112 | * On newer CentOS/RedHat systems problems with sudo/SELinux might occur. In order to run the plugin
113 | as root the following sudoers options might help:
114 | Defaults:nrpe !requiretty
115 | Further more cf. https://github.com/thomas-krenn/check_lsi_raid/issues/7
116 |
117 | ## Parameter usage (example)
118 |
119 | ./check_lsi_raid -Tw 40 -Tc 50 -LD 0,1 -PD 1 -b 0
120 |
121 | ## License
122 |
123 | Copyright (C) 2013-2018 Thomas-Krenn.AG
124 |
125 | This program is free software; you can redistribute it and/or modify it under
126 | the terms of the GNU General Public License as published by the Free Software
127 | Foundation; either version 3 of the License, or (at your option) any later version.
128 |
129 | This program is distributed in the hope that it will be useful, but WITHOUT
130 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
131 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
132 |
133 | You should have received a copy of the GNU General Public License along with
134 | this program; if not, see .
135 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/check_lsi_raid:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w
2 | # =============================================================================
3 | # check_lsi_raid: Nagios/Icinga plugin to check LSI Raid Controller status
4 | # -----------------------------------------------------------------------------
5 | # Created as part of a semester project at the University of Applied Sciences
6 | # Hagenberg (http://www.fh-ooe.at/en/hagenberg-campus/)
7 | #
8 | # Copyright (c) 2013-2016:
9 | # Georg Schoenberger (gschoenberger@thomas-krenn.com)
10 | # Grubhofer Martin (s1110239013@students.fh-hagenberg.at)
11 | # Scheipner Alexander (s1110239032@students.fh-hagenberg.at)
12 | # Werner Sebastian (s1110239038@students.fh-hagenberg.at)
13 | # Jonas Meurer (jmeurer@inet.de)
14 | #
15 | # This program is free software; you can redistribute it and/or modify it under
16 | # the terms of the GNU General Public License as published by the Free Software
17 | # Foundation; either version 3 of the License, or (at your option) any later
18 | # version.
19 | #
20 | # This program is distributed in the hope that it will be useful, but WITHOUT
21 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
23 | # details.
24 | #
25 | # You should have received a copy of the GNU General Public License along with
26 | # this program; if not, see .
27 | # ==============================================================================
28 | use strict;
29 | use warnings;
30 | use Getopt::Long qw(:config no_ignore_case);
31 | use File::Which;
32 |
33 | our $VERBOSITY = 0;
34 | our $VERSION = "2.5";
35 | our $NAME = "check_lsi_raid: Nagios/Icinga plugin to check LSI Raid Controller status";
36 | our $C_TEMP_WARNING = 85;
37 | our $C_TEMP_CRITICAL = 95;
38 | our $C_MEM_CORRECTABLE_WARNING = 0;
39 | our $C_MEM_CORRECTABLE_CRITICAL = 0;
40 | our $PD_TEMP_WARNING = 40;
41 | our $PD_TEMP_CRITICAL = 45;
42 | our $BBU_TEMP_WARNING = 50;
43 | our $BBU_TEMP_CRITICAL = 60;
44 | our $CV_TEMP_WARNING = 70;
45 | our $CV_TEMP_CRITICAL = 85;
46 | our ($IGNERR_M, $IGNERR_O, $IGNERR_P, $IGNERR_S, $IGNERR_B) = (0, 0, 0, 0, 0);
47 | our $NOENCLOSURES = 0;
48 | our $NOWRITEBACKOK = 0;
49 | our $NOCONSISTENCYOK = 0;
50 | our $CONTROLLER = 0;
51 |
52 | use constant {
53 | STATE_OK => 0,
54 | STATE_WARNING => 1,
55 | STATE_CRITICAL => 2,
56 | STATE_UNKNOWN => 3,
57 | };
58 |
59 | # Header maps to parse logical and physical devices
60 | our $LDMAP;
61 | our @map_a = ('DG/VD','TYPE','State','Access','Consist','Cache','sCC','Size');
62 | our @map_cc_a = ('DG/VD','TYPE','State','Access','Consist','Cache','Cac','sCC','Size');
63 | our @pdmap_a = ('EID:Slt','DID','State','DG','Size','Intf','Med','SED','PI','SeSz','Model','Sp');
64 |
65 | # Print command line usage to stdout.
66 | sub displayUsage {
67 | print "Usage: \n";
68 | print " [ -h | --help ]
69 | Display this help page\n";
70 | print " [ -v | -vv | -vvv | --verbose ]
71 | Sets the verbosity level.
72 | No -v is the normal single line output for Nagios/Icinga, -v is a
73 | more detailed version but still usable in Nagios. -vv is a
74 | multiline output for debugging configuration errors or more
75 | detailed information. -vvv is for plugin problem diagnosis.
76 | For further information please visit:
77 | http://nagiosplug.sourceforge.net/developer-guidelines.html#AEN39\n";
78 | print " [ -V --version ]
79 | Displays the plugin and, if available, the version of StorCLI.\n";
80 | print " [ -C | --controller ]
81 | Specifies a controller number, defaults to 0.\n";
82 | print " [ -EID | --enclosure ]
83 | Specifies one or more enclosure numbers, per default all enclosures. Takes either
84 | an integer as additional argument or a commaseperated list,
85 | e.g. '0,1,2'. With --noenclosures enclosures can be disabled.\n";
86 | print " [ -LD | --logicaldevice ]
87 | Specifies one or more logical devices, defaults to all. Takes either an
88 | integer as additional argument or a comma seperated list e.g. '0,1,2'.\n";
89 | print " [ -PD | --physicaldevice ]
90 | Specifies one or more physical devices, defaults to all. Takes either an
91 | integer as additional argument or a comma seperated list e.g. '0,1,2'.\n";
92 | print " [ -Tw | --temperature-warn ]
93 | Specifies the RAID controller temperature warning threshold, the default
94 | threshold is ${C_TEMP_WARNING}C.\n";
95 | print " [ -Tc | --temperature-critical ]
96 | Specifies the RAID controller temperature critical threshold, the default
97 | threshold is ${C_TEMP_CRITICAL}C.\n";
98 | print " [ -PDTw | --physicaldevicetemperature-warn ]
99 | Specifies the disk temperature warning threshold, the default threshold
100 | is ${PD_TEMP_WARNING}C.\n";
101 | print " [ -PDTc | --physicaldevicetemperature-critical ]
102 | Specifies the disk temperature critical threshold, the default threshold
103 | is ${PD_TEMP_CRITICAL}C.\n";
104 | print " [ -BBUTw | --bbutemperature-warning ]
105 | Specifies the BBU temperature warning threshold, default threshold
106 | is ${BBU_TEMP_WARNING}C.\n";
107 | print " [ -BBUTc | --bbutemperature-critical ]
108 | Specifies the BBU temperature critical threshold, default threshold
109 | is ${BBU_TEMP_CRITICAL}C.\n";
110 | print " [ -CVTw | --cvtemperature-warning ]
111 | Specifies the CV temperature warning threshold, default threshold
112 | is ${CV_TEMP_WARNING}C.\n";
113 | print " [ -CVTc | --cvtemperature-critical ]
114 | Specifies the CV temperature critical threshold, default threshold
115 | is ${CV_TEMP_CRITICAL}C.\n";
116 | print " [ -Im | --ignore-media-errors ]
117 | Specifies the warning threshold for media errors per disk, the default
118 | threshold is $IGNERR_M.\n";
119 | print " [ -Io | --ignore-other-errors ]
120 | Specifies the warning threshold for other errors per disk, the default
121 | threshold is $IGNERR_O.\n";
122 | print " [ -Ip | --ignore-predictive-fail-count ]
123 | Specifies the warning threshold for predictive failure analysis errors per disk, the default
124 | threshold is $IGNERR_P.\n";
125 | print " [ -Is | --ignore-shield-counter ]
126 | Specifies the warning threshold for shield counter per disk, the default
127 | threshold is $IGNERR_S.\n";
128 | print " [ -Ib | --ignore-bbm-counter ]
129 | Specifies the warning threshold for bbm errors per disk, the default
130 | threshold is $IGNERR_B.\n";
131 | print " [ -p | --path ]
132 | Specifies the path to StorCLI, per default uses the tool 'which' to get
133 | the StorCLI path and also checks for binaries in /opt/MegaRAID/storcli.\n";
134 | print " [ -b <0/1/2> | --BBU <0/1/2> ]
135 | Check if a BBU or a CacheVault module is present. One must be present unless
136 | '-b 0' is defined. This ensures that for a given controller a BBU/CV must be
137 | present per default. '-b 2' checks if one is present, but does not exit
138 | CRITICAL when there is no BBU/CV.\n";
139 | print " [ --noenclosures <0/1> ]
140 | Specifies if enclosures are present or not. 0 means enclosures are
141 | present (default), 1 states no enclosures are used (no 'eall' in
142 | storcli commands).\n";
143 | print " [ --nowritebackok <0/1> ]
144 | Specifies if a WriteThrough Cache configuration is ok or not. 0 means WriteThrough is
145 | considered Critical (default), 1 states WriteThrough is ok.\n";
146 | print " [ --noconsistencyok <0/1> ]
147 | Specifies if the consistency of logical devices should be checked. 0 means consistency
148 | should be checked (default), 1 means not.\n";
149 | print " [ --nosudo ]
150 | Turn off using sudo.\n";
151 | print " [ --nocleanlogs ]
152 | Do not clean storcli logs after running storcli commands.\n";
153 | }
154 |
155 | # Displays a short Help text for the user
156 | sub displayHelp {
157 | print $NAME."\n";
158 | print "Pulgin version: " . $VERSION ."\n";
159 | print "Copyright (C) 2013-2015 Thomas-Krenn.AG\n";
160 | print "Current updates available at
161 | https://github.com/thomas-krenn/check_lsi_raid.git\n";
162 | print "This Nagios/Icinga Plugin checks LSI RAID controllers for controller,
163 | physical device, logical device, BBU and CV warnings and errors.\n";
164 | print "In order for this plugin to work properly you need to add the nagios
165 | user to your sudoers file (or create a new one in /etc/sudoers.d/).\n";
166 | displayUsage();
167 | print "Further information about this plugin can be found at:
168 | http://www.thomas-krenn.com/de/wiki/LSI_RAID_Monitoring_Plugin and
169 | http://www.thomas-krenn.com/de/wiki/LSI_RAID_Monitoring_Plugin
170 | Please send an email to the tk-monitoring plugin-user mailing list:
171 | tk-monitoring-plugins-user\@lists.thomas-krenn.com
172 | if you have questions regarding use of this software, to submit patches, or
173 | suggest improvements.
174 | Example usage:
175 | * If StorCli can be found with 'which' or in /opt/MegaRAID/storcli
176 | * check_lsi_raid
177 | * check_lsi_raid -p /opt/MegaRAID/storcli/storcli64
178 | * check_lsi_raid -p /opt/MegaRAID/storcli/storcli64 -C 1\n";
179 | exit(STATE_UNKNOWN);
180 | }
181 |
182 | # Prints the name and the version of check_lsi_raid. If storcli is available,
183 | # the version of it is printed also.
184 | # @param storcli The path to storcli command utility
185 | sub displayVersion {
186 | my $storcli = shift;
187 | my $writelogs = shift;
188 | if(defined($storcli)){
189 | my $command = $storcli.' -v';
190 | if(!$writelogs) { $command .= ' nolog'; }
191 | my @storcliVersion = `$command`;
192 | foreach my $line (@storcliVersion){
193 | if($line =~ /^\s+StorCli.*/) {
194 | $line =~ s/^\s+|\s+$//g;
195 | print $line;
196 | }
197 | }
198 | print "\n";
199 | }
200 | exit(STATE_OK);
201 | }
202 |
203 | # Checks if a storcli call was successfull, i.e. if the line 'Status = Sucess'
204 | # is present in the command output.
205 | # @param output The output of the storcli command as array
206 | # @return 1 on success, 0 if not
207 | sub checkCommandStatus{
208 | my @output = @{(shift)};
209 | foreach my $line (@output){
210 | if($line =~ /^Status/){
211 | if($line eq "Status = Success\n"){
212 | return 1;
213 | }
214 | elsif (grep { /Fail(ed|ure)\s+46/i } @output){
215 | # Return 46 means a drive is not attached, this is a valid failure
216 | return 1;
217 | }
218 | else{
219 | return 0;
220 | }
221 | }
222 | }
223 | }
224 |
225 | # Shows the time the controller is using. Can be used to check if the
226 | # controller number is a correct one.
227 | # @param storcli The path to storcli command utility, followed by the controller
228 | # number, e.g. 'storcli64 /c0'.
229 | # @return 1 on success, 0 if not
230 | sub getControllerTime{
231 | my $storcli = shift;
232 | my $writelogs = shift;
233 | my $command = $storcli.' show time';
234 | if(!$writelogs) { $command .= ' nolog'; }
235 | my @output = `$command`;
236 | return (checkCommandStatus(\@output));
237 | }
238 |
239 | # Get the status of the raid controller
240 | # @param storcli The path to storcli command utility, followed by the controller
241 | # number, e.g. 'storcli64 /c0'.
242 | # @param logDevices If given, a list of desired logical device numbers
243 | # @param commands_a An array to push the used command to
244 | # @return A hash, each key a value of the raid controller info
245 | sub getControllerInfo{
246 | my $storcli = shift;
247 | my $writelogs = shift;
248 | my $commands_a = shift;
249 | my $command = '';
250 |
251 | $storcli =~ /^(.*)\/c[0-9]+/;
252 | $command = $1.'/c'.$CONTROLLER.' show all';
253 | if(!$writelogs) { $command .= ' nolog'; }
254 |
255 | push @{$commands_a}, $command;
256 | my @output = `$command`;
257 | if($? >> 8 != 0){
258 | print "Invalid StorCLI command! ($command)\n";
259 | exit(STATE_UNKNOWN);
260 | }
261 | my %foundController_h;
262 | foreach my $line(@output){
263 | if($line =~ /\=/){
264 | my @lineVals = split('=', $line);
265 | $lineVals[0] =~ s/^\s+|\s+$//g;
266 | $lineVals[1] =~ s/^\s+|\s+$//g;
267 | $foundController_h{$lineVals[0]} = $lineVals[1];
268 | }
269 | }
270 | return \%foundController_h;
271 | }
272 |
273 | # Checks the status of the raid controller
274 | # @param statusLevel_a The status level array, elem 0 is the current status,
275 | # elem 1 the warning sensors, elem 2 the critical sensors, elem 3 the verbose
276 | # information for the sensors.
277 | # @param foundController The hash of controller infos, created by getControllerInfo
278 | sub getControllerStatus{
279 | my @statusLevel_a = @{(shift)};
280 | my %foundController = %{(shift)};
281 | my $status = '';
282 | foreach my $key (%foundController){
283 | if($key eq 'ROC temperature(Degree Celsius)'){
284 | $foundController{$key} =~ /^([0-9]+\.?[0-9]+).*$/;
285 | if(defined($1)){
286 | if(!(checkThreshs($1, $C_TEMP_CRITICAL))){
287 | $status = 'Critical';
288 | push @{$statusLevel_a[2]}, 'ROC_Temperature';
289 | }
290 | elsif(!(checkThreshs($1, $C_TEMP_WARNING))){
291 | $status = 'Warning' unless $status eq 'Critical';
292 | push @{$statusLevel_a[1]}, 'ROC_Temperature';
293 | }
294 | $statusLevel_a[3]->{'ROC_Temperature'} = $1;
295 | }
296 | }
297 | elsif($key eq 'Degraded'){
298 | if($foundController{$key} != 0){
299 | $status = 'Warning' unless $status eq 'Critical';
300 | push @{$statusLevel_a[1]}, 'CTR_Degraded_drives';
301 | $statusLevel_a[3]->{'CTR_Degraded_drives'} = $foundController{$key};
302 | }
303 | }
304 | elsif($key eq 'Offline'){
305 | if($foundController{$key} != 0){
306 | $status = 'Warning' unless $status eq 'Critical';
307 | push @{$statusLevel_a[1]}, 'CTR_Offline_drives';
308 | $statusLevel_a[3]->{'CTR_Offline_drives'} = $foundController{$key};
309 | }
310 | }
311 | elsif($key eq 'Critical Disks'){
312 | if($foundController{$key} != 0){
313 | $status = 'Critical';
314 | push @{$statusLevel_a[2]}, 'CTR_Critical_disks';
315 | $statusLevel_a[3]->{'CTR_Critical_disks'} = $foundController{$key};
316 | }
317 | }
318 | elsif($key eq 'Failed Disks'){
319 | if($foundController{$key} != 0){
320 | $status = 'Critical';
321 | push @{$statusLevel_a[2]}, 'CTR_Failed_disks';
322 | $statusLevel_a[3]->{'CTR_Failed_disks'} = $foundController{$key};
323 | }
324 | }
325 | elsif($key eq 'Memory Correctable Errors'){
326 | if($foundController{$key} != 0){
327 | if(!(checkThreshs($foundController{$key}, $C_MEM_CORRECTABLE_CRITICAL))){
328 | $status = 'Critical';
329 | push @{$statusLevel_a[2]}, 'CTR_Memory_correctable_errors';
330 | }
331 | elsif(!(checkThreshs($foundController{$key}, $C_MEM_CORRECTABLE_WARNING))){
332 | $status = 'Warning' unless $status eq 'Critical';
333 | push @{$statusLevel_a[1]}, 'CTR_Memory_correctable_errors';
334 | }
335 | $statusLevel_a[3]->{'CTR_Memory_correctable_errors'} = $foundController{$key};
336 | }
337 | }
338 | elsif($key eq 'Memory Uncorrectable Errors'){
339 | if($foundController{$key} != 0){
340 | $status = 'Critical';
341 | push @{$statusLevel_a[2]}, 'CTR_Memory_Uncorrectable_errors';
342 | $statusLevel_a[3]->{'CTR_Memory_Uncorrectable_errors'} = $foundController{$key};
343 | }
344 | }
345 | }
346 | if($status ne ''){
347 | if($status eq 'Warning'){
348 | if(${$statusLevel_a[0]} ne 'Critical'){
349 | ${$statusLevel_a[0]} = 'Warning';
350 | }
351 | }
352 | else{
353 | ${$statusLevel_a[0]} = 'Critical';
354 | }
355 | $statusLevel_a[3]->{'CTR_Status'} = $status;
356 | }
357 | else{
358 | $statusLevel_a[3]->{'CTR_Status'} = 'OK';
359 | }
360 | }
361 |
362 | # Checks which logical devices are present for the given controller and parses
363 | # the logical devices to a list of hashes. Each hash represents a logical device
364 | # with its values from the output.
365 | # @param storcli The path to storcli command utility, followed by the controller
366 | # number, e.g. 'storcli64 /c0'.
367 | # @param logDevices If given, a list of desired logical device numbers
368 | # @param action The storcli action to check, 'all' or 'init'
369 | # @param commands_a An array to push the used command to
370 | # @return A list of hashes, each hash is one logical device. Check ldmap_a for valid
371 | # hash keys.
372 | sub getLogicalDevices{
373 | my $storcli = shift;
374 | my $writelogs = shift;
375 | my @logDevices = @{(shift)};
376 | my $action = shift;
377 | my $commands_a = shift;
378 |
379 | my $command = $storcli;
380 | if(scalar(@logDevices) == 0) { $command .= "/vall"; }
381 | elsif(scalar(@logDevices) == 1) { $command .= "/v$logDevices[0]"; }
382 | else { $command .= "/v".join(",", @logDevices); }
383 | $command .= " show $action";
384 | if(!$writelogs) { $command .= ' nolog'; }
385 | push @{$commands_a}, $command;
386 |
387 | my @output = `$command`;
388 | my @foundDevs;
389 | if(checkCommandStatus(\@output)) {
390 | if($action eq "all") {
391 | my $currBlock;
392 | foreach my $line(@output){
393 | my @splittedLine;
394 | if($line =~ /^\/(c[0-9]*\/v[0-9]*).*/){
395 | $currBlock = $1;
396 | next;
397 | }
398 | if(defined($currBlock)){
399 | if($line =~ /^DG\/VD[[:blank:]]+TYPE.*/){
400 | @splittedLine = split(' ', $line);
401 | if(scalar(@splittedLine)== 9){
402 | $LDMAP = \@map_a;
403 | }
404 | if(scalar(@splittedLine)== 10){
405 | $LDMAP = \@map_cc_a;
406 | }
407 | }
408 | if($line =~ /^\d+\/\d+\s+\w+\d\s+\w+.*/){
409 | @splittedLine = map { s/^\s*//; s/\s*$//; $_; } split(/\s+/,$line);
410 | my %lineValues_h;
411 | # The current block is the c0/v0 name
412 | $lineValues_h{'ld'} = $currBlock;
413 | for(my $i = 0; $i < @{$LDMAP}; $i++){
414 | $lineValues_h{$LDMAP->[$i]} = $splittedLine[$i];
415 | }
416 | push @foundDevs, \%lineValues_h;
417 | }
418 | }
419 | }
420 | }
421 | elsif($action eq "init") {
422 | foreach my $line(@output){
423 | $line =~ s/^\s+|\s+$//g;#trim line
424 | if($line =~ /^([0-9]+)\s+INIT.*$/){
425 | my $vdNum = 'c'.$CONTROLLER.'/v'.$1;
426 | if($line !~ /Not in progress/i){
427 | my %lineValues_h;
428 | my @vals = split('\s+',$line);
429 | $lineValues_h{'ld'} = $vdNum;
430 | $lineValues_h{'init'} = $vals[2];
431 | push @foundDevs, \%lineValues_h;
432 | }
433 | }
434 | }
435 | }
436 | }
437 | else {
438 | print "Invalid StorCLI command! ($command)\n";
439 | exit(STATE_UNKNOWN);
440 | }
441 | return \@foundDevs;
442 | }
443 |
444 | # Checks the status of the logical devices.
445 | # @param statusLevel_a The status level array, elem 0 is the current status,
446 | # elem 1 the warning sensors, elem 2 the critical sensors, elem 3 the verbose
447 | # information for the sensors.
448 | # @param foundLDs The array of logical devices, created by getLogicalDevices
449 | sub getLDStatus{
450 | my @statusLevel_a = @{(shift)};
451 | my @foundLDs = @{(shift)};
452 | my $status = '';
453 | foreach my $LD (@foundLDs){
454 | if(exists($LD->{'State'})){
455 | if($LD->{'State'} ne 'Optl'){
456 | $status = 'Critical';
457 | push @{$statusLevel_a[2]}, $LD->{'ld'}.'_State';
458 | $statusLevel_a[3]->{$LD->{'ld'}.'_State'} = $LD->{'State'};
459 | }
460 | }
461 | if(!$NOWRITEBACKOK){
462 | if(exists($LD->{'Cache'})){
463 | if(index($LD->{'Cache'}, 'WB') == -1){
464 | $status = 'Critical';
465 | push @{$statusLevel_a[2]}, $LD->{'ld'}.'_Cache';
466 | $statusLevel_a[3]->{$LD->{'ld'}.'_Cache'} = $LD->{'Cache'};
467 | }
468 | }
469 | }
470 | if(!$NOCONSISTENCYOK){
471 | if(exists($LD->{'Consist'})){
472 | if($LD->{'Consist'} ne 'Yes' && $LD->{'TYPE'} ne 'Cac1'){
473 | $status = 'Warning' unless $status eq 'Critical';
474 | push @{$statusLevel_a[1]}, $LD->{'ld'}.'_Consist';
475 | $statusLevel_a[3]->{$LD->{'ld'}.'_Consist'} = $LD->{'Consist'};
476 | }
477 | }
478 | }
479 | if(exists($LD->{'init'})){
480 | $status = 'Warning' unless $status eq 'Critical';
481 | push @{$statusLevel_a[1]}, $LD->{'ld'}.'_Init';
482 | $statusLevel_a[3]->{$LD->{'ld'}.'_Init'} = $LD->{'init'};
483 | }
484 | }
485 | if($status ne ''){
486 | if($status eq 'Warning'){
487 | if(${$statusLevel_a[0]} ne 'Critical'){
488 | ${$statusLevel_a[0]} = 'Warning';
489 | }
490 | }
491 | else{
492 | ${$statusLevel_a[0]} = 'Critical';
493 | }
494 | $statusLevel_a[3]->{'LD_Status'} = $status;
495 | }
496 | else{
497 | if(!exists($statusLevel_a[3]->{'LD_Status'})){
498 | $statusLevel_a[3]->{'LD_Status'} = 'OK';
499 | }
500 | }
501 | }
502 |
503 | # Checks which physical devices are present for the given controller and parses
504 | # the physical devices to a list of hashes. Each hash represents a physical device
505 | # with its values from the output.
506 | # @param storcli The path to storcli command utility, followed by the controller
507 | # number, e.g. 'storcli64 /c0'.
508 | # @param physDevices If given, a list of desired physical device numbers
509 | # @param action The storcli action to check, 'all', 'initialization' or 'rebuild'
510 | # @param commands_a An array to push the used command to
511 | # @return A list of hashes, each hash is one physical device. Check pdmap_a for valid
512 | # hash keys.
513 | sub getPhysicalDevices{
514 | my $storcli = shift;
515 | my $writelogs = shift;
516 | my @enclosures = @{(shift)};
517 | my @physDevices = @{(shift)};
518 | my $action = shift;
519 | my $commands_a = shift;
520 |
521 | my $command = $storcli;
522 | if(!$NOENCLOSURES){
523 | if(scalar(@enclosures) == 0) { $command .= "/eall"; }
524 | elsif(scalar(@enclosures) == 1) { $command .= "/e$enclosures[0]"; }
525 | else { $command .= "/e".join(",", @enclosures); }
526 | }
527 | if(scalar(@physDevices) == 0) { $command .= "/sall"; }
528 | elsif(scalar(@physDevices) == 1) { $command .= "/s$physDevices[0]"; }
529 | else { $command .= "/s".join(",", @physDevices); }
530 | $command .= " show $action";
531 | if(!$writelogs) { $command .= ' nolog'; }
532 | push @{$commands_a}, $command;
533 |
534 | my @output = `$command`;
535 | my @foundDevs;
536 | if(checkCommandStatus(\@output)){
537 | if($action eq "all") {
538 | my $currBlock;
539 | my $line_ref;
540 | foreach my $line(@output){
541 | my @splittedLine;
542 | if($line =~ /^Drive \/(c[0-9]*\/e[0-9]*\/s[0-9]*) \:$/){
543 | $currBlock = $1;
544 | $line_ref = {};
545 | next;
546 | }
547 | if(defined($currBlock)){
548 | # If a drive is not in a group, a - is at the DG column
549 | if($line =~ /^\d+\:\d+\s+\d+\s+\w+\s+[0-9-F]+.*/){
550 | @splittedLine = map { s/^\s*//; s/\s*$//; $_; } split(/\s+/,$line);
551 | # The current block is the c0/e252/s0 name
552 | $line_ref->{'pd'} = $currBlock;
553 | my $j = 0;
554 | for(my $i = 0; $i < @pdmap_a; $i++){
555 | if($pdmap_a[$i] eq 'Size'){
556 | my $size = $splittedLine[$j];
557 | if($splittedLine[$j+1] eq 'GB' || $splittedLine[$j+1] eq 'TB'){
558 | $size .= ''.$splittedLine[$j+1];
559 | $j++;
560 | }
561 | $line_ref->{$pdmap_a[$i]} = $size;
562 | $j++;
563 | }
564 | elsif($pdmap_a[$i] eq 'Model'){
565 | my $model = $splittedLine[$j];
566 | # Model should be the next last element, j starts at 0
567 | if(($j+2) != scalar(@splittedLine)){
568 | $model .= ' '.$splittedLine[$j+1];
569 | $j++;
570 | }
571 | $line_ref->{$pdmap_a[$i]} = $model;
572 | $j++;
573 | }
574 | else{
575 | $line_ref->{$pdmap_a[$i]} = $splittedLine[$j];
576 | $j++;
577 | }
578 | }
579 | }
580 | if($line =~ /^(Shield Counter|Media Error Count|Other Error Count|BBM Error Count|Drive Temperature|Predictive Failure Count|S\.M\.A\.R\.T alert flagged by drive)\s\=\s+(.*)$/){
581 | $line_ref->{$1} = $2;
582 | }
583 | # If the last value is parsed, set up for the next device
584 | if(exists($line_ref->{'S.M.A.R.T alert flagged by drive'})){
585 | push @foundDevs, $line_ref;
586 | undef $currBlock;
587 | undef $line_ref;
588 | }
589 | }
590 | }
591 | }
592 | elsif($action eq 'rebuild' || $action eq 'initialization') {
593 | foreach my $line(@output){
594 | $line =~ s/^\s+|\s+$//g;#trim line
595 | if($line =~ /^\/c$CONTROLLER\/.*/){
596 | if($line !~ /Not in progress/i){
597 | my %lineValues_h;
598 | my @vals = split('\s+',$line);
599 | my $key;
600 | if($action eq 'rebuild'){ $key = 'rebuild'; }
601 | if($action eq 'initialization'){ $key = 'init'; }
602 | $lineValues_h{'pd'} = substr($vals[0], 1);
603 | $lineValues_h{$key} = $vals[1];
604 | push @foundDevs, \%lineValues_h;
605 | }
606 | }
607 | }
608 | }
609 | # Now we check if a drive is not attached, error code 46
610 | my $failed_pattern = 'c[0-9]*\/e[0-9]*\/s[0-9]*\s+Failure\s+46';
611 | if(my @match = grep { /$failed_pattern/ } @output){
612 | $match[0] =~ /(c[0-9]*\/e[0-9]*\/s[0-9]*)/;
613 | my $dev = {};
614 | $dev->{'pd'} = $1;
615 | $dev->{'Detailed Status'} = 'Failure-46';
616 | push @foundDevs, $dev;
617 | }
618 | }
619 | else {
620 | if(grep { /No drive found/i } @output){
621 | print "Warning (CTR Warn) [No storage attached] ($command)\n";
622 | exit(STATE_WARNING);
623 | }
624 | print "Invalid StorCLI command! ($command)\n";
625 | exit(STATE_UNKNOWN);
626 | }
627 | return \@foundDevs;
628 | }
629 |
630 | # Checks the status of the physical devices.
631 | # @param statusLevel_a The status level array, elem 0 is the current status,
632 | # elem 1 the warning sensors, elem 2 the critical sensors, elem 3 the vebose
633 | # information for the sensors.
634 | # @param foundPDs The array of physical devices, created by getPhysicalDevices
635 | sub getPDStatus{
636 | my @statusLevel_a = @{(shift)};
637 | my @foundPDs = @{(shift)};
638 | my $status = '';
639 | foreach my $PD (@foundPDs){
640 | if(exists($PD->{'State'})){
641 | if($PD->{'State'} ne 'Onln' && $PD->{'State'} ne 'UGood' && $PD->{'State'} ne 'GHS' && $PD->{'State'} ne 'DHS' && $PD->{'State'} ne 'JBOD'){
642 | $status = 'Critical';
643 | push @{$statusLevel_a[2]}, $PD->{'pd'}.'_State';
644 | $statusLevel_a[3]->{$PD->{'pd'}.'_State'} = $PD->{'State'};
645 | }
646 | }
647 | if(exists($PD->{'Shield Counter'})){
648 | if($PD->{'Shield Counter'} > $IGNERR_S){
649 | $status = 'Warning' unless $status eq 'Critical';
650 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_Shield_counter';
651 | $statusLevel_a[3]->{$PD->{'pd'}.'_Shield_counter'} = $PD->{'Shield Counter'};
652 | }
653 | }
654 | if(exists($PD->{'Media Error Count'})){
655 | if($PD->{'Media Error Count'} > $IGNERR_M){
656 | $status = 'Warning' unless $status eq 'Critical';
657 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_Media_error_count';
658 | $statusLevel_a[3]->{$PD->{'pd'}.'_Media_error_count'} = $PD->{'Media Error Count'};
659 | }
660 | }
661 | if(exists($PD->{'Other Error Count'})){
662 | if(($IGNERR_O != -1) && ($PD->{'Other Error Count'} > $IGNERR_O)){
663 | $status = 'Warning' unless $status eq 'Critical';
664 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_Other_error_count';
665 | $statusLevel_a[3]->{$PD->{'pd'}.'_Other_error_count'} = $PD->{'Other Error Count'};
666 | }
667 | }
668 | if(exists($PD->{'BBM Error Count'})){
669 | if($PD->{'BBM Error Count'} > $IGNERR_B){
670 | $status = 'Warning' unless $status eq 'Critical';
671 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_BBM_error_count';
672 | $statusLevel_a[3]->{$PD->{'pd'}.'_BBM_error_count'} = $PD->{'BBM Error Count'};
673 | }
674 | }
675 | if(exists($PD->{'Predictive Failure Count'})){
676 | if($PD->{'Predictive Failure Count'} > $IGNERR_P){
677 | $status = 'Warning' unless $status eq 'Critical';
678 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_Predictive_failure_count';
679 | $statusLevel_a[3]->{$PD->{'pd'}.'_Predictive_failure_count'} = $PD->{'Predictive Failure Count'};
680 | }
681 | }
682 | if(exists($PD->{'S.M.A.R.T alert flagged by drive'})){
683 | if($PD->{'S.M.A.R.T alert flagged by drive'} ne 'No'){
684 | $status = 'Warning' unless $status eq 'Critical';
685 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_SMART_flag';
686 | }
687 | }
688 | if(exists($PD->{'Detailed Status'})){
689 | if($PD->{'Detailed Status'} eq 'Failure-46'){
690 | $status = 'Warning' unless $status eq 'Critical';
691 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_Detailed_status';
692 | }
693 | }
694 | if(exists($PD->{'DG'})){
695 | if($PD->{'DG'} eq 'F'){
696 | $status = 'Warning' unless $status eq 'Critical';
697 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_DG';
698 | $statusLevel_a[3]->{$PD->{'pd'}.'_DG'} = $PD->{'DG'};
699 | }
700 | }
701 | if(exists($PD->{'Drive Temperature'})){
702 | my $temp = $PD->{'Drive Temperature'};
703 | if($temp ne 'N/A' && $temp ne '0C (32.00 F)'){
704 | $temp =~ /^([0-9]+)C/;
705 | if(!(checkThreshs($1, $PD_TEMP_CRITICAL))){
706 | $status = 'Critical';
707 | push @{$statusLevel_a[2]}, $PD->{'pd'}.'_Drive_Temperature';
708 | }
709 | elsif(!(checkThreshs($1, $PD_TEMP_WARNING))){
710 | $status = 'Warning' unless $status eq 'Critical';
711 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_Drive_Temperature';
712 | }
713 | $statusLevel_a[3]->{$PD->{'pd'}.'_Drive_Temperature'} = $1;
714 | }
715 | }
716 | if(exists($PD->{'init'})){
717 | $status = 'Warning' unless $status eq 'Critical';
718 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_Init';
719 | $statusLevel_a[3]->{$PD->{'pd'}.'_Init'} = $PD->{'init'};
720 | }
721 | if(exists($PD->{'rebuild'})){
722 | $status = 'Warning' unless $status eq 'Critical';
723 | push @{$statusLevel_a[1]}, $PD->{'pd'}.'_Rebuild';
724 | $statusLevel_a[3]->{$PD->{'pd'}.'_Rebuild'} = $PD->{'rebuild'};
725 | }
726 | }
727 | if($status ne ''){
728 | if($status eq 'Warning'){
729 | if(${$statusLevel_a[0]} ne 'Critical'){
730 | ${$statusLevel_a[0]} = 'Warning';
731 | }
732 | }
733 | else{
734 | ${$statusLevel_a[0]} = 'Critical';
735 | }
736 | $statusLevel_a[3]->{'PD_Status'} = $status;
737 | }
738 | else{
739 | if(!exists($statusLevel_a[3]->{'PD_Status'})){
740 | $statusLevel_a[3]->{'PD_Status'} = 'OK';
741 | }
742 | }
743 | }
744 |
745 | # Checks the status of the BBU, parses 'bbu show status' for the given controller.
746 | # @param storcli The path to storcli command utility, followed by the controller
747 | # number, e.g. 'storcli64 /c0'.
748 | # @param statusLevel_a The status level array, elem 0 is the current status,
749 | # elem 1 the warning sensors, elem 2 the critical sensors, elem 3 the verbose
750 | # information for the sensors.
751 | # @param commands_a An array to push the used command to
752 | sub getBBUStatus {
753 | my $storcli = shift;
754 | my $writelogs = shift;
755 | my @statusLevel_a = @{(shift)};
756 | my $commands_a = shift;
757 |
758 | my $command = "$storcli /bbu show status";
759 | if(!$writelogs) { $command .= ' nolog'; }
760 | push @{$commands_a}, $command;
761 |
762 | my $status = '';
763 | my $learn_cycle_active = 0;
764 | my @output = `$command`;
765 | if(checkCommandStatus(\@output)) {
766 | my $currBlock;
767 | foreach my $line (@output) {
768 | if($line =~ /^(BBU_Firmware_Status)/){
769 | $currBlock = $1;
770 | next;
771 | }
772 | if(defined($currBlock and $currBlock eq 'BBU_Firmware_Status')){
773 | $line =~ s/^\s+|\s+$//g;#trim line
774 | if ($line =~ /^Learn Cycle Active/){
775 | $line =~ /([a-zA-Z\/]*)$/;
776 | if($1 eq "Yes") {
777 | $learn_cycle_active = 1;
778 | }
779 | }
780 | }
781 | }
782 | undef $currBlock;
783 | foreach my $line (@output) {
784 | if($line =~ /^(BBU_Info|BBU_Firmware_Status|GasGaugeStatus)/){
785 | $currBlock = $1;
786 | next;
787 | }
788 | if(defined($currBlock)){
789 | $line =~ s/^\s+|\s+$//g;#trim line
790 | if($currBlock eq 'BBU_Info'){
791 | if ($line =~ /^Battery State/){
792 | $line =~ /([a-zA-Z]*)$/;
793 | if(!$learn_cycle_active && $1 ne 'Optimal'){
794 | $status = 'Warning' unless $status eq 'Critical';
795 | push @{$statusLevel_a[1]}, 'BBU_State';
796 | $statusLevel_a[3]->{'BBU_State'} = $1
797 | }
798 | }
799 | elsif($line =~ /^Temperature/){
800 | $line =~ /([0-9]+) C$/;
801 | if(!(checkThreshs($1, $BBU_TEMP_CRITICAL))){
802 | $status = 'Critical';
803 | push @{$statusLevel_a[2]}, 'BBU_Temperature';
804 | }
805 | elsif(!(checkThreshs($1, $BBU_TEMP_WARNING))){
806 | $status = 'Warning' unless $status eq 'Critical';
807 | push @{$statusLevel_a[1]}, 'BBU_Temperature';
808 | }
809 | $statusLevel_a[3]->{'BBU_Temperature'} = $1;
810 | }
811 | }
812 | elsif($currBlock eq 'BBU_Firmware_Status'){
813 | if($line =~ /^Temperature/){
814 | $line =~ /([a-zA-Z]*)$/;
815 | if($1 ne "OK") {
816 | $status = 'Critical';
817 | push @{$statusLevel_a[2]},'BBU_Firmware_temperature';
818 | $statusLevel_a[3]->{'BBU_Firmware_temperature'} = $1;
819 | }
820 | }
821 | elsif($line =~ /^Voltage/){
822 | $line =~ /([a-zA-Z]*)$/;
823 | if($1 ne "OK") {
824 | $status = 'Warning' unless $status eq 'Critical';
825 | push @{$statusLevel_a[1]},'BBU_Voltage';
826 | $statusLevel_a[3]->{'BBU_Voltage'} = $1;
827 | }
828 | }
829 | elsif($line =~ /^I2C Errors Detected/){
830 | $line =~ /([a-zA-Z]*)$/;
831 | if($1 ne "No") {
832 | $status = 'Critical';
833 | push @{$statusLevel_a[2]},'BBU_Firmware_I2C_errors';
834 | $statusLevel_a[3]->{'BBU_Firmware_I2C_Errors'} = $1;
835 | }
836 | }
837 | elsif($line =~ /^Battery Pack Missing/){
838 | $line =~ /([a-zA-Z]*)$/;
839 | if($1 ne "No") {
840 | $status = 'Critical';
841 | push @{$statusLevel_a[2]},'BBU_Pack_missing';
842 | $statusLevel_a[3]->{'BBU_Pack_missing'} = $1;
843 | }
844 | }
845 | elsif($line =~ /^Replacement required/){
846 | $line =~ /([a-zA-Z]*)$/;
847 | if($1 ne "No") {
848 | $status = 'Critical';
849 | push @{$statusLevel_a[2]},'BBU_Replacement_required';
850 | $statusLevel_a[3]->{'BBU_Replacement_required'} = $1;
851 | }
852 | }
853 | elsif($line =~ /^Remaining Capacity Low/){
854 | $line =~ /([a-zA-Z]*)$/;
855 | if(!$learn_cycle_active && $1 ne "No") {
856 | $status = 'Warning' unless $status eq 'Critical';
857 | push @{$statusLevel_a[1]},'BBU_Remaining_capacity_low';
858 | $statusLevel_a[3]->{'BBU_Remaining_capacity_low'} = $1;
859 | }
860 | }
861 | elsif($line =~ /^Pack is about to fail \& should be replaced/){
862 | $line =~ /([a-zA-Z]*)$/;
863 | if($1 ne "No") {
864 | $status = 'Critical';
865 | push @{$statusLevel_a[2]},'BBU_Should_be_replaced';
866 | $statusLevel_a[3]->{'BBU_Should_be_replaced'} = $1;
867 | }
868 | }
869 | }
870 | elsif($currBlock eq 'GasGaugeStatus'){
871 | if($line =~ /^Fully Discharged/){
872 | $line =~ /([a-zA-Z\/]*)$/;
873 | if(!$learn_cycle_active && $1 ne "No" && $1 ne "N/A") {
874 | $status = 'Critical';
875 | push @{$statusLevel_a[2]},'BBU_GasGauge_discharged';
876 | $statusLevel_a[3]->{'BBU_GasGauge_discharged'} = $1;
877 | }
878 | }
879 | elsif($line =~ /^Over Temperature/){
880 | $line =~ /([a-zA-Z\/]*)$/;
881 | if($1 ne "No" && $1 ne "N/A") {
882 | $status = 'Warning' unless $status eq 'Critical';
883 | push @{$statusLevel_a[1]},'BBU_GasGauge_over_temperature';
884 | $statusLevel_a[3]->{'BBU_GasGauge_over_temperature'} = $1;
885 | }
886 | }
887 | elsif($line =~ /^Over Charged/){
888 | $line =~ /([a-zA-Z\/]*)$/;
889 | if($1 ne "No" && $1 ne "N/A") {
890 | $status = 'Critical';
891 | push @{$statusLevel_a[2]},'BBU_GasGauge_over_charged';
892 | $statusLevel_a[3]->{'BBU_GasGauge_over_charged'} = $1;
893 | }
894 | }
895 | }
896 | }
897 | if($status ne ''){
898 | if($status eq 'Warning'){
899 | if(${$statusLevel_a[0]} ne 'Critical'){
900 | ${$statusLevel_a[0]} = 'Warning';
901 | }
902 | }
903 | else{
904 | ${$statusLevel_a[0]} = 'Critical';
905 | }
906 | $statusLevel_a[3]->{'BBU_Status'} = $status;
907 | }
908 | else{
909 | $statusLevel_a[3]->{'BBU_Status'} = 'OK';
910 | }
911 | }
912 | }
913 | else {
914 | print "Invalid StorCLI command! ($command)\n";
915 | exit(STATE_UNKNOWN);
916 | }
917 | }
918 |
919 | # Checks the status of the CV module, parses 'cv show status' for the given
920 | # controller.
921 | # @param storcli The path to storcli command utility, followed by the controller
922 | # number, e.g. 'storcli64 /c0'.
923 | # @param statusLevel_a The status level array, elem 0 is the current status,
924 | # elem 1 the warning sensors, elem 2 the critical sensors, elem 3 the verbose
925 | # information for the sensors.
926 | # @param commands_a An array to push the used command to
927 | sub getCVStatus {
928 | my $storcli = shift;
929 | my $writelogs = shift;
930 | my @statusLevel_a = @{(shift)};
931 | my $commands_a = shift;
932 |
933 | my $command = $storcli." /cv show status";
934 | if(!$writelogs) { $command .= ' nolog'; }
935 | push @{$commands_a}, $command;
936 |
937 | my $status = '';
938 | my @output = `$command`;
939 | if(checkCommandStatus(\@output)) {
940 | my $currBlock;
941 | foreach my $line (@output) {
942 | if($line =~ /^(Cachevault_Info|Firmware_Status)/){
943 | $currBlock = $1;
944 | next;
945 | }
946 | if(defined($currBlock)){
947 | $line =~ s/^\s+|\s+$//g;#trim line
948 | if($currBlock eq 'Cachevault_Info' && $line =~ /^State/){
949 | my @vals = split('\s{2,}',$line);
950 | if($vals[1] ne "Optimal") {
951 | $status = 'Warning' unless $status eq 'Critical';
952 | push @{$statusLevel_a[1]}, 'CV_State';
953 | $statusLevel_a[3]->{'CV_State'} = $vals[1]
954 | }
955 | }
956 | elsif($currBlock eq 'Cachevault_Info' && $line =~ /^Temperature/){
957 | $line =~ /([0-9]+) C$/;
958 | if(!(checkThreshs($1, $CV_TEMP_CRITICAL))){
959 | $status = 'Critical';
960 | push @{$statusLevel_a[2]}, 'CV_Temperature';
961 | }
962 | elsif(!(checkThreshs($1, $CV_TEMP_WARNING))){
963 | $status = 'Warning' unless $status eq 'Critical';
964 | push @{$statusLevel_a[1]}, 'CV_Temperature';
965 | }
966 | $statusLevel_a[3]->{'CV_Temperature'} = $1;
967 | }
968 | elsif($currBlock eq 'Firmware_Status' && $line =~ /^Replacement required/){
969 | $line =~ /([a-zA-Z0-9]*)$/;
970 | if($1 ne "No") {
971 | $status = 'Critical';
972 | push @{$statusLevel_a[2]},'CV_Replacement_required';
973 | }
974 | $statusLevel_a[3]->{'CV_Replacement_required'} = $1;
975 | }
976 | }
977 | if($status ne ''){
978 | if($status eq 'Warning'){
979 | if(${$statusLevel_a[0]} ne 'Critical'){
980 | ${$statusLevel_a[0]} = 'Warning';
981 | }
982 | }
983 | else{
984 | ${$statusLevel_a[0]} = 'Critical';
985 | }
986 | $statusLevel_a[3]->{'CV_Status'} = $status;
987 | }
988 | else{
989 | $statusLevel_a[3]->{'CV_Status'} = 'OK';
990 | }
991 | }
992 | }
993 | else {
994 | print "Invalid StorCLI command! ($command)\n";
995 | exit(STATE_UNKNOWN);
996 | }
997 | }
998 |
999 | # Checks if wheter BBU or CV is present
1000 | # @param storcli The path to storcli command utility, followed by the controller
1001 | # number, e.g. 'storcli64 /c0'.
1002 | # @return A tuple, e.g. (0,0), where 0 means module is not present, 1 present
1003 | sub checkBBUorCVIsPresent{
1004 | my $storcli = shift;
1005 | my $writelogs = shift;
1006 | my ($bbu,$cv);
1007 | my $command = $storcli." /bbu show";
1008 | if(!$writelogs) { $command .= ' nolog'; }
1009 | my @output = `$command`;
1010 | if(checkCommandStatus(\@output)){ $bbu = 1; }
1011 | else{ $bbu = 0 };
1012 | $command = $storcli." /cv show";
1013 | if(!$writelogs) { $command .= ' nolog'; }
1014 | @output = `$command`;
1015 | if(checkCommandStatus(\@output)) { $cv = 1; }
1016 | else{ $cv = 0 };
1017 | return ($bbu, $cv);
1018 | }
1019 |
1020 | # Checks if a given value is in a specified range, the range must follow the
1021 | # nagios development guidelines:
1022 | # http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT
1023 | # @param value The given value to check the pattern for
1024 | # @param pattern The pattern specifying the threshold range, e.g. '10:', '@10:20'
1025 | # @return 0 if the value is outside the range, 1 if the value satisfies the range
1026 | sub checkThreshs{
1027 | my $value = shift;
1028 | my $pattern = shift;
1029 | if($pattern =~ /(^[0-9]+$)/){
1030 | if($value < 0 || $value > $1){
1031 | return 0;
1032 | }
1033 | }
1034 | elsif($pattern =~ /(^[0-9]+)\:$/){
1035 | if($value < $1){
1036 | return 0;
1037 | }
1038 | }
1039 | elsif($pattern =~ /^\~\:([0-9]+)$/){
1040 | if($value > $1){
1041 | return 0;
1042 | }
1043 | }
1044 | elsif($pattern =~ /^([0-9]+)\:([0-9]+)$/){
1045 | if($value < $1 || $value > $2){
1046 | return 0;
1047 | }
1048 | }
1049 | elsif($pattern =~ /^\@([0-9]+)\:([0-9]+)$/){
1050 | if($value >= $1 and $value <= $2){
1051 | return 0;
1052 | }
1053 | }
1054 | else{
1055 | print "Invalid temperature parameter! ($pattern)\n";
1056 | exit(STATE_UNKNOWN);
1057 | }
1058 | return 1;
1059 | }
1060 |
1061 | # Get the status string as plugin output
1062 | # @param level The desired level to get the status string for. Either 'Warning'
1063 | # or 'Critical'.
1064 | # @param statusLevel_a The status level array, elem 0 is the current status,
1065 | # elem 1 the warning sensors, elem 2 the critical sensors, elem 3 the verbose
1066 | # information for the sensors, elem 4 the used storcli commands.
1067 | # @return The created status string
1068 | sub getStatusString{
1069 | my $level = shift;
1070 | my @statusLevel_a = @{(shift)};
1071 | my @sensors_a;
1072 | my $status_str = "";
1073 | if($level eq "Warning"){
1074 | @sensors_a = @{$statusLevel_a[1]};
1075 | }
1076 | if($level eq "Critical"){
1077 | @sensors_a = @{$statusLevel_a[2]};
1078 | }
1079 | # Add the controller parts only once
1080 | my $parts = '';
1081 | # level comes from the method call, not the real status level
1082 | if($level eq "Critical"){
1083 | my @keys = ('CTR_Status','LD_Status','PD_Status','BBU_Status','CV_Status');
1084 | # Check which parts where checked
1085 | foreach my $key (@keys){
1086 | $key =~ /^([A-Z]+)\_.*$/;
1087 | my $part = $1;
1088 | if(${$statusLevel_a[0]} eq 'OK'){
1089 | if(exists($statusLevel_a[3]->{$key}) && $statusLevel_a[3]->{$key} eq 'OK'){
1090 | $parts .= ", " unless $parts eq '';
1091 | $parts .= $part;
1092 | }
1093 | }
1094 | else{
1095 | if(exists($statusLevel_a[3]->{$key}) && $statusLevel_a[3]->{$key} ne 'OK'){
1096 | $parts .= ", " unless $parts eq '';
1097 | $parts .= $part;
1098 | $parts .= ' '.substr($statusLevel_a[3]->{$key}, 0, 4);
1099 | }
1100 | }
1101 | }
1102 | $status_str.= '(';
1103 | $status_str .= $parts unless !defined($parts);
1104 | $status_str.= ')';
1105 | }
1106 | if($level eq 'Critical'){
1107 | $status_str.= ' ' unless !(@sensors_a);
1108 | }
1109 | if($level eq 'Warning' && !@{$statusLevel_a[2]}){
1110 | $status_str.= ' ' unless !(@sensors_a);
1111 | }
1112 | if($level eq "Warning" || $level eq "Critical"){
1113 | if(@sensors_a){
1114 | # Print which sensors are Warn or Crit
1115 | foreach my $sensor (@sensors_a){
1116 | $status_str .= "[".$sensor." = ".$level;
1117 | if($VERBOSITY){
1118 | if(exists($statusLevel_a[3]->{$sensor})){
1119 | $status_str .= " (".$statusLevel_a[3]->{$sensor}.")";
1120 | }
1121 | }
1122 | $status_str .= "]";
1123 | }
1124 | }
1125 | }
1126 | return $status_str;
1127 | }
1128 |
1129 | # Get the verbose string if a higher verbose level is used
1130 | # @param statusLevel_a The status level array, elem 0 is the current status,
1131 | # elem 1 the warning sensors, elem 2 the critical sensors, elem 3 the verbose
1132 | # information for the sensors, elem 4 the used storcli commands.
1133 | # @param controllerToCheck Controller parsed by getControllerInfo
1134 | # @param LDDevicesToCheck LDs parsed by getLogicalDevices
1135 | # @param LDInitToCheck LDs parsed by getLogicalDevices init
1136 | # @param PDDevicesToCheck PDs parsed by getPhysicalDevices
1137 | # @param PDInitToCheck PDs parsed by getPhysicalDevices init
1138 | # @param PDRebuildToCheck PDs parsed by getPhysicalDevices rebuild
1139 | # @return The created verbosity string
1140 | sub getVerboseString{
1141 | my @statusLevel_a = @{(shift)};
1142 | my %controllerToCheck = %{(shift)};
1143 | my @LDDevicesToCheck = @{(shift)};
1144 | my @LDInitToCheck = @{(shift)};
1145 | my @PDDevicesToCheck = @{(shift)};
1146 | my @PDInitToCheck = @{(shift)};
1147 | my @PDRebuildToCheck = @{(shift)};
1148 | my @sensors_a;
1149 | my $verb_str;
1150 |
1151 | $verb_str .= "Used storcli commands:\n";
1152 | foreach my $cmd (@{$statusLevel_a[4]}){
1153 | $verb_str .= '- '.$cmd."\n";
1154 | }
1155 | if(${$statusLevel_a[0]} eq 'Critical'){
1156 | $verb_str .= "Critical sensors:\n";
1157 | foreach my $sensor (@{$statusLevel_a[2]}){
1158 | $verb_str .= "\t- ".$sensor;
1159 | if(exists($statusLevel_a[3]->{$sensor})){
1160 | $verb_str .= ' ('.$statusLevel_a[3]->{$sensor}.')';
1161 | }
1162 | $verb_str .= "\n";
1163 | }
1164 |
1165 | }
1166 | if( ${$statusLevel_a[0]} ne 'OK'){
1167 | $verb_str .= "Warning sensors:\n";
1168 | foreach my $sensor (@{$statusLevel_a[1]}){
1169 | $verb_str .= "\t- ".$sensor;
1170 | if(exists($statusLevel_a[3]->{$sensor})){
1171 | $verb_str .= ' ('.$statusLevel_a[3]->{$sensor}.')';
1172 | }
1173 | $verb_str .= "\n";
1174 | }
1175 |
1176 | }
1177 | if($VERBOSITY == 3){
1178 | $verb_str .= "CTR information:\n";
1179 | $verb_str .= "\t- ".$controllerToCheck{'Model'}.":\n";
1180 | $verb_str .= "\t\t- ".'Serial Number='.$controllerToCheck{'Serial Number'}."\n";
1181 | $verb_str .= "\t\t- ".'Firmware Package Build='.$controllerToCheck{'Firmware Package Build'}."\n";
1182 | $verb_str .= "\t\t- ".'Mfg Date='.$controllerToCheck{'Mfg Date'}."\n";
1183 | $verb_str .= "\t\t- ".'Revision No='.$controllerToCheck{'Revision No'}."\n";
1184 | $verb_str .= "\t\t- ".'Bios Version='.$controllerToCheck{'Bios Version'}."\n";
1185 | $verb_str .= "\t\t- ".'Firmware Version='.$controllerToCheck{'Firmware Version'}."\n";
1186 | if(exists($controllerToCheck{'ROC temperature'})){
1187 | $verb_str .= "\t\t- ".'ROC temperature='.$controllerToCheck{'ROC temperature'}."\n";
1188 | }
1189 | $verb_str .= "LD information:\n";
1190 | foreach my $LD (@LDDevicesToCheck){
1191 | $verb_str .= "\t- ".$LD->{'ld'}.":\n";
1192 | foreach my $key (sort (keys(%{$LD}))){
1193 | $verb_str .= "\t\t- ".$key.'='.$LD->{$key}."\n";
1194 | }
1195 | foreach my $LDinit (@LDInitToCheck){
1196 | if($LDinit->{'ld'} eq $LD->{'ld'}){
1197 | $verb_str .= "\t\t- init=".$LDinit->{'init'}."\n";
1198 | }
1199 | }
1200 | }
1201 | $verb_str .= "PD information:\n";
1202 | foreach my $PD (@PDDevicesToCheck){
1203 | $verb_str .= "\t- ".$PD->{'pd'}.":\n";
1204 | foreach my $key (sort (keys(%{$PD}))){
1205 | $verb_str .= "\t\t- ".$key.'='.$PD->{$key}."\n";
1206 | }
1207 | foreach my $PDinit (@PDInitToCheck){
1208 | if($PDinit->{'pd'} eq $PD->{'pd'}){
1209 | $verb_str .= "\t\t- init=".$PDinit->{'init'}."\n";
1210 | }
1211 | }
1212 | foreach my $PDrebuild (@PDRebuildToCheck){
1213 | if($PDrebuild->{'pd'} eq $PD->{'pd'}){
1214 | $verb_str .= "\t\t- rebuild=".$PDrebuild->{'rebuild'}."\n";
1215 | }
1216 | }
1217 | }
1218 | my @keys = ('BBU_Status','CV_Status');
1219 | foreach my $key(@keys){
1220 | if(exists($statusLevel_a[3]->{$key})){
1221 | $key =~ /^(\w+)_\w+$/;
1222 | my $type = $1;
1223 | $verb_str .= $type." information:\n";
1224 | foreach my $stat (sort (keys(%{$statusLevel_a[3]}))){
1225 | if($stat =~ /^$type.+$/){
1226 | $verb_str .= "\t\t- $stat=".$statusLevel_a[3]->{$stat}."\n";
1227 | }
1228 | }
1229 | }
1230 | }
1231 | }
1232 | return $verb_str;
1233 | }
1234 |
1235 | # Get the performance string for the current check. The values are taken from
1236 | # the verbose hash in the status level array.
1237 | # @param statusLevel_a The current status level array
1238 | # @return The created performance string
1239 | sub getPerfString{
1240 | my @statusLevel_a = @{(shift)};
1241 | my %verboseValues_h = %{$statusLevel_a[3]};
1242 | my $perf_str;
1243 | foreach my $key (sort (keys(%verboseValues_h))){
1244 | if($key =~ /temperature/i){
1245 | $perf_str .= ' ' unless !defined($perf_str);
1246 | $perf_str .= $key.'='.$verboseValues_h{$key};
1247 | }
1248 | if($key =~ /ROC_Temperature$/){
1249 | $perf_str .= ';'.$C_TEMP_WARNING.';'.$C_TEMP_CRITICAL;
1250 | }
1251 | elsif($key =~ /Drive_Temperature$/){
1252 | $perf_str .= ';'.$PD_TEMP_WARNING.';'.$PD_TEMP_CRITICAL;
1253 | }
1254 | elsif($key eq 'BBU_Temperature'){
1255 | $perf_str .= ';'.$BBU_TEMP_WARNING.';'.$BBU_TEMP_CRITICAL;
1256 | }
1257 | elsif($key eq 'CV_Temperature'){
1258 | $perf_str .= ';'.$CV_TEMP_WARNING.';'.$CV_TEMP_CRITICAL;
1259 | }
1260 | }
1261 | return $perf_str;
1262 | }
1263 |
1264 | MAIN: {
1265 | my ($storcli, $sudo, $noSudo, $noCleanlogs, $version, $exitCode);
1266 | # Create default sensor arrays and push them to status level
1267 | my @statusLevel_a ;
1268 | my $status_str = 'OK';
1269 | my $warnings_a = [];
1270 | my $criticals_a = [];
1271 | my $verboseValues_h = {};
1272 | my $verboseCommands_a = [];
1273 | push @statusLevel_a, \$status_str;
1274 | push @statusLevel_a, $warnings_a;
1275 | push @statusLevel_a, $criticals_a;
1276 | push @statusLevel_a, $verboseValues_h;
1277 | push @statusLevel_a, $verboseCommands_a;
1278 | # Per default use a BBU
1279 | my $bbu = 1;
1280 | my @enclosures;
1281 | my @logDevices;
1282 | my @physDevices;
1283 | my $platform = $^O;
1284 |
1285 | if( !(GetOptions(
1286 | 'h|help' => sub {displayHelp();},
1287 | 'v|verbose' => sub {$VERBOSITY = 1 },
1288 | 'vv' => sub {$VERBOSITY = 2},
1289 | 'vvv' => sub {$VERBOSITY = 3},
1290 | 'V|version' => \$version,
1291 | 'C|controller=i' => \$CONTROLLER,
1292 | 'EID|enclosure=s' => \@enclosures,
1293 | 'LD|logicaldevice=s' => \@logDevices,
1294 | 'PD|physicaldevice=s' => \@physDevices,
1295 | 'Tw|temperature-warn=s' => \$C_TEMP_WARNING,
1296 | 'Tc|temperature-critical=s' => \$C_TEMP_CRITICAL,
1297 | 'Mcw|mem-correctable-warn=s' => \$C_MEM_CORRECTABLE_WARNING,
1298 | 'Mcc|mem-correctable-critical=s' => \$C_MEM_CORRECTABLE_CRITICAL,
1299 | 'PDTw|physicaldevicetemperature-warn=s' => \$PD_TEMP_WARNING,
1300 | 'PDTc|physicaldevicetemperature-critical=s' => \$PD_TEMP_CRITICAL,
1301 | 'BBUTw|bbutemperature-warning=s' => \$BBU_TEMP_WARNING,
1302 | 'BBUTc|bbutemperature-critical=s' => \$BBU_TEMP_CRITICAL,
1303 | 'CVTw|cvtemperature-warning=s' => \$CV_TEMP_WARNING,
1304 | 'CVTc|cvtemperature-critical=s' => \$CV_TEMP_CRITICAL,
1305 | 'Im|ignore-media-errors=i' => \$IGNERR_M,
1306 | 'Io|ignore-other-errors=i' => \$IGNERR_O,
1307 | 'Ip|ignore-predictive-fail-count=i' => \$IGNERR_P,
1308 | 'Is|ignore-shield-counter=i' => \$IGNERR_S,
1309 | 'Ib|ignore-bbm-counter=i' => \$IGNERR_B,
1310 | 'p|path=s' => \$storcli,
1311 | 'b|BBU=i' => \$bbu,
1312 | 'noenclosures=i' => \$NOENCLOSURES,
1313 | 'nowritebackok=i' => \$NOWRITEBACKOK,
1314 | 'noconsistencyok=i' => \$NOCONSISTENCYOK,
1315 | 'nosudo' => \$noSudo,
1316 | 'nocleanlogs' => \$noCleanlogs
1317 | ))){
1318 | print $NAME . " Version: " . $VERSION ."\n";
1319 | displayUsage();
1320 | exit(STATE_UNKNOWN);
1321 | }
1322 | if(defined($version)){ print $NAME . "\nVersion: ". $VERSION . "\n"; }
1323 | # Check storcli tool
1324 | if(!defined($storcli)){
1325 | if($platform eq 'linux' || $platform eq 'solaris'){
1326 | $storcli = which('storcli');
1327 | if(!defined($storcli)){
1328 | $storcli = which('storcli64');
1329 | }
1330 | if(!defined($storcli)){
1331 | if(-x '/opt/MegaRAID/storcli/storcli64'){
1332 | $storcli = '/opt/MegaRAID/storcli/storcli64';
1333 | }
1334 | }
1335 | if(!defined($storcli)){
1336 | if(-x '/opt/MegaRAID/storcli/storcli'){
1337 | $storcli = '/opt/MegaRAID/storcli/storcli';
1338 | }
1339 | }
1340 | }
1341 | else{
1342 | $storcli = which('storcli.exe');
1343 | if(!defined($storcli)){
1344 | $storcli = which('storcli64.exe');
1345 | }
1346 | }
1347 | }
1348 | if(!defined($storcli)){
1349 | print "Error: cannot find storcli executable.\n";
1350 | print "Ensure storcli is in your path, or use the '-p ' switch!\n";
1351 | exit(STATE_UNKNOWN);
1352 | }
1353 | if($platform eq 'linux' || $platform eq 'solaris') {
1354 | if(!defined($noSudo)){
1355 | my $sudo;
1356 | chomp($sudo = `which sudo`);
1357 | if(!defined($sudo)){
1358 | print "Error: cannot find sudo executable.\n";
1359 | exit(STATE_UNKNOWN);
1360 | }
1361 | if($> != 0){
1362 | $storcli = $sudo.' '.$storcli;
1363 | }
1364 | }
1365 | }
1366 | # Print storcli version if available
1367 | if(defined($version)){ displayVersion($storcli, $noCleanlogs) }
1368 | # Prepare storcli command
1369 | $storcli .= " /c$CONTROLLER";
1370 | # Check if the controller number can be used
1371 | if(!getControllerTime($storcli, $noCleanlogs)){
1372 | print "Error: invalid controller number, controller not found!\n";
1373 | exit(STATE_UNKNOWN);
1374 | }
1375 | # Prepare command line arrays
1376 | @enclosures = split(/,/,join(',', @enclosures));
1377 | @logDevices = split(/,/,join(',', @logDevices));
1378 | @physDevices = split(/,/,join(',', @physDevices));
1379 | # Check if the BBU param is correct
1380 | if(($bbu != 2) && ($bbu != 1) && ($bbu != 0)) {
1381 | print "Error: invalid BBU/CV parameter, must be 0, 1 or 2!\n";
1382 | exit(STATE_UNKNOWN);
1383 | }
1384 | my ($bbuPresent,$cvPresent) = (0,0);
1385 | if($bbu == 1 || $bbu == 2){
1386 | ($bbuPresent,$cvPresent) = checkBBUorCVIsPresent($storcli, $noCleanlogs);
1387 | # Only exit with CRITICAL if bbu param is 1
1388 | if($bbuPresent == 0 && $cvPresent == 0 && $bbu == 1){
1389 | ${$statusLevel_a[0]} = 'Critical';
1390 | push @{$criticals_a}, 'BBU/CV_Present';
1391 | $statusLevel_a[3]->{'BBU_Status'} = 'Critical';
1392 | $statusLevel_a[3]->{'CV_Status'} = 'Critical';
1393 | }
1394 | }
1395 | if($bbuPresent == 1){getBBUStatus($storcli, $noCleanlogs, \@statusLevel_a, $verboseCommands_a); }
1396 | if($cvPresent == 1){ getCVStatus($storcli, $noCleanlogs, \@statusLevel_a, $verboseCommands_a); }
1397 |
1398 | my $controllerToCheck = getControllerInfo($storcli, $noCleanlogs, $verboseCommands_a);
1399 | my $LDDevicesToCheck = getLogicalDevices($storcli, $noCleanlogs, \@logDevices, 'all', $verboseCommands_a);
1400 | my $LDInitToCheck = getLogicalDevices($storcli, $noCleanlogs, \@logDevices, 'init', $verboseCommands_a);
1401 | my $PDDevicesToCheck = getPhysicalDevices($storcli, $noCleanlogs, \@enclosures, \@physDevices, 'all', $verboseCommands_a);
1402 | my $PDInitToCheck = getPhysicalDevices($storcli, $noCleanlogs, \@enclosures, \@physDevices, 'initialization', $verboseCommands_a);
1403 | my $PDRebuildToCheck = getPhysicalDevices($storcli, $noCleanlogs, \@enclosures, \@physDevices, 'rebuild', $verboseCommands_a);
1404 |
1405 | getControllerStatus(\@statusLevel_a, $controllerToCheck);
1406 | getLDStatus(\@statusLevel_a, $LDDevicesToCheck);
1407 | getLDStatus(\@statusLevel_a, $LDInitToCheck);
1408 | getPDStatus(\@statusLevel_a, $PDDevicesToCheck);
1409 | getPDStatus(\@statusLevel_a, $PDInitToCheck);
1410 | getPDStatus(\@statusLevel_a, $PDRebuildToCheck);
1411 |
1412 | print ${$statusLevel_a[0]}." ";
1413 | print getStatusString("Critical",\@statusLevel_a);
1414 | print getStatusString("Warning",\@statusLevel_a);
1415 | my $perf_str = getPerfString(\@statusLevel_a);
1416 | if($perf_str){
1417 | print "|".$perf_str;
1418 | }
1419 | if($VERBOSITY == 2 || $VERBOSITY == 3){
1420 | print "\n".getVerboseString(\@statusLevel_a, $controllerToCheck, $LDDevicesToCheck, $LDInitToCheck,
1421 | $PDDevicesToCheck, $PDInitToCheck, $PDRebuildToCheck)
1422 | }
1423 | $exitCode = STATE_OK;
1424 | if(${$statusLevel_a[0]} eq "Critical"){
1425 | $exitCode = STATE_CRITICAL;
1426 | }
1427 | if(${$statusLevel_a[0]} eq "Warning"){
1428 | $exitCode = STATE_WARNING;
1429 | }
1430 | exit($exitCode);
1431 | }
1432 |
--------------------------------------------------------------------------------