1 | <?php |
||
2 | |||
3 | use App\Models\Device; |
||
4 | use LibreNMS\Config; |
||
5 | use LibreNMS\RRD\RrdDefinition; |
||
6 | |||
7 | function get_service_status($device = null) |
||
8 | { |
||
9 | $sql_query = 'SELECT service_status, count(service_status) as count FROM services WHERE'; |
||
10 | $sql_param = []; |
||
11 | $add = 0; |
||
12 | |||
13 | if (! is_null($device)) { |
||
14 | // Add a device filter to the SQL query. |
||
15 | $sql_query .= ' `device_id` = ?'; |
||
16 | $sql_param[] = $device; |
||
17 | $add++; |
||
18 | } |
||
19 | |||
20 | if ($add == 0) { |
||
21 | // No filters, remove " WHERE" -6 |
||
22 | $sql_query = substr($sql_query, 0, strlen($sql_query) - 6); |
||
23 | } |
||
24 | $sql_query .= ' GROUP BY service_status'; |
||
25 | |||
26 | // $service is not null, get only what we want. |
||
27 | $result = dbFetchRows($sql_query, $sql_param); |
||
28 | |||
29 | // Set our defaults to 0 |
||
30 | $service_count = [0 => 0, 1 => 0, 2 => 0]; |
||
31 | // Rebuild the array in a more convenient method |
||
32 | foreach ($result as $v) { |
||
33 | $service_count[$v['service_status']] = $v['count']; |
||
34 | } |
||
35 | |||
36 | return $service_count; |
||
37 | } |
||
38 | |||
39 | function add_service($device, $type, $desc, $ip = '', $param = '', $ignore = 0, $disabled = 0, $template_id = '', $name) |
||
40 | { |
||
41 | if (! is_array($device)) { |
||
42 | $device = device_by_id_cache($device); |
||
43 | } |
||
44 | |||
45 | if (empty($ip)) { |
||
46 | $ip = Device::pollerTarget($device['hostname']); |
||
47 | } |
||
48 | |||
49 | $insert = ['device_id' => $device['device_id'], 'service_ip' => $ip, 'service_type' => $type, 'service_changed' => ['UNIX_TIMESTAMP(NOW())'], 'service_desc' => $desc, 'service_param' => $param, 'service_ignore' => $ignore, 'service_status' => 3, 'service_message' => 'Service not yet checked', 'service_ds' => '{}', 'service_disabled' => $disabled, 'service_template_id' => $template_id, 'service_name' => $name]; |
||
50 | |||
51 | return dbInsert($insert, 'services'); |
||
52 | } |
||
53 | |||
54 | function service_get($device = null, $service = null) |
||
55 | { |
||
56 | $sql_query = 'SELECT `service_id`,`device_id`,`service_ip`,`service_type`,`service_desc`,`service_param`,`service_ignore`,`service_status`,`service_changed`,`service_message`,`service_disabled`,`service_ds`,`service_template_id`,`service_name` FROM `services` WHERE'; |
||
57 | $sql_param = []; |
||
58 | $add = 0; |
||
59 | |||
60 | d_echo('SQL Query: ' . $sql_query); |
||
61 | if (! is_null($service)) { |
||
62 | // Add a service filter to the SQL query. |
||
63 | $sql_query .= ' `service_id` = ? AND'; |
||
64 | $sql_param[] = $service; |
||
65 | $add++; |
||
66 | } |
||
67 | if (! is_null($device)) { |
||
68 | // Add a device filter to the SQL query. |
||
69 | $sql_query .= ' `device_id` = ? AND'; |
||
70 | $sql_param[] = $device; |
||
71 | $add++; |
||
72 | } |
||
73 | |||
74 | if ($add == 0) { |
||
75 | // No filters, remove " WHERE" -6 |
||
76 | $sql_query = substr($sql_query, 0, strlen($sql_query) - 6); |
||
77 | } else { |
||
78 | // We have filters, remove " AND" -4 |
||
79 | $sql_query = substr($sql_query, 0, strlen($sql_query) - 4); |
||
80 | } |
||
81 | d_echo('SQL Query: ' . $sql_query); |
||
82 | |||
83 | // $service is not null, get only what we want. |
||
84 | $services = dbFetchRows($sql_query, $sql_param); |
||
85 | d_echo('Service Array: ' . print_r($services, true) . "\n"); |
||
86 | |||
87 | return $services; |
||
88 | } |
||
89 | |||
90 | function edit_service($update = [], $service = null) |
||
91 | { |
||
92 | if (! is_numeric($service)) { |
||
93 | return false; |
||
94 | } |
||
95 | |||
96 | return dbUpdate($update, 'services', '`service_id`=?', [$service]); |
||
97 | } |
||
98 | |||
99 | function delete_service($service = null) |
||
100 | { |
||
101 | if (! is_numeric($service)) { |
||
102 | return false; |
||
103 | } |
||
104 | |||
105 | return dbDelete('services', '`service_id` = ?', [$service]); |
||
106 | } |
||
107 | |||
108 | function discover_service($device, $service) |
||
109 | { |
||
110 | if (! dbFetchCell('SELECT COUNT(service_id) FROM `services` WHERE `service_type`= ? AND `device_id` = ?', [$service, $device['device_id']])) { |
||
111 | add_service($device, $service, "$service Monitoring (Auto Discovered)", null, null, 0, 0, 0, "AUTO: $service"); |
||
112 | log_event('Autodiscovered service: type ' . $service, $device, 'service', 2); |
||
113 | echo '+'; |
||
114 | } |
||
115 | echo "$service "; |
||
116 | } |
||
117 | |||
118 | function poll_service($service) |
||
119 | { |
||
120 | $update = []; |
||
121 | $old_status = $service['service_status']; |
||
122 | $check_cmd = ''; |
||
123 | |||
124 | // if we have a script for this check, use it. |
||
125 | $check_script = Config::get('install_dir') . '/includes/services/check_' . strtolower($service['service_type']) . '.inc.php'; |
||
126 | if (is_file($check_script)) { |
||
127 | include $check_script; |
||
128 | } |
||
129 | |||
130 | // If we do not have a cmd from the check script, build one. |
||
131 | if ($check_cmd == '') { |
||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
132 | $check_cmd = Config::get('nagios_plugins') . '/check_' . $service['service_type'] . ' -H ' . ($service['service_ip'] ? $service['service_ip'] : $service['hostname']); |
||
133 | $check_cmd .= ' ' . $service['service_param']; |
||
134 | } |
||
135 | |||
136 | $service_id = $service['service_id']; |
||
137 | // Some debugging |
||
138 | d_echo("\nNagios Service - $service_id\n"); |
||
139 | // the check_service function runs $check_cmd through escapeshellcmd, so |
||
140 | [$new_status, $msg, $perf] = check_service($check_cmd); |
||
141 | d_echo("Response: $msg\n"); |
||
142 | |||
143 | // If we have performance data we will store it. |
||
144 | if (count($perf) > 0) { |
||
145 | // Yes, We have perf data. |
||
146 | $rrd_name = ['services', $service_id]; |
||
147 | |||
148 | // Set the DS in the DB if it is blank. |
||
149 | $DS = []; |
||
150 | foreach ($perf as $k => $v) { |
||
151 | $DS[$k] = $v['uom']; |
||
152 | } |
||
153 | d_echo('Service DS: ' . json_encode($DS, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n"); |
||
154 | if (($service['service_ds'] == '{}') || ($service['service_ds'] == '')) { |
||
155 | $update['service_ds'] = json_encode($DS); |
||
156 | } |
||
157 | |||
158 | // rrd definition |
||
159 | $rrd_def = new RrdDefinition(); |
||
160 | foreach ($perf as $k => $v) { |
||
161 | if (($v['uom'] == 'c') && ! (preg_match('/[Uu]ptime/', $k))) { |
||
162 | // This is a counter, create the DS as such |
||
163 | $rrd_def->addDataset($k, 'COUNTER', 0); |
||
164 | } else { |
||
165 | // Not a counter, must be a gauge |
||
166 | $rrd_def->addDataset($k, 'GAUGE', 0); |
||
167 | } |
||
168 | } |
||
169 | |||
170 | // Update data |
||
171 | $fields = []; |
||
172 | foreach ($perf as $k => $v) { |
||
173 | $fields[$k] = $v['value']; |
||
174 | } |
||
175 | |||
176 | $tags = compact('service_id', 'rrd_name', 'rrd_def'); |
||
177 | //TODO not sure if we have $device at this point, if we do replace faked $device |
||
178 | data_update(['hostname' => $service['hostname']], 'services', $tags, $fields); |
||
179 | } |
||
180 | |||
181 | if ($old_status != $new_status) { |
||
182 | // Status has changed, update. |
||
183 | $update['service_changed'] = time(); |
||
184 | $update['service_status'] = $new_status; |
||
185 | $update['service_message'] = $msg; |
||
186 | |||
187 | // TODO: Put the 3 lines below in a function getStatus(int) ? |
||
188 | $status_text = [0 => 'OK', 1 => 'Warning', 3 => 'Unknown']; |
||
189 | $old_status_text = isset($status_text[$old_status]) ? $status_text[$old_status] : 'Critical'; |
||
190 | $new_status_text = isset($status_text[$new_status]) ? $status_text[$new_status] : 'Critical'; |
||
191 | |||
192 | log_event( |
||
193 | "Service '{$service['service_type']}' changed status from $old_status_text to $new_status_text - {$service['service_desc']} - $msg", |
||
194 | $service['device_id'], |
||
195 | 'service', |
||
196 | 4, |
||
197 | $service['service_id'] |
||
198 | ); |
||
199 | } |
||
200 | |||
201 | if ($service['service_message'] != $msg) { |
||
202 | // Message has changed, update. |
||
203 | $update['service_message'] = $msg; |
||
204 | } |
||
205 | |||
206 | if (count($update) > 0) { |
||
207 | edit_service($update, $service['service_id']); |
||
208 | } |
||
209 | |||
210 | return true; |
||
211 | } |
||
212 | |||
213 | function check_service($command) |
||
214 | { |
||
215 | // This array is used to test for valid UOM's to be used for graphing. |
||
216 | // Valid values from: https://nagios-plugins.org/doc/guidelines.html#AEN200 |
||
217 | // Note: This array must be decend from 2 char to 1 char so that the search works correctly. |
||
218 | $valid_uom = ['us', 'ms', 'KB', 'MB', 'GB', 'TB', 'c', 's', '%', 'B']; |
||
219 | |||
220 | // Make our command safe. |
||
221 | $parts = preg_split('~(?:\'[^\']*\'|"[^"]*")(*SKIP)(*F)|\h+~', trim($command)); |
||
222 | $safe_command = implode(' ', array_map(function ($part) { |
||
223 | $trimmed = preg_replace('/^(\'(.*)\'|"(.*)")$/', '$2$3', $part); |
||
224 | |||
225 | return escapeshellarg($trimmed); |
||
226 | }, $parts)); |
||
227 | |||
228 | d_echo("Request: $safe_command\n"); |
||
229 | |||
230 | // Run the command and return its response. |
||
231 | exec('LC_NUMERIC="C" ' . $safe_command, $response_array, $status); |
||
232 | |||
233 | // exec returns an array, lets implode it back to a string. |
||
234 | $response_string = implode("\n", $response_array); |
||
235 | |||
236 | // Split out the response and the performance data. |
||
237 | [$response, $perf] = explode('|', $response_string); |
||
238 | |||
239 | // Split each performance metric |
||
240 | $perf_arr = explode(' ', $perf); |
||
241 | |||
242 | // Create an array for our metrics. |
||
243 | $metrics = []; |
||
244 | |||
245 | // Loop through the perf string extracting our metric data |
||
246 | foreach ($perf_arr as $string) { |
||
247 | // Separate the DS and value: DS=value |
||
248 | [$ds,$values] = explode('=', trim($string)); |
||
249 | |||
250 | // Keep the first value, discard the others. |
||
251 | [$value,,,] = explode(';', trim($values)); |
||
252 | $value = trim($value); |
||
253 | |||
254 | // Set an empty uom |
||
255 | $uom = ''; |
||
256 | |||
257 | // is the UOM valid - https://nagios-plugins.org/doc/guidelines.html#AEN200 |
||
258 | foreach ($valid_uom as $v) { |
||
259 | if ((strlen($value) - strlen($v)) === strpos($value, $v)) { |
||
260 | // Yes, store and strip it off the value |
||
261 | $uom = $v; |
||
262 | $value = substr($value, 0, -strlen($v)); |
||
263 | break; |
||
264 | } |
||
265 | } |
||
266 | |||
267 | if ($ds != '') { |
||
268 | // Normalize ds for rrd : ds-name must be 1 to 19 characters long in the characters [a-zA-Z0-9_] |
||
269 | // http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html |
||
270 | $normalized_ds = preg_replace('/[^a-zA-Z0-9_]/', '', $ds); |
||
271 | // if ds_name is longer than 19 characters, only use the first 19 |
||
272 | if (strlen($normalized_ds) > 19) { |
||
273 | $normalized_ds = substr($normalized_ds, 0, 19); |
||
274 | d_echo($ds . ' exceeded 19 characters, renaming to ' . $normalized_ds . "\n"); |
||
275 | } |
||
276 | if ($ds != $normalized_ds) { |
||
277 | // ds has changed. check if normalized_ds is already in the array |
||
278 | if (isset($metrics[$normalized_ds])) { |
||
279 | d_echo($normalized_ds . " collides with an existing index\n"); |
||
280 | $perf_unique = 0; |
||
281 | // Try to generate a unique name |
||
282 | for ($i = 0; $i < 10; $i++) { |
||
283 | $tmp_ds_name = substr($normalized_ds, 0, 18) . $i; |
||
284 | if (! isset($metrics[$tmp_ds_name])) { |
||
285 | d_echo($normalized_ds . " collides with an existing index\n"); |
||
286 | $normalized_ds = $tmp_ds_name; |
||
287 | $perf_unique = 1; |
||
288 | break; |
||
289 | } |
||
290 | } |
||
291 | if ($perf_unique == 0) { |
||
292 | // Try harder to generate a unique name |
||
293 | for ($i = 0; $i < 10; $i++) { |
||
294 | for ($j = 0; $j < 10; $j++) { |
||
295 | $tmp_ds_name = substr($normalized_ds, 0, 17) . $j . $i; |
||
296 | if (! isset($perf[$tmp_ds_name])) { |
||
297 | $normalized_ds = $tmp_ds_name; |
||
298 | $perf_unique = 1; |
||
299 | break 2; |
||
300 | } |
||
301 | } |
||
302 | } |
||
303 | } |
||
304 | if ($perf_unique == 0) { |
||
305 | d_echo('could not generate a unique ds-name for ' . $ds . "\n"); |
||
306 | } |
||
307 | } |
||
308 | $ds = $normalized_ds; |
||
309 | } |
||
310 | // We have a DS. Add an entry to the array. |
||
311 | d_echo('Perf Data - DS: ' . $ds . ', Value: ' . $value . ', UOM: ' . $uom . "\n"); |
||
312 | $metrics[$ds] = ['value'=>$value, 'uom'=>$uom]; |
||
313 | } else { |
||
314 | // No DS. Don't add an entry to the array. |
||
315 | d_echo("Perf Data - None.\n"); |
||
316 | } |
||
317 | } |
||
318 | |||
319 | return [$status, $response, $metrics]; |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * List all available services from nagios plugins directory |
||
324 | * |
||
325 | * @return array |
||
326 | */ |
||
327 | function list_available_services() |
||
328 | { |
||
329 | return \LibreNMS\Services::list(); |
||
330 | } |
||
331 |