2010-06-28 12:49:26 +00:00
< ? php
2020-01-30 13:20:30 +01:00
use App\Models\Device ;
2021-10-20 20:44:39 -05:00
use LibreNMS\Alert\AlertRules ;
2019-06-23 00:29:12 -05:00
use LibreNMS\Config ;
2017-02-23 22:45:50 +00:00
use LibreNMS\RRD\RrdDefinition ;
2022-04-20 21:32:36 -05:00
use LibreNMS\Util\Clean ;
2022-04-22 17:05:55 +02:00
use LibreNMS\Util\IP ;
2017-02-23 22:45:50 +00:00
2016-08-28 12:32:58 -05:00
function get_service_status ( $device = null )
{
2016-03-15 22:16:08 +10:00
$sql_query = 'SELECT service_status, count(service_status) as count FROM services WHERE' ;
$sql_param = [];
$add = 0 ;
2015-07-10 13:36:21 +02:00
2016-03-15 22:16:08 +10:00
if ( ! is_null ( $device )) {
// Add a device filter to the SQL query.
$sql_query .= ' `device_id` = ?' ;
$sql_param [] = $device ;
$add ++ ;
}
if ( $add == 0 ) {
// No filters, remove " WHERE" -6
$sql_query = substr ( $sql_query , 0 , strlen ( $sql_query ) - 6 );
}
$sql_query .= ' GROUP BY service_status' ;
// $service is not null, get only what we want.
$result = dbFetchRows ( $sql_query , $sql_param );
// Set our defaults to 0
$service_count = [ 0 => 0 , 1 => 0 , 2 => 0 ];
// Rebuild the array in a more convenient method
foreach ( $result as $v ) {
$service_count [ $v [ 'service_status' ]] = $v [ 'count' ];
}
return $service_count ;
}
2022-08-30 12:55:37 -05:00
function add_service ( $device , $type , $desc , $ip = '' , $param = '' , $ignore = 0 , $disabled = 0 , $template_id = '' , $name = '' )
2016-08-28 12:32:58 -05:00
{
2016-03-15 22:16:08 +10:00
if ( ! is_array ( $device )) {
$device = device_by_id_cache ( $device );
}
if ( empty ( $ip )) {
2020-01-30 13:20:30 +01:00
$ip = Device :: pollerTarget ( $device [ 'hostname' ]);
2016-03-15 22:16:08 +10:00
}
2021-02-02 06:40:11 +00:00
$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 ];
2020-09-21 15:40:17 +02:00
2016-03-15 22:16:08 +10:00
return dbInsert ( $insert , 'services' );
}
2016-08-28 12:32:58 -05:00
function service_get ( $device = null , $service = null )
{
2021-02-02 06:40:11 +00:00
$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' ;
2016-03-15 22:16:08 +10:00
$sql_param = [];
$add = 0 ;
d_echo ( 'SQL Query: ' . $sql_query );
if ( ! is_null ( $service )) {
// Add a service filter to the SQL query.
$sql_query .= ' `service_id` = ? AND' ;
$sql_param [] = $service ;
$add ++ ;
}
if ( ! is_null ( $device )) {
// Add a device filter to the SQL query.
$sql_query .= ' `device_id` = ? AND' ;
$sql_param [] = $device ;
$add ++ ;
}
if ( $add == 0 ) {
// No filters, remove " WHERE" -6
$sql_query = substr ( $sql_query , 0 , strlen ( $sql_query ) - 6 );
2016-08-28 12:32:58 -05:00
} else {
2016-03-15 22:16:08 +10:00
// We have filters, remove " AND" -4
$sql_query = substr ( $sql_query , 0 , strlen ( $sql_query ) - 4 );
}
d_echo ( 'SQL Query: ' . $sql_query );
// $service is not null, get only what we want.
$services = dbFetchRows ( $sql_query , $sql_param );
2016-08-28 12:32:58 -05:00
d_echo ( 'Service Array: ' . print_r ( $services , true ) . " \n " );
2016-03-15 22:16:08 +10:00
return $services ;
}
2016-08-28 12:32:58 -05:00
function edit_service ( $update = [], $service = null )
{
2016-03-15 22:16:08 +10:00
if ( ! is_numeric ( $service )) {
return false ;
}
return dbUpdate ( $update , 'services' , '`service_id`=?' , [ $service ]);
}
2016-08-28 12:32:58 -05:00
function delete_service ( $service = null )
{
2016-03-15 22:16:08 +10:00
if ( ! is_numeric ( $service )) {
return false ;
}
return dbDelete ( 'services' , '`service_id` = ?' , [ $service ]);
}
2016-08-28 12:32:58 -05:00
function discover_service ( $device , $service )
{
2015-07-10 13:36:21 +02:00
if ( ! dbFetchCell ( 'SELECT COUNT(service_id) FROM `services` WHERE `service_type`= ? AND `device_id` = ?' , [ $service , $device [ 'device_id' ]])) {
2021-02-02 06:40:11 +00:00
add_service ( $device , $service , " $service Monitoring (Auto Discovered) " , null , null , 0 , 0 , 0 , " AUTO: $service " );
2021-03-28 17:25:30 -05:00
log_event ( 'Autodiscovered service: type ' . $service , $device , 'service' , 2 );
2015-07-10 13:36:21 +02:00
echo '+' ;
}
echo " $service " ;
2016-03-15 22:16:08 +10:00
}
2016-08-28 12:32:58 -05:00
function poll_service ( $service )
{
2016-03-15 22:16:08 +10:00
$update = [];
$old_status = $service [ 'service_status' ];
2022-04-20 21:32:36 -05:00
$service [ 'service_type' ] = Clean :: fileName ( $service [ 'service_type' ]);
2022-04-22 17:05:55 +02:00
$service [ 'service_ip' ] = IP :: isValid ( $service [ 'service_ip' ]) ? $service [ 'service_ip' ] : Clean :: fileName ( $service [ 'service_ip' ]);
$service [ 'hostname' ] = IP :: isValid ( $service [ 'hostname' ]) ? $service [ 'hostname' ] : Clean :: fileName ( $service [ 'hostname' ]);
$service [ 'overwrite_ip' ] = IP :: isValid ( $service [ 'overwrite_ip' ]) ? $service [ 'overwrite_ip' ] : Clean :: fileName ( $service [ 'overwrite_ip' ]);
2016-04-12 07:09:33 +10:00
$check_cmd = '' ;
2016-03-15 22:16:08 +10:00
// if we have a script for this check, use it.
2019-06-23 00:29:12 -05:00
$check_script = Config :: get ( 'install_dir' ) . '/includes/services/check_' . strtolower ( $service [ 'service_type' ]) . '.inc.php' ;
2016-03-15 22:16:08 +10:00
if ( is_file ( $check_script )) {
include $check_script ;
}
// If we do not have a cmd from the check script, build one.
2016-04-12 07:09:33 +10:00
if ( $check_cmd == '' ) {
2022-04-20 21:32:36 -05:00
$check_cmd = Config :: get ( 'nagios_plugins' ) . '/check_' . $service [ 'service_type' ] . ' -H ' . ( $service [ 'service_ip' ] ? : $service [ 'hostname' ]);
2016-03-15 22:16:08 +10:00
$check_cmd .= ' ' . $service [ 'service_param' ];
}
2016-07-07 01:33:43 -05:00
$service_id = $service [ 'service_id' ];
2016-03-15 22:16:08 +10:00
// Some debugging
2016-07-07 01:33:43 -05:00
d_echo ( " \n Nagios Service - $service_id\n " );
2016-11-04 00:09:40 -07:00
// the check_service function runs $check_cmd through escapeshellcmd, so
2016-03-29 17:11:21 +10:00
[ $new_status , $msg , $perf ] = check_service ( $check_cmd );
2016-07-07 01:33:43 -05:00
d_echo ( " Response: $msg\n " );
2016-03-15 22:16:08 +10:00
// If we have performance data we will store it.
if ( count ( $perf ) > 0 ) {
// Yes, We have perf data.
2016-07-07 01:33:43 -05:00
$rrd_name = [ 'services' , $service_id ];
2016-03-15 22:16:08 +10:00
// Set the DS in the DB if it is blank.
$DS = [];
foreach ( $perf as $k => $v ) {
$DS [ $k ] = $v [ 'uom' ];
}
2021-03-04 07:55:41 -06:00
d_echo ( 'Service DS: ' . json_encode ( $DS , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . " \n " );
2017-01-25 18:41:48 +01:00
if (( $service [ 'service_ds' ] == '{}' ) || ( $service [ 'service_ds' ] == '' )) {
2016-03-15 22:16:08 +10:00
$update [ 'service_ds' ] = json_encode ( $DS );
}
2016-07-07 01:33:43 -05:00
// rrd definition
2017-02-23 22:45:50 +00:00
$rrd_def = new RrdDefinition ();
2016-07-07 01:33:43 -05:00
foreach ( $perf as $k => $v ) {
2023-03-13 22:32:22 +01:00
if (( $v [ 'uom' ] == 'c' ) && ! preg_match ( '/[Uu]ptime/' , $k )) {
2016-07-07 01:33:43 -05:00
// This is a counter, create the DS as such
2017-02-23 22:45:50 +00:00
$rrd_def -> addDataset ( $k , 'COUNTER' , 0 );
2016-08-28 12:32:58 -05:00
} else {
2016-07-07 01:33:43 -05:00
// Not a counter, must be a gauge
2017-02-23 22:45:50 +00:00
$rrd_def -> addDataset ( $k , 'GAUGE' , 0 );
2016-03-15 22:16:08 +10:00
}
}
2016-07-07 01:33:43 -05:00
// Update data
$fields = [];
2016-03-15 22:16:08 +10:00
foreach ( $perf as $k => $v ) {
2016-07-07 01:33:43 -05:00
$fields [ $k ] = $v [ 'value' ];
2016-03-15 22:16:08 +10:00
}
2016-07-07 01:33:43 -05:00
$tags = compact ( 'service_id' , 'rrd_name' , 'rrd_def' );
//TODO not sure if we have $device at this point, if we do replace faked $device
data_update ([ 'hostname' => $service [ 'hostname' ]], 'services' , $tags , $fields );
2016-03-15 22:16:08 +10:00
}
if ( $old_status != $new_status ) {
// Status has changed, update.
$update [ 'service_changed' ] = time ();
$update [ 'service_status' ] = $new_status ;
$update [ 'service_message' ] = $msg ;
2018-08-04 22:35:23 +02:00
// TODO: Put the 3 lines below in a function getStatus(int) ?
$status_text = [ 0 => 'OK' , 1 => 'Warning' , 3 => 'Unknown' ];
$old_status_text = isset ( $status_text [ $old_status ]) ? $status_text [ $old_status ] : 'Critical' ;
$new_status_text = isset ( $status_text [ $new_status ]) ? $status_text [ $new_status ] : 'Critical' ;
log_event (
" Service ' { $service [ 'service_type' ] } ' changed status from $old_status_text to $new_status_text - { $service [ 'service_desc' ] } - $msg " ,
$service [ 'device_id' ],
'service' ,
4 ,
$service [ 'service_id' ]
);
2021-10-20 20:44:39 -05:00
// Run alert rules due to status changed
$rules = new AlertRules ;
2022-01-12 05:00:58 +08:00
$rules -> runRules ( $service [ 'device_id' ]);
2016-03-15 22:16:08 +10:00
}
2016-04-26 06:46:22 +10:00
if ( $service [ 'service_message' ] != $msg ) {
// Message has changed, update.
$update [ 'service_message' ] = $msg ;
}
2016-03-15 22:16:08 +10:00
if ( count ( $update ) > 0 ) {
2016-08-28 12:32:58 -05:00
edit_service ( $update , $service [ 'service_id' ]);
2016-03-15 22:16:08 +10:00
}
return true ;
}
2016-08-28 12:32:58 -05:00
function check_service ( $command )
{
2016-03-15 22:16:08 +10:00
// This array is used to test for valid UOM's to be used for graphing.
// Valid values from: https://nagios-plugins.org/doc/guidelines.html#AEN200
// Note: This array must be decend from 2 char to 1 char so that the search works correctly.
$valid_uom = [ 'us' , 'ms' , 'KB' , 'MB' , 'GB' , 'TB' , 'c' , 's' , '%' , 'B' ];
// Make our command safe.
2018-10-17 10:46:07 -05:00
$parts = preg_split ( '~(?:\'[^\']*\'|"[^"]*")(*SKIP)(*F)|\h+~' , trim ( $command ));
$safe_command = implode ( ' ' , array_map ( function ( $part ) {
$trimmed = preg_replace ( '/^(\'(.*)\'|"(.*)")$/' , '$2$3' , $part );
2020-09-21 15:40:17 +02:00
2018-10-17 10:46:07 -05:00
return escapeshellarg ( $trimmed );
}, $parts ));
2017-08-12 22:19:12 +03:00
2018-10-17 10:46:07 -05:00
d_echo ( " Request: $safe_command\n " );
2016-03-15 22:16:08 +10:00
// Run the command and return its response.
2018-10-17 10:46:07 -05:00
exec ( 'LC_NUMERIC="C" ' . $safe_command , $response_array , $status );
2016-03-15 22:16:08 +10:00
// exec returns an array, lets implode it back to a string.
$response_string = implode ( " \n " , $response_array );
// Split out the response and the performance data.
[ $response , $perf ] = explode ( '|' , $response_string );
2024-08-03 19:31:07 +02:00
// Split performance metrics into an array
preg_match_all ( '/\'[^\']*\'\S*|\S+/' , $perf , $perf_arr );
// preg_match_all returns a 2D array, we only need the first one
$perf_arr = $perf_arr [ 0 ];
2016-03-15 22:16:08 +10:00
// Create an array for our metrics.
$metrics = [];
// Loop through the perf string extracting our metric data
foreach ( $perf_arr as $string ) {
// Separate the DS and value: DS=value
[ $ds , $values ] = explode ( '=' , trim ( $string ));
// Keep the first value, discard the others.
2024-04-14 16:48:13 -05:00
$value = explode ( ';' , trim ( $values ));
$value = trim ( $value [ 0 ] ? ? '' );
2016-03-15 22:16:08 +10:00
// Set an empty uom
$uom = '' ;
// is the UOM valid - https://nagios-plugins.org/doc/guidelines.html#AEN200
foreach ( $valid_uom as $v ) {
2016-08-28 12:32:58 -05:00
if (( strlen ( $value ) - strlen ( $v )) === strpos ( $value , $v )) {
2016-03-15 22:16:08 +10:00
// Yes, store and strip it off the value
$uom = $v ;
$value = substr ( $value , 0 , - strlen ( $v ));
break ;
}
}
if ( $ds != '' ) {
2016-11-16 01:24:56 -06:00
// Normalize ds for rrd : ds-name must be 1 to 19 characters long in the characters [a-zA-Z0-9_]
// http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html
$normalized_ds = preg_replace ( '/[^a-zA-Z0-9_]/' , '' , $ds );
// if ds_name is longer than 19 characters, only use the first 19
if ( strlen ( $normalized_ds ) > 19 ) {
$normalized_ds = substr ( $normalized_ds , 0 , 19 );
d_echo ( $ds . ' exceeded 19 characters, renaming to ' . $normalized_ds . " \n " );
}
if ( $ds != $normalized_ds ) {
// ds has changed. check if normalized_ds is already in the array
if ( isset ( $metrics [ $normalized_ds ])) {
d_echo ( $normalized_ds . " collides with an existing index \n " );
$perf_unique = 0 ;
// Try to generate a unique name
for ( $i = 0 ; $i < 10 ; $i ++ ) {
$tmp_ds_name = substr ( $normalized_ds , 0 , 18 ) . $i ;
if ( ! isset ( $metrics [ $tmp_ds_name ])) {
d_echo ( $normalized_ds . " collides with an existing index \n " );
$normalized_ds = $tmp_ds_name ;
$perf_unique = 1 ;
2020-12-30 15:37:08 +01:00
break ;
2016-11-16 01:24:56 -06:00
}
}
if ( $perf_unique == 0 ) {
// Try harder to generate a unique name
for ( $i = 0 ; $i < 10 ; $i ++ ) {
for ( $j = 0 ; $j < 10 ; $j ++ ) {
$tmp_ds_name = substr ( $normalized_ds , 0 , 17 ) . $j . $i ;
if ( ! isset ( $perf [ $tmp_ds_name ])) {
$normalized_ds = $tmp_ds_name ;
$perf_unique = 1 ;
break 2 ;
}
}
}
}
if ( $perf_unique == 0 ) {
d_echo ( 'could not generate a unique ds-name for ' . $ds . " \n " );
}
}
$ds = $normalized_ds ;
}
2016-03-15 22:16:08 +10:00
// We have a DS. Add an entry to the array.
d_echo ( 'Perf Data - DS: ' . $ds . ', Value: ' . $value . ', UOM: ' . $uom . " \n " );
2024-01-05 05:39:12 +01:00
$metrics [ $ds ] = [ 'value' => $value , 'uom' => $uom ];
2016-08-28 12:32:58 -05:00
} else {
2016-03-15 22:16:08 +10:00
// No DS. Don't add an entry to the array.
d_echo ( " Perf Data - None. \n " );
}
}
2015-07-10 13:36:21 +02:00
2016-03-15 22:16:08 +10:00
return [ $status , $response , $metrics ];
2015-07-10 13:36:21 +02:00
}
2018-01-21 20:56:57 +01:00
/**
* List all available services from nagios plugins directory
*
* @ return array
*/
function list_available_services ()
{
2021-02-02 06:40:11 +00:00
return \LibreNMS\Services :: list ();
2018-01-21 20:56:57 +01:00
}