Rev 75 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4 | daniel-mar | 1 | <?php /* <ViaThinkSoftSignature> |
77 | daniel-mar | 2 | AhLH0uzYSC5/Au2X92QY2namTtPWujFr9iWDdPhrgAQq1MW3Z53fKr6eIt+kxjb5R |
3 | qMOZzG5gnnhp9nUKZgYDaK+Ic/5yumhv2ekVVZsbW6FSogpfDUTlXbJSqUby+TyYk |
||
4 | OSXj09oHiudrshLdI4Lx5ns75g/tzdR+9DPfNbWxEBFV1kVOtUM0MFnW31u56pHwm |
||
5 | D3oZcWXplxNDt+5sm6K1AdWXAswslMzKwaGIrKHFJdRt1mFmAp2uYBRVz6DQ0MeyN |
||
6 | zR9dMzrO7dgUrwRsxLPp3+hK9veENwcMwRXhKqVtJ6izTIgDP04hTsWeIaluUdMoX |
||
7 | UXX72/y1fh4F3jCDeWJ2iXVFLU4W6Uf7L5S3FR41+uR/pw9/6BALuuDLrA14NDiv5 |
||
8 | Q7dqRtD9cJT270Ppj/6oCbdv2FtsUogSFWpd+Ca3v2IHQ9Vhsz55OJ8mlqtAkJ+TL |
||
9 | GXc46tbwi2OYHk/Iqq+A0pIgW9/1aKAeLobK0y9lgHg8zg0YXkBt3fF8ZwZvI4cnf |
||
10 | qUhnzvJ9F8kB/23hiSZVZFovYRn/kDTTX6JGdHQWCXWSU7EI6/lIc6dKBNJF3JZnJ |
||
11 | 4MkKqcyLoPxnQlpG8U71bOQfgPRwpmgaTI7b9Ay9X4Y4dLah84LRxpMLGg0EuQxZL |
||
12 | F/NVKqf7wwgGclyUG4oGI+dyCed8IIvEvq8C8aKdHVtZB8pvPv6UfhHj26LCk+UeP |
||
13 | aViNRuif6TrlkYUiEzbKbfpSrQcKvWavd3QsTZfbnivflxLayrahVVebEPNOhdGDR |
||
14 | fyporIb5EhtU5CuPxqCYDhPNjNFOj0C/JJlyf6a/WiGr6TNYjkYp/jdJhBrvDk6b/ |
||
15 | d7Y+TdUQaotgtLr3usqTSqoEHZrdqkcdUldB7hUHXzvYogZVYVKtuqlKRn96Yc2sc |
||
16 | 3KR2ZAi+Pl3W52pMVzPlZ6zy8UZgmiuDxXuGEJ8hwcVijcTE7WV6P/fAZ7bu0LCKl |
||
17 | HsBFfSRA1ke3hZWtWRisKGpsHhXPTM8ShAHwo6nr56gJnc5Lb1scqfhJRC41V/AtG |
||
18 | 4UfvpOTBJhL7wqqM8iIlZxHvGuTOH8b2NgVdkhTEmYxj69ZRZEescAB9znklUn4lF |
||
19 | XD9uW9ZvKwZXhdQ6lTEGcxI/CxGAEo3AcsFTsebK3BP1ugGcg+mkG4gU3/XKB6Hvx |
||
20 | x/3KogBF7tu2lWwPfHiV7F5cUXPpx3H8UykwFdNdQAsTKTWl58LDP/efpvStP8TZ0 |
||
21 | kLOMxXXxhdVeIelQQMSsGUQ0Lz/7T4u4G7X9KdsaUrNw/1Btbr9vPRQ7ZkuXMVOzV |
||
22 | VbcCJsanRk7isRkob0574FyPyvMitdTZKC9lDOLAqbYPeaUQMsvoNyFXsvLlfJPRS |
||
23 | A== |
||
4 | daniel-mar | 24 | </ViaThinkSoftSignature> */ ?> |
2 | daniel-mar | 25 | <?php |
26 | |||
27 | /* |
||
28 | * VNag - Nagios Framework for PHP |
||
29 | * Developed by Daniel Marschall, ViaThinkSoft <www.viathinksoft.com> |
||
30 | * Licensed under the terms of the Apache 2.0 license |
||
31 | * |
||
77 | daniel-mar | 32 | * Revision 2023-10-13 |
2 | daniel-mar | 33 | */ |
34 | |||
35 | declare(ticks=1); |
||
36 | |||
37 | class MdStatCheck extends VNag { |
||
38 | public function __construct() { |
||
39 | parent::__construct(); |
||
40 | |||
41 | if ($this->is_http_mode()) { |
||
42 | // Don't allow the standard arguments via $_REQUEST |
||
43 | $this->registerExpectedStandardArguments(''); |
||
44 | } else { |
||
45 | $this->registerExpectedStandardArguments('Vhtv'); |
||
46 | } |
||
47 | |||
48 | $this->getHelpManager()->setPluginName('vnag_mdstat'); |
||
49 | $this->getHelpManager()->setVersion('2.0'); |
||
50 | $this->getHelpManager()->setShortDescription('This plugin checks the contents of /proc/mdstat and warns when a harddisk has failed.'); |
||
51 | $this->getHelpManager()->setCopyright('Copyright (C) 2011-$CURYEAR$ Daniel Marschall, ViaThinkSoft.'); |
||
52 | $this->getHelpManager()->setSyntax('$SCRIPTNAME$ (no additional arguments expected)'); |
||
53 | $this->getHelpManager()->setFootNotes('If you encounter bugs, please contact ViaThinkSoft at www.viathinksoft.com'); |
||
54 | } |
||
55 | |||
56 | private function getDisks($device) { |
||
57 | $disks = glob("/sys/block/$device/md/dev-*"); |
||
58 | foreach ($disks as &$disk) { |
||
59 | $ary = explode('/', $disk); |
||
60 | $disk = substr(array_pop($ary), 4); |
||
61 | } |
||
62 | return $disks; |
||
63 | } |
||
64 | |||
65 | private function raidLevel($device) { |
||
66 | $level_file = "/sys/block/$device/md/level"; |
||
67 | if (!file_exists($level_file)) { |
||
68 | throw new VNagException("Kernel too old to fetch RAID level of array $device"); |
||
69 | } |
||
77 | daniel-mar | 70 | $cont = @file_get_contents($level_file); |
71 | if ($cont === false) { |
||
72 | throw new VNagException("Cannot read $level_file"); |
||
73 | } |
||
74 | $level = file_exists($level_file) ? trim($cont) : 'RAID?'; |
||
2 | daniel-mar | 75 | return $level; |
76 | } |
||
77 | |||
78 | private function raidState($device) { |
||
74 | daniel-mar | 79 | // mdadm outputs "clean, degraded", but /sys/block/md0/md/array_state only outputs "clean" |
80 | $output = []; |
||
81 | exec("mdadm --detail /dev/".escapeshellarg($device)." | grep -e '^\s*State : '", $output, $ec); |
||
82 | if ($ec == 0) { |
||
83 | $state = trim(implode("\n", $output)); |
||
84 | $state = trim(explode(':', $state)[1]); |
||
85 | return $state; |
||
86 | } |
||
87 | |||
88 | // Fallback |
||
2 | daniel-mar | 89 | $state_file = "/sys/block/$device/md/array_state"; |
90 | if (!file_exists($state_file)) { |
||
91 | throw new VNagException("Kernel too old to fetch state of array $device"); |
||
92 | } |
||
77 | daniel-mar | 93 | $cont = @file_get_contents($state_file); |
94 | if ($cont === false) { |
||
95 | throw new VNagException("Cannot read $state_file"); |
||
96 | } |
||
97 | $state = trim($cont); |
||
2 | daniel-mar | 98 | return $state; |
99 | } |
||
100 | |||
101 | private function check_disk_state($array, $disk) { |
||
102 | $disk_state_file = "/sys/block/$array/md/dev-$disk/state"; |
||
103 | if (!file_exists($disk_state_file)) { |
||
104 | throw new VNagException("Kernel too old to fetch state of disk $array:$disk"); |
||
105 | } |
||
77 | daniel-mar | 106 | $cont = @file_get_contents($disk_state_file); |
107 | if ($cont === false) { |
||
108 | throw new VNagException("Cannot read $disk_state_file"); |
||
109 | } |
||
110 | $disk_states = trim($cont); |
||
2 | daniel-mar | 111 | $disk_state_ary = explode(',', $disk_states); |
112 | $disk_state_ary = array_map('trim', $disk_state_ary); |
||
113 | |||
114 | $status = VNag::STATUS_OK; |
||
115 | $verbosity = VNag::VERBOSITY_ADDITIONAL_INFORMATION; |
||
116 | |||
117 | foreach ($disk_state_ary as $disk_state) { |
||
118 | // https://www.kernel.org/doc/html/v4.15/admin-guide/md.html |
||
119 | // CRIT faulty: device has been kicked from active use due to a detected fault, or it has unacknowledged bad blocks |
||
120 | // OK in_sync: device is a fully in-sync member of the array |
||
121 | // OK writemostly: device will only be subject to read requests if there are no other options. This applies only to raid1 arrays. |
||
122 | // CRIT blocked: device has failed, and the failure hasn.t been acknowledged yet by the metadata handler. Writes that would write to this device if it were not faulty are blocked. |
||
123 | // WARN spare: device is working, but not a full member. This includes spares that are in the process of being recovered to |
||
124 | // WARN write_error: device has ever seen a write error. |
||
125 | // WARN want_replacement: device is (mostly) working but probably should be replaced, either due to errors or due to user request. |
||
126 | // OK replacement: device is a replacement for another active device with same raid_disk. |
||
127 | |||
128 | if (($disk_state == 'faulty') || ($disk_state == 'blocked')) { |
||
129 | $status = max($status, VNag::STATUS_CRITICAL); |
||
130 | $verbosity = min($verbosity, VNag::VERBOSITY_SUMMARY); |
||
131 | } |
||
132 | if (($disk_state == 'spare') || ($disk_state == 'write_error') || ($disk_state == 'want_replacement')) { |
||
133 | $status = max($status, VNag::STATUS_WARNING); |
||
134 | $verbosity = min($verbosity, VNag::VERBOSITY_SUMMARY); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | return array($status, $verbosity, $disk_states); |
||
139 | } |
||
140 | |||
141 | private function get_raid_arrays() { |
||
142 | $arrays = array(); |
||
75 | daniel-mar | 143 | $devices = glob('/dev/md/'.'*'); |
2 | daniel-mar | 144 | foreach ($devices as $device) { |
145 | $ary = explode('/', $device); |
||
146 | $arrays[] = 'md'.array_pop($ary); |
||
147 | } |
||
148 | return $arrays; |
||
149 | } |
||
150 | |||
151 | protected function cbRun() { |
||
152 | $disks_total = 0; |
||
153 | $disks_critical = 0; |
||
154 | $disks_warning = 0; |
||
155 | |||
156 | $arrays = $this->get_raid_arrays(); |
||
157 | foreach ($arrays as $array) { |
||
158 | $level = $this->raidLevel($array); |
||
159 | $state = $this->raidState($array); |
||
160 | |||
75 | daniel-mar | 161 | // https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git/tree/Detail.c#n491 |
162 | if (stripos($state, ', FAILED') !== false) $this->setStatus(VNag::STATUS_CRITICAL); |
||
163 | if (stripos($state, ', degraded') !== false) $this->setStatus(VNag::STATUS_CRITICAL); |
||
74 | daniel-mar | 164 | |
2 | daniel-mar | 165 | $disk_texts = array(); |
166 | $verbosity = VNag::VERBOSITY_ADDITIONAL_INFORMATION; |
||
167 | $disks = $this->getDisks($array); |
||
168 | foreach ($disks as $disk) { |
||
169 | $disks_total++; |
||
170 | list($status, $verbosity_, $disk_states) = $this->check_disk_state($array, $disk); |
||
171 | $verbosity = min($verbosity, $verbosity_); |
||
172 | $this->setStatus($status); |
||
173 | if ($status == VNag::STATUS_WARNING) $disks_warning++; |
||
174 | if ($status == VNag::STATUS_CRITICAL) $disks_critical++; |
||
175 | $status_text = VNagLang::status($status, VNag::STATUSMODEL_SERVICE); |
||
176 | $disk_texts[] = "$disk ($status_text: $disk_states)"; |
||
177 | } |
||
178 | |||
179 | # Example output: |
||
180 | # Array md0 (raid1, degraded): sda1 (Warning: faulty, blocked), sdb1 (OK: in_sync) |
||
181 | $this->addVerboseMessage("Array $array ($level, $state): ".implode(', ', $disk_texts), $verbosity); |
||
182 | } |
||
183 | |||
184 | $this->setHeadline(sprintf('%s disks in %s arrays (%s warnings, %s critical)', $disks_total, count($arrays), $disks_warning, $disks_critical)); |
||
185 | } |
||
186 | } |