diff --git a/CHANGELOG.md b/CHANGELOG.md index 6172227..0167f80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,11 @@ * feature: Add cpu/memory statistics * feature: Better process control * feature: Add return data size stats -* feature: Add log renention +* feature: Add log retention * feature: Command run only for selected tests +* feature: Add configurable test timeout +* feature: Add Servcheck graph template +* issue: Better themes support --- 0.3 --- diff --git a/README.md b/README.md index 3997aa7..1a66b34 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ This is Cacti's Services monitoring plugin. Based on Webseer plugin. This plugin allows you to add service monitoring to Cacti. -You simply add service check, allow service specific test +You simply add service check, allow service specific test (like certificate check, long duration check, ...) -and you can add the expected response. Servcheck periodically run test +and you can add the expected response. Servcheck periodically run test and notify if a service check fails. The plugin records statistics -about the connection and service response. Servcheck can send email notification +about the connection and service response. Servcheck can send email notification or run script when the status changes. This plugin, like many others, integrates with Cacti's Maintenance or 'maint' @@ -18,10 +18,10 @@ take place during maintenance periods. ## Tests and results -Setvcheck try to run test once or more than once (you can configure it). -If successful on the first attempt, no further -tests will be performed. Main result is ok/error. A lot of information -are returned with main result. After the test is completed, statistics are generated. +Setvcheck try to run test once or more than once (you can configure it). +If successful on the first attempt, no further tests will be performed. +Main result is ok/error. A lot of information are returned with main result. +After the test is completed, statistics are generated. Based on the statistics, email notifications can be sent: - service state change - search text in result change @@ -45,6 +45,8 @@ MQTT - try to subscribe topic or wait for any message and print result SNMP - get or walk specified OID and return answer SSH_COMMAND - ssh connect, run command and return output +## Graph template +You can create graph for each test. How to and template is in templates directory. ## Important Servcheck has great debug output. If you have any problem, try to run your test: @@ -52,11 +54,15 @@ Servcheck has great debug output. If you have any problem, try to run your test: Example_script.sh shows you how to use returned variables in own script. +Test duration - default test duration is 5 seconds, you can change it in settings. If you need different for +specific test, change "Long duration alert" parametr in test. After that the max. duration will be +Long duration alert + 2 seconds. + Recommendation for tests with download - please download only small not binary files. Default Libcurl build doesn't compile all services. You have to compile again for SMB, LDAP, ... -For POP3 and IMAP tests is better insert correct username and password. Without credentials, +For POP3 and IMAP tests is better insert correct username and password. Without credentials, curl can will return incorrect result. SCP is in insecure mode - doesn't check SSH server key! diff --git a/includes/arrays.php b/includes/arrays.php index 04639a3..c177a4b 100644 --- a/includes/arrays.php +++ b/includes/arrays.php @@ -42,30 +42,12 @@ ]; $servcheck_states = [ - 'error' => [ - 'color' => '#FB4A14', - 'display' => __('Error', 'servcheck') - ], - 'duration' => [ - 'color' => '#CDFDFF', - 'display' => __('Long duration', 'servcheck') - ], - 'warning' => [ - 'color' => '#FCAA94', - 'display' => __('Warning', 'servcheck') - ], - 'failing' => [ - 'color' => '#FAFD9E', - 'display' => __('Failing', 'servcheck') - ], - 'ok' => [ - 'color' => '#E0FFE0', - 'display' => __('Ok', 'servcheck') - ], - 'disabled' => [ - 'color' => '#CDCFC4', - 'display' => __('Disabled', 'servcheck') - ] + 'error' => __('Error', 'servcheck'), + 'duration' => __('Long duration', 'servcheck'), + 'warning' => __('Warning', 'servcheck'), + 'failing' => __('Failing', 'servcheck'), + 'ok' => __('Ok', 'servcheck'), + 'disabled' => __('Disabled', 'servcheck') ]; $service_types = [ @@ -555,7 +537,7 @@ 'default' => 0, 'max_length' => '5', 'size' => '30', - 'description' => __('If the test time is greater than this value more times in a row, send a notification. Related to variable Duration count.', 'servcheck'), + 'description' => __('If the test time is greater than this value more times in a row, send a notification. Related to variable Duration count. The test inherits the maximum runtime from the settings. If a higher value is set here, the maximum runtime is this value + 2 seconds.', 'servcheck'), 'value' => '|arg1:duration_trigger|', ], 'duration_count' => [ @@ -936,7 +918,7 @@ 'ftp_scp' => __('Encrypted SCP connection, login and try to download file specified in path (/path/to/file.txt).', 'servcheck'), 'smb_smb' => __('Try SMB protocol, username and password are required. Try to login and download file.', 'servcheck'), 'smb_smbs' => __('Try SMB protocol, username and password are required. Try to login and download file.', 'servcheck'), - 'mqtt_mqtt' => __('Connetct to MQTT server and listen for any message. You can specify topic in Path (bedroom/temp), blank for any topic', 'servcheck'), + 'mqtt_mqtt' => __('Connetct to MQTT server and listen for any message. You can specify topic in Path (bedroom/temp), blank for any topic. For this test is recommended increase \"Long duration alert\"', 'servcheck'), 'rest_basic' => __('REST API test with basic HTTP auth. Prepare credential first.', 'servcheck'), 'rest_apikey' => __('REST API test with API key auth. Prepare credential first.', 'servcheck'), 'rest_oauth2' => __('REST API test with Oauth2. Prepare credential first.', 'servcheck'), diff --git a/includes/functions.php b/includes/functions.php index da945ae..dab56e2 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -63,6 +63,13 @@ function servcheck_check_debug() { } } +/** + * Log debug message + * + * @param string $message Text of the message that will be logged + * + * @return void + */ function servcheck_debug($message = '') { global $debug; @@ -226,7 +233,7 @@ function servcheck_legend() { print ''; foreach ($servcheck_states as $index => $state) { - print '' . $state['display'] . ''; + print '' . $state . ''; } print ''; diff --git a/includes/test_curl.php b/includes/test_curl.php index 26dfcd8..e16a1aa 100644 --- a/includes/test_curl.php +++ b/includes/test_curl.php @@ -44,7 +44,7 @@ function curl_try($test) { CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 4, - CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 1) : 5, + CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), CURLOPT_CAINFO => $ca_info, ]; diff --git a/includes/test_dns.php b/includes/test_dns.php index a59d873..cdb17bd 100644 --- a/includes/test_dns.php +++ b/includes/test_dns.php @@ -38,7 +38,7 @@ function dns_try($test) { servcheck_debug('Querying ' . $test['hostname'] . ' for record ' . $test['dns_query']); - $a = new mxlookup($test['dns_query'], $test['hostname'], $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 3) : 5); + $a = new mxlookup($test['dns_query'], $test['hostname'], $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration')); if (!cacti_sizeof($a->arrMX)) { $results['result'] = 'error'; @@ -117,7 +117,7 @@ function doh_try($test) { CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 4, - CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 3) : 5, + CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), CURLOPT_CAINFO => $ca_info, ]; diff --git a/includes/test_ftp.php b/includes/test_ftp.php index fb7e285..1affa0a 100644 --- a/includes/test_ftp.php +++ b/includes/test_ftp.php @@ -43,7 +43,7 @@ function ftp_try($test) { CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 4, - CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 1) : 5, + CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), CURLOPT_CAINFO => $ca_info, ]; diff --git a/includes/test_mail.php b/includes/test_mail.php index 0c963d9..a7043dd 100644 --- a/includes/test_mail.php +++ b/includes/test_mail.php @@ -123,7 +123,7 @@ function mail_try($test) { 'tcp://' . $test['hostname'], $errno, $errstr, - 3, + $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), STREAM_CLIENT_CONNECT, $context ); @@ -153,7 +153,7 @@ function mail_try($test) { 'ssl://' . $test['hostname'], $errno, $errstr, - 3, + $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), STREAM_CLIENT_CONNECT, $context ); @@ -204,7 +204,7 @@ function mail_try($test) { 'tcp://' . $test['hostname'], $errno, $errstr, - 3, + $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), STREAM_CLIENT_CONNECT, $context ); @@ -283,7 +283,7 @@ function mail_try($test) { $method . '://' . $test['hostname'], $errno, $errstr, - 3, + $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), STREAM_CLIENT_CONNECT, $context ); @@ -364,7 +364,7 @@ function mail_try($test) { $method . '://' . $test['hostname'], $errno, $errstr, - 3, + $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), STREAM_CLIENT_CONNECT, $context ); diff --git a/includes/test_mqtt.php b/includes/test_mqtt.php index a533945..1d6c2bb 100644 --- a/includes/test_mqtt.php +++ b/includes/test_mqtt.php @@ -126,7 +126,7 @@ function mqtt_try($test) { CURLOPT_HEADER => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_FILE => $file, - CURLOPT_TIMEOUT => 7, + CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration'), CURLOPT_NOPROGRESS => false, CURLOPT_XFERINFOFUNCTION => function ($download_size, $downloaded, $upload_size, $uploaded) { if ($downloaded > 0) { diff --git a/includes/test_restapi.php b/includes/test_restapi.php index 82b3d24..f8ec93b 100644 --- a/includes/test_restapi.php +++ b/includes/test_restapi.php @@ -42,7 +42,7 @@ function restapi_try($test) { CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 4, - CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 3) : 5, + CURLOPT_TIMEOUT => $test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 3) : read_config_option('servcheck_test_max_duration'), CURLOPT_CAINFO => $ca_info, ]; diff --git a/includes/test_snmp.php b/includes/test_snmp.php index deb6121..ec894fb 100644 --- a/includes/test_snmp.php +++ b/includes/test_snmp.php @@ -29,6 +29,7 @@ function snmp_try($test) { $version = 2; $port = 161; + $timeout = ($test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration')) * 1000; // default result $results['result'] = 'error'; @@ -97,12 +98,14 @@ function snmp_try($test) { servcheck_debug('SNMP GET request, hostname ' . $test['hostname'] . ':' . $port); $data = cacti_snmp_get($test['hostname'], $credential['community'], $test['snmp_oid'], $version, $credential['snmp_username'], $credential['snmp_password'], $credential['snmp_auth_protocol'], - $credential['snmp_priv_passphrase'], $credential['snmp_priv_protocol'], $credential['snmp_context'], $port); + $credential['snmp_priv_passphrase'], $credential['snmp_priv_protocol'], $credential['snmp_context'], + $port, $timeout); } else { servcheck_debug('SNMP WALK request, hostname ' . $test['hostname'] . ':' . $port); $data = cacti_snmp_walk($test['hostname'], $credential['community'], $test['snmp_oid'], $version, $credential['snmp_username'], $credential['snmp_password'], $credential['snmp_auth_protocol'], - $credential['snmp_priv_passphrase'], $credential['snmp_priv_protocol'], $credential['snmp_context'], $port); + $credential['snmp_priv_passphrase'], $credential['snmp_priv_protocol'], $credential['snmp_context'], + $port, $timeout); $data = var_export($data, true); } diff --git a/includes/test_ssh.php b/includes/test_ssh.php index 01bdee5..80f0777 100644 --- a/includes/test_ssh.php +++ b/includes/test_ssh.php @@ -61,6 +61,8 @@ function ssh_try($test) { $ssh->enableQuietMode(); } + $ssh->setTimeout($test['duration_trigger'] > 0 ? ($test['duration_trigger'] + 2) : read_config_option('servcheck_test_max_duration')); + if ($test['cred_id'] > 0) { $cred = db_fetch_row_prepared('SELECT * FROM plugin_servcheck_credential WHERE id = ?', [$test['cred_id']]); diff --git a/poller_servcheck.php b/poller_servcheck.php index fee765c..7d2b3b5 100644 --- a/poller_servcheck.php +++ b/poller_servcheck.php @@ -60,7 +60,6 @@ $debug = false; $force = false; $start = microtime(true); -$test_id = 0; $test_cond = ''; $poller_id = $config['poller_id']; $poller_int = read_config_option('poller_interval'); @@ -75,15 +74,6 @@ } switch ($arg) { - case '--id': - if (is_numeric($value) && $value > 0) { - $test_id = $value; - } else { - print "FATAL: Option 'id' is not numeric" . PHP_EOL; - exit(1); - } - - break; case '-f': case '--force': $force = true; @@ -162,63 +152,51 @@ } } +$max_processes = read_config_option('servcheck_processes'); + +if (empty($max_processes)) { + $max_processes = 8; +} + $params = []; $params[] = $poller_id; -$sql_where = ' AND poller_id = ?'; +$sql_where = 'poller_id = ? '; -if ($test_id > 0) { - $sql_where = ' AND id = ?'; - $params[] = $test_id; +if (!$force) { + $sql_where .= 'AND enabled = "on" '; } -$tests = db_fetch_assoc_prepared("SELECT * +$tests = db_fetch_cell_prepared("SELECT COUNT(id) FROM plugin_servcheck_test - WHERE enabled = 'on' + WHERE $sql_where", $params); -$max_processes = read_config_option('servcheck_processes'); +$timeout = db_fetch_cell_prepared("SELECT MAX(duration_trigger*attempt) + FROM plugin_servcheck_test + WHERE + $sql_where", + $params); -if (empty($max_processes)) { - $max_processes = 8; +if ($timeout > 0) { + $timeout += 2; +} else { + read_config_option('servcheck_test_max_duration'); } -if (cacti_sizeof($tests)) { - $loop_count = 0; - - foreach ($tests as $test) { - while (true) { - $running = db_fetch_cell_prepared('SELECT COUNT(id) - FROM processes - WHERE tasktype = ? - AND taskname = ?', - ['servcheck', "child:$poller_id"]); - - if ($loop_count % 40 == 0) { - servcheck_debug("There are $running processes"); - } - - if ($max_processes - $running > 0) { - // The timeout is the number of attempts * the duration trigger or 5 seconds plus 5 seconds overhead - $timeout = $test['attempt'] * ($test['duration_trigger'] == 0 ? 5 : $test['duration_trigger']) + 5; +$use_processes = min($tests, $max_processes); - if (!register_process_start('servcheck', $taskname, $test['id'], $timeout)) { - cacti_log(sprintf('WARNING: Not Running Service Check %s it is still running', $test['name']), false, 'SERVCHECK'); - } else { - servcheck_debug('Launching Service Check ' . $test['name']); - - $command = read_config_option('path_php_binary'); - $args = '-q "' . $config['base_path'] . '/plugins/servcheck/servcheck_process.php" --id=' . $test['id'] . ($debug ? ' --debug' : ''); +if ($tests > 0) { + for ($f = 1; $f <= $use_processes; $f++) { + if (!register_process_start('servcheck', $taskname, $f, $timeout)) { + cacti_log(sprintf('WARNING: Not Running Service Check %s it is still running', $test['name']), false, 'SERVCHECK'); + } else { + servcheck_debug('Launching Servceck process ' . $f); - exec_background($command, $args); - } + $command = read_config_option('path_php_binary'); + $args = '-q "' . $config['base_path'] . '/plugins/servcheck/servcheck_process.php" --process=' . $f . ($debug ? ' --debug' : '') . ($force ? ' --force' : ''); - break; - } else { - usleep(2000); - } - - $loop_count++; + exec_background($command, $args); } } } else { @@ -248,7 +226,13 @@ $stat_search_ok = 0; $stat_search_ko = 0; -if (cacti_sizeof($tests)) { +if ($tests > 0) { + $tests = db_fetch_assoc_prepared("SELECT * + FROM plugin_servcheck_test + WHERE + $sql_where", + $params); + foreach ($tests as $test) { $test_last = db_fetch_row_prepared('SELECT result, result_search FROM plugin_servcheck_log @@ -362,7 +346,6 @@ function display_help() { print PHP_EOL . 'usage: poller_servcheck.php [--debug] [--force]' . PHP_EOL . PHP_EOL; print 'This binary will exec all the Service check child processes.' . PHP_EOL . PHP_EOL; - print '--id - Run for a specific test' . PHP_EOL . PHP_EOL; - print '--force - Force all the service checks to run now' . PHP_EOL . PHP_EOL; + print '--force - Force all the service checks to run now. Run even if the job is disabled or set to run less frequently than every poller cycle' . PHP_EOL . PHP_EOL; print '--debug - Display verbose output during execution' . PHP_EOL . PHP_EOL; } diff --git a/servcheck_ca.php b/servcheck_ca.php index b8cbdc0..2313d94 100644 --- a/servcheck_ca.php +++ b/servcheck_ca.php @@ -44,7 +44,7 @@ break; case 'edit': top_header(); - data_ca_edit(); + servcheck_data_edit(); bottom_footer(); break; @@ -169,7 +169,7 @@ function form_save() { exit; } -function data_ca_edit() { +function servcheck_data_edit() { global $servcheck_ca_fields; // ================= input validation ================= diff --git a/servcheck_credential.php b/servcheck_credential.php index 9e70d56..aab0c66 100644 --- a/servcheck_credential.php +++ b/servcheck_credential.php @@ -45,7 +45,7 @@ break; case 'edit': top_header(); - data_credential_edit(); + servcheck_data_edit(); bottom_footer(); break; @@ -424,7 +424,7 @@ function form_save() { exit; } -function data_credential_edit() { +function servcheck_data_edit() { global $servcheck_credential_fields, $servcheck_help_credential; // ================= input validation ================= diff --git a/servcheck_process.php b/servcheck_process.php index 5c81caa..ac96a64 100644 --- a/servcheck_process.php +++ b/servcheck_process.php @@ -62,12 +62,12 @@ $debug = false; $force = false; $test_id = 0; +$process = 0; $start = microtime(true); +$max_mem = 0; $poller_interval = read_config_option('poller_interval'); $poller_id = $config['poller_id']; -$cert_expiry_days = read_config_option('servcheck_certificate_expiry_days'); -$new_notify_expire = false; if (cacti_sizeof($parms)) { foreach ($parms as $parameter) { @@ -82,6 +82,10 @@ case '--id': $test_id = intval($value); + break; + case '--process': + $process = intval($value); + break; case '-d': case '--debug': @@ -93,7 +97,7 @@ case '-v': display_version(); - unregister_process('servcheck', "child:$poller_id", $test_id); + unregister_process('servcheck', "child:$poller_id"); exit; case '--help': @@ -101,7 +105,7 @@ case '-h': display_help(); - unregister_process('servcheck', "child:$poller_id", $test_id); + unregister_process('servcheck', "child:$poller_id"); exit; case '--force': @@ -114,425 +118,499 @@ print 'ERROR: Invalid Parameter ' . $parameter . PHP_EOL . PHP_EOL; display_help(); - unregister_process('servcheck', "child:$poller_id", $test_id); + unregister_process('servcheck', "child:$poller_id"); exit; } } } -if ($test_id <= 0) { - print 'ERROR: You must specify a test id' . PHP_EOL; +if ($test_id > 0 && $process > 0) { + print 'ERROR: Specify a test id or process number, not both' . PHP_EOL; - unregister_process('servcheck', "child:$poller_id", $test_id); + unregister_process('servcheck', "child:$poller_id"); exit(1); } -servcheck_check_debug(); +if ($test_id <= 0 && $process <= 0) { + print 'ERROR: You must specify a test id or process number' . PHP_EOL; -$enabled = ''; + unregister_process('servcheck', "child:$poller_id"); -if (!$force) { - $enabled = 'AND enabled = "on"'; + exit(1); } -$test = db_fetch_row_prepared('SELECT *, UNIX_TIMESTAMP(DATE_ADD(last_check, - INTERVAL (? * how_often) SECOND)) AS next_run - FROM plugin_servcheck_test - WHERE id = ? ' . $enabled, - [($poller_interval - 10), $test_id]); +servcheck_check_debug(); -if (!cacti_sizeof($test)) { - print 'ERROR: Test not Found!' . PHP_EOL; +$max_processes = read_config_option('servcheck_processes'); - unregister_process('servcheck', "child:$poller_id", $test_id); +if (empty($max_processes)) { + $max_processes = 8; +} - exit(1); +$params = []; +$params[] = $poller_id; +$sql_where = 'poller_id = ? '; + +if (!$force) { + $sql_where .= 'AND enabled = "on" '; } -$poller = db_fetch_cell_prepared('SELECT * FROM poller WHERE id = ?', - [$test['poller_id']]); +$tests = db_fetch_cell_prepared('SELECT count(*) + FROM plugin_servcheck_test + WHERE ' . $sql_where, + $params); -if ($poller == false) { - print 'Selected poller not found, changing to poller 1' . PHP_EOL; +$use_processes = min($tests, $max_processes); - db_execute_prepared('UPDATE plugin_servcheck_test - SET poller_id = 1 - WHERE id = ?', - [$test['poller_id']]); -} +if ($process > 0) { + if ($process > $use_processes) { + print 'ERROR: Process number is bigger than max. servcheck process' . PHP_EOL; -$logs = db_fetch_cell_prepared('SELECT COUNT(*) - FROM plugin_servcheck_log - WHERE test_id = ?', - [$test['id']]); + unregister_process('servcheck', "child:$poller_id", $process); -if ($logs > 0 && $test['next_run'] > time() && !$force) { - servcheck_debug('INFO: Test "' . $test['name'] . '" skipped. Not the right time to run the test.'); + exit(1); + } else { + $x = $process - 1; - unregister_process('servcheck', "child:$poller_id", $test_id); + $baseSize = intdiv($tests, $use_processes); + $remainder = $tests % $use_processes; - exit(0); -} + $extraBefore = min($x, $remainder); -if (api_plugin_is_enabled('maint')) { - require_once($config['base_path'] . '/plugins/maint/functions.php'); -} + $length = $baseSize + ($x < $remainder ? 1 : 0); + $offset = $x * $baseSize + $extraBefore; -if (function_exists('plugin_maint_check_servcheck_test')) { - if (plugin_maint_check_servcheck_test($test_id)) { - servcheck_debug('Maintenance schedule active, skipped.'); + array_unshift($params, ($poller_interval - 10)); - unregister_process('servcheck', "child:$poller_id", $test_id); + $tests = db_fetch_assoc_prepared("SELECT *, UNIX_TIMESTAMP(DATE_ADD(last_check, + INTERVAL (? * how_often) SECOND)) AS next_run + FROM plugin_servcheck_test + WHERE $sql_where + LIMIT $offset, $length", + $params); - exit(0); + print "Process: $process, offset: $offset, length: $length" . PHP_EOL; } } -$test['days_left'] = 0; -$test['duration'] = false; +if ($test_id > 0) { + $sql_where .= 'AND id = ?'; + $params[] = $test_id; + array_unshift($params, ($poller_interval - 10)); -// try it three times. First valid result skips next attempts -$x = 0; -$results = []; + $tests = db_fetch_assoc_prepared("SELECT *, UNIX_TIMESTAMP(DATE_ADD(last_check, + INTERVAL (? * how_often) SECOND)) AS next_run + FROM plugin_servcheck_test + WHERE $sql_where", + $params); +} -[$category, $service] = explode('_', $test['type']); +if (!cacti_sizeof($tests)) { + print 'ERROR: Test/tests not Found!' . PHP_EOL; -servcheck_debug('Category: ' . $category); -servcheck_debug('Service: ' . $service); + unregister_process('servcheck', "child:$poller_id", $process); -while ($x < $test['attempt']) { - $x++; + exit(1); +} - servcheck_debug('Service Check attempt ' . $x); +foreach ($tests as $test) { + $max_mem = memory_get_peak_usage(true); - if (!function_exists('curl_init') && ($category == 'web' || $category == 'smb' || $category == 'ldap' || - $category == 'ftp' || $category == 'mqtt' || $category == 'rest' || $service == 'doh')) { - print 'FATAL: You must install php-curl to use this test' . PHP_EOL; + if (function_exists('memory_reset_peak_usage')) { + memory_reset_peak_usage(); + } - servcheck_debug('Test ' . $test['id'] . ' requires php-curl library'); + servcheck_debug(str_pad('Starting test ' . $test['name'], 10)); + servcheck_run_test($test, $force); +} - $results['result'] = 'error'; - $results['curl'] = false; - $results['error'] = 'missing php-curl library'; - $results['result_search'] = 'not tested'; - $results['start'] = microtime(true); - $results['duration'] = 0; - $results['data'] = ''; +unregister_process('servcheck', "child:$poller_id", $process); - continue; - } +$rusage = getrusage(); - switch ($category) { - case 'web': - case 'ldap': - case 'smb': - require_once($config['base_path'] . '/plugins/servcheck/includes/test_curl.php'); - $results = curl_try($test); +$end = microtime(true); +$stats = sprintf('Time:%.2f, Stats:%s/%s, Down triggered:%s, Duration triggered:%s, Memory:%s MB, CPUuser:%.2f CPUsystem:%.2f', + $end - $start, + $test['stats_ok'], + $test['stats_bad'], + ($test['triggered'] == 0 ? 'No' : 'Yes'), + ($test['triggered_duration'] == 0 ? 'No' : 'Yes'), + $max_mem / 1024 / 1024, + $rusage['ru_utime.tv_sec'] + $rusage['ru_utime.tv_usec'] / 1E6, + $rusage['ru_stime.tv_sec'] + $rusage['ru_stime.tv_usec'] / 1E6); - break; - case 'mail': - require_once($config['base_path'] . '/plugins/servcheck/includes/test_mail.php'); - $results = mail_try($test); +servcheck_debug($stats); - break; - case 'mqtt': - require_once($config['base_path'] . '/plugins/servcheck/includes/test_mqtt.php'); - $results = mqtt_try($test); +function servcheck_run_test($test, $force) { + global $config; - break; - case 'dns': - require_once($config['base_path'] . '/plugins/servcheck/includes/test_dns.php'); + $usage_start = getrusage(); - if ($service == 'doh') { - $results = doh_try($test); - } else { - $results = dns_try($test); - } + $cert_expiry_days = read_config_option('servcheck_certificate_expiry_days'); + $test['new_notify_expire'] = false; - break; - case 'rest': - require_once($config['base_path'] . '/plugins/servcheck/includes/test_restapi.php'); - $results = restapi_try($test); + $poller = db_fetch_cell_prepared('SELECT * FROM poller WHERE id = ?', + [$test['poller_id']]); - break; - case 'snmp': - require_once($config['base_path'] . '/plugins/servcheck/includes/test_snmp.php'); - $results = snmp_try($test); + if ($poller == false) { + print 'Selected poller not found, changing to poller 1' . PHP_EOL; - break; - case 'ssh': - require_once($config['base_path'] . '/plugins/servcheck/includes/test_ssh.php'); - $results = ssh_try($test); + db_execute_prepared('UPDATE plugin_servcheck_test + SET poller_id = 1 + WHERE id = ?', + [$test['poller_id']]); + } - break; - case 'ftp': - require_once($config['base_path'] . '/plugins/servcheck/includes/test_ftp.php'); - $results = ftp_try($test); + $logs = db_fetch_cell_prepared('SELECT COUNT(*) + FROM plugin_servcheck_log + WHERE test_id = ?', + [$test['id']]); - break; + if ($logs > 0 && $test['next_run'] > time() && !$force) { + servcheck_debug('INFO: Test "' . $test['name'] . '" skipped. Not the right time to run the test.'); + + return false; } - $results['duration'] = round(microtime(true) - $results['start'], 4); + if (api_plugin_is_enabled('maint')) { + require_once($config['base_path'] . '/plugins/maint/functions.php'); + } - if ($results['result'] == 'ok') { - break; - } else { - servcheck_debug('Attempt ' . $x . ' was unsuccessful'); - servcheck_debug('Result: ' . clean_up_lines(var_export($results, true))); + if (function_exists('plugin_maint_check_servcheck_test')) { + if (plugin_maint_check_servcheck_test($test['id'])) { + servcheck_debug('Maintenance schedule active, skipped.'); + + return false; + } } - servcheck_debug('Sleeping 1 second'); + $test['days_left'] = 0; + $test['duration'] = false; - sleep(1); -} + // try it three times. First valid result skips next attempts + $x = 0; + $results = []; -$results['x'] = $x; + [$category, $service] = explode('_', $test['type']); -if (cacti_sizeof($results) == 0) { - servcheck_debug('Unknown error for test ' . $test['id']); + servcheck_debug('Category: ' . $category); + servcheck_debug('Service: ' . $service); - unregister_process('servcheck', "child:$poller_id", $test_id); + while ($x < $test['attempt']) { + $x++; - exit('Unknown error for test ' . $test['id']); -} + servcheck_debug('Service Check attempt ' . $x); -$results['time'] = time(); -$test['expiry_date'] = null; + if (!function_exists('curl_init') && ($category == 'web' || $category == 'smb' || $category == 'ldap' || + $category == 'ftp' || $category == 'mqtt' || $category == 'rest' || $service == 'doh')) { + print 'FATAL: You must install php-curl to use this test' . PHP_EOL; -if ($results['result'] == 'ok' && $test['certexpirenotify']) { - if (isset($results['options']['certinfo'][0])) { // curl - servcheck_debug('Returned certificate info: ' . clean_up_lines(var_export($results['options']['certinfo'], true))); - $parsed = date_parse_from_format('M j H:i:s Y e', $results['options']['certinfo'][0]['Expire date']); - // Prepare to retrieve the local expiry date of certificate instead of UTC date - $dt = new DateTime( - "{$parsed['year']}-{$parsed['month']}-{$parsed['day']} {$parsed['hour']}:{$parsed['minute']}:{$parsed['second']}", - new DateTimeZone('UTC') - ); + servcheck_debug('Test ' . $test['id'] . ' requires php-curl library'); - $local_tz = date_default_timezone_get(); + $results['result'] = 'error'; + $results['curl'] = false; + $results['error'] = 'missing php-curl library'; + $results['result_search'] = 'not tested'; + $results['start'] = microtime(true); + $results['duration'] = 0; + $results['data'] = ''; - if (empty($local_tz)) { - $local_tz = 'UTC'; + continue; } - $dt->setTimezone(new DateTimeZone($local_tz)); - $exp = $dt->getTimestamp(); - $test['days_left'] = round(($exp - time()) / 86400,1); - $test['expiry_date'] = date(date_time_format(), $exp); - } elseif (isset($results['cert_valid_to'])) { - // only for log - $test['days_left'] = round(($results['cert_valid_to'] - time()) / 86400,1); - $test['expiry_date'] = date(date_time_format(), $results['cert_valid_to']); - } -} + switch ($category) { + case 'web': + case 'ldap': + case 'smb': + require_once($config['base_path'] . '/plugins/servcheck/includes/test_curl.php'); + $results = curl_try($test); -$last_log = db_fetch_row_prepared('SELECT * - FROM plugin_servcheck_log - WHERE test_id = ? ORDER BY id DESC LIMIT 1', - [$test['id']]); + break; + case 'mail': + require_once($config['base_path'] . '/plugins/servcheck/includes/test_mail.php'); + $results = mail_try($test); -if (!$last_log) { - $last_log['result'] = 'not yet'; - $last_log['result_search'] = 'not yet'; -} + break; + case 'mqtt': + require_once($config['base_path'] . '/plugins/servcheck/includes/test_mqtt.php'); + $results = mqtt_try($test); -if ($results['result'] == 'ok') { - $test['stats_ok'] += 1; -} else { - $test['stats_bad'] += 1; -} + break; + case 'dns': + require_once($config['base_path'] . '/plugins/servcheck/includes/test_dns.php'); -$test['notify_result'] = false; -$test['notify_search'] = false; -$test['notify_duration'] = false; -$test['notify_certificate'] = false; + if ($service == 'doh') { + $results = doh_try($test); + } else { + $results = dns_try($test); + } -servcheck_debug('Checking for triggerers'); + break; + case 'rest': + require_once($config['base_path'] . '/plugins/servcheck/includes/test_restapi.php'); + $results = restapi_try($test); -if ($last_log['result'] != $results['result'] || $results['result'] != 'ok') { - servcheck_debug('Result changed, notification will be send'); + break; + case 'snmp': + require_once($config['base_path'] . '/plugins/servcheck/includes/test_snmp.php'); + $results = snmp_try($test); - if ($results['result'] != 'ok') { - $test['failures']++; + break; + case 'ssh': + require_once($config['base_path'] . '/plugins/servcheck/includes/test_ssh.php'); + $results = ssh_try($test); - if ($test['failures'] >= $test['downtrigger'] && $test['triggered'] == 0) { - $test['notify_result'] = true; - $test['triggered'] = 1; + break; + case 'ftp': + require_once($config['base_path'] . '/plugins/servcheck/includes/test_ftp.php'); + $results = ftp_try($test); + + break; } - } - if ($results['result'] == 'ok') { - if ($test['triggered'] == 1) { - $test['notify_result'] = true; + $results['duration'] = round(microtime(true) - $results['start'], 4); + + if ($results['result'] == 'ok') { + break; + } else { + servcheck_debug('Attempt ' . $x . ' was unsuccessful'); + servcheck_debug('Result: ' . clean_up_lines(var_export($results, true))); } - $test['triggered'] = 0; - $test['failures'] = 0; + servcheck_debug('Sleeping 1 second'); + + sleep(1); } -} else { // only for stats, without notification - if ($results['result'] != 'ok') { - $test['failures']++; - if ($test['failures'] >= $test['downtrigger']) { - $test['triggered'] = 1; - } - } else { - $test['triggered'] = 0; - $test['failures'] = 0; + $results['x'] = $x; + + if (cacti_sizeof($results) == 0) { + servcheck_debug('Unknown error for test ' . $test['id']); + + return false; } -} -// checks only if test passed or some search string exists -if ($results['result_search'] != 'not tested' && $results['result'] == 'ok') { - if ($last_log['result_search'] != $results['result_search']) { - servcheck_debug('Search result changed, notification will be send'); - $test['notify_search'] = true; + $results['time'] = time(); + $test['expiry_date'] = null; + + if ($results['result'] == 'ok' && $test['certexpirenotify']) { + if (isset($results['options']['certinfo'][0])) { // curl + servcheck_debug('Returned certificate info: ' . clean_up_lines(var_export($results['options']['certinfo'], true))); + $parsed = date_parse_from_format('M j H:i:s Y e', $results['options']['certinfo'][0]['Expire date']); + // Prepare to retrieve the local expiry date of certificate instead of UTC date + $dt = new DateTime("{$parsed['year']}-{$parsed['month']}-{$parsed['day']} {$parsed['hour']}:{$parsed['minute']}:{$parsed['second']}", + new DateTimeZone('UTC') + ); + + $local_tz = date_default_timezone_get(); + + if (empty($local_tz)) { + $local_tz = 'UTC'; + } + + $dt->setTimezone(new DateTimeZone($local_tz)); + $exp = $dt->getTimestamp(); + $test['days_left'] = round(($exp - time()) / 86400,1); + $test['expiry_date'] = date(date_time_format(), $exp); + } elseif (isset($results['cert_valid_to'])) { + // only for log + $test['days_left'] = round(($results['cert_valid_to'] - time()) / 86400,1); + $test['expiry_date'] = date(date_time_format(), $results['cert_valid_to']); + } } -} -// check certificate expiry -if ($test['certexpirenotify'] && $cert_expiry_days > 0 && $test['days_left'] < $cert_expiry_days && $results['result'] == 'ok') { - // notify once per day - $new_notify = db_fetch_cell_prepared('SELECT UNIX_TIMESTAMP(DATE_ADD(last_exp_notify, INTERVAL 1 DAY)) - FROM plugin_servcheck_test - WHERE id = ?', + $last_log = db_fetch_row_prepared('SELECT * + FROM plugin_servcheck_log + WHERE test_id = ? ORDER BY id DESC LIMIT 1', [$test['id']]); - if ($new_notify < time()) { - servcheck_debug('Certificate will expire soon (or is expired), will notify about expiration'); - $test['notify_certificate'] = true; - $test['certificate_state'] = 'ko'; - $new_notify_expire = true; + if (!$last_log) { + $last_log['result'] = 'not yet'; + $last_log['result_search'] = 'not yet'; + } + + if ($results['result'] == 'ok') { + $test['stats_ok'] += 1; + } else { + $test['stats_bad'] += 1; } -} -// check renewed cert -if ($test['certexpirenotify'] && $results['result'] == 'ok') { - if (isset($last_log['cert_expire']) && - $last_log['cert_expire'] != '0000-00-00 00:00:00' && !is_null($last_log['cert_expire'])) { - $days_before = round((strtotime($last_log['cert_expire']) - strtotime($last_log['last_check'])) / 86400,1); + $test['notify_result'] = false; + $test['notify_search'] = false; + $test['notify_duration'] = false; + $test['notify_certificate'] = false; - if ($test['days_left'] > 0 && $test['days_left'] > $days_before) { - if (!servcheck_summer_time_changed()) { - servcheck_debug('Renewed or changed certificate, notification will be send'); + servcheck_debug('Checking for triggerers'); - $test['notify_certificate'] = true; - $test['certificate_state'] = 'new'; - } else { - servcheck_debug('Renewed or changed certificate, but summer/winter time change detected. Skipping notification.'); + if ($last_log['result'] != $results['result'] || $results['result'] != 'ok') { + servcheck_debug('Result changed, notification will be send'); + + if ($results['result'] != 'ok') { + $test['failures']++; + + if ($test['failures'] >= $test['downtrigger'] && $test['triggered'] == 0) { + $test['notify_result'] = true; + $test['triggered'] = 1; } } - } -} -// long duration -if ($test['duration_trigger'] > 0 && $test['duration_count'] > 0 && $results['result'] == 'ok') { - $test['durs'] = []; + if ($results['result'] == 'ok') { + if ($test['triggered'] == 1) { + $test['notify_result'] = true; + } - if ($results['duration'] > $test['duration_trigger']) { - $test['triggered_duration']++; + $test['triggered'] = 0; + $test['failures'] = 0; + } + } else { // only for stats, without notification + if ($results['result'] != 'ok') { + $test['failures']++; + + if ($test['failures'] >= $test['downtrigger']) { + $test['triggered'] = 1; + } + } else { + $test['triggered'] = 0; + $test['failures'] = 0; + } } - $test['durs'][] = $results['duration'] . ' (' . date('Y-m-d H:i:s', $results['time']) . ')'; + // checks only if test passed or some search string exists + if ($results['result_search'] != 'not tested' && $results['result'] == 'ok') { + if ($last_log['result_search'] != $results['result_search']) { + servcheck_debug('Search result changed, notification will be send'); + $test['notify_search'] = true; + } + } - if ($test['duration_count'] > 1) { - $durations = db_fetch_assoc_prepared('SELECT duration, last_check, result - FROM plugin_servcheck_log - WHERE test_id = ? - ORDER BY id DESC LIMIT ' . ($test['duration_count'] - 1), + // check certificate expiry + if ($test['certexpirenotify'] && $cert_expiry_days > 0 && $test['days_left'] < $cert_expiry_days && $results['result'] == 'ok') { + // notify once per day + $new_notify = db_fetch_cell_prepared('SELECT UNIX_TIMESTAMP(DATE_ADD(last_exp_notify, INTERVAL 1 DAY)) + FROM plugin_servcheck_test + WHERE id = ?', [$test['id']]); - foreach ($durations as $d) { - $test['durs'][] = $d['duration'] . ' (' . $d['last_check'] . ')'; + if ($new_notify < time()) { + servcheck_debug('Certificate will expire soon (or is expired), will notify about expiration'); + $test['notify_certificate'] = true; + $test['certificate_state'] = 'ko'; + $test['new_notify_expire'] = true; } } - if ($test['triggered_duration'] == $test['duration_count']) { - servcheck_debug('Long duration detected, sending notification'); + // check renewed cert + if ($test['certexpirenotify'] && $results['result'] == 'ok') { + if (isset($last_log['cert_expire']) && + $last_log['cert_expire'] != '0000-00-00 00:00:00' && !is_null($last_log['cert_expire'])) { + $days_before = round((strtotime($last_log['cert_expire']) - strtotime($last_log['last_check'])) / 86400,1); - $test['notify_duration'] = true; - $test['duration_state'] = 'ko'; - $test['triggered_duration']++; - } elseif ($test['triggered_duration'] > $test['duration_count']) { - servcheck_debug('Long duration issue continue'); + if ($test['days_left'] > 0 && $test['days_left'] > $days_before) { + if (!servcheck_summer_time_changed()) { + servcheck_debug('Renewed or changed certificate, notification will be send'); - $test['triggered_duration']++; + $test['notify_certificate'] = true; + $test['certificate_state'] = 'new'; + } else { + servcheck_debug('Renewed or changed certificate, but summer/winter time change detected. Skipping notification.'); + } + } + } } - if ($results['duration'] < $test['duration_trigger'] && $test['triggered_duration'] >= $test['duration_count']) { - servcheck_debug('Normal duration detected, sending notification'); + // long duration + if ($test['duration_trigger'] > 0 && $test['duration_count'] > 0 && $results['result'] == 'ok') { + $test['durs'] = []; - $test['notify_duration'] = true; - $test['duration_state'] = 'ok'; - $test['triggered_duration'] = 0; - } -} + if ($results['duration'] > $test['duration_trigger']) { + $test['triggered_duration']++; + } -if ($test['notify_result'] || $test['notify_search'] || $test['notify_duration'] || $test['notify_certificate']) { - if (read_config_option('servcheck_disable_notification') == 'on') { - cacti_log('Notifications are disabled, notification will not send for test ' . $test['name'], false, 'SERVCHECK'); - servcheck_debug('Notification disabled globally'); - } else { - if ($test['notify'] != '') { - servcheck_debug('Time to send email'); - plugin_servcheck_send_notification($results, $test, $last_log); - } else { - servcheck_debug('Time to send email, but email notification for this test is disabled'); + $test['durs'][] = $results['duration'] . ' (' . date('Y-m-d H:i:s', $results['time']) . ')'; + + if ($test['duration_count'] > 1) { + $durations = db_fetch_assoc_prepared('SELECT duration, last_check, result + FROM plugin_servcheck_log + WHERE test_id = ? + ORDER BY id DESC LIMIT ' . ($test['duration_count'] - 1), + [$test['id']]); + + foreach ($durations as $d) { + $test['durs'][] = $d['duration'] . ' (' . $d['last_check'] . ')'; + } } - } - $command = read_config_option('servcheck_change_command'); - $command_enable = read_config_option('servcheck_enable_scripts'); + if ($test['triggered_duration'] == $test['duration_count']) { + servcheck_debug('Long duration detected, sending notification'); - if ($command_enable && $command != '' && $test['run_script'] == 'on') { - servcheck_debug('Time to run command'); + $test['notify_duration'] = true; + $test['duration_state'] = 'ko'; + $test['triggered_duration']++; + } elseif ($test['triggered_duration'] > $test['duration_count']) { + servcheck_debug('Long duration issue continue'); - putenv('SERVCHECK_TEST_NAME=' . $test['name']); - putenv('SERVCHECK_EXTERNAL_ID=' . $test['external_id']); - putenv('SERVCHECK_TEST_TYPE=' . $test['type']); - putenv('SERVCHECK_POLLER=' . $test['poller_id']); - putenv('SERVCHECK_RESULT=' . $results['result']); - putenv('SERVCHECK_RESULT_SEARCH=' . $results['result_search']); - putenv('SERVCHECK_CERTIFICATE_EXPIRATION=' . (isset($test['expiry_date']) ? $test['expiry_date'] : 'Not tested or unknown ')); + $test['triggered_duration']++; + } - if (file_exists($command) && is_executable($command)) { - $output = []; - $return = 0; + if ($results['duration'] < $test['duration_trigger'] && $test['triggered_duration'] >= $test['duration_count']) { + servcheck_debug('Normal duration detected, sending notification'); - exec($command, $output, $return); + $test['notify_duration'] = true; + $test['duration_state'] = 'ok'; + $test['triggered_duration'] = 0; + } + } - cacti_log('Servcheck Command for test[' . $test['id'] . '] Command[' . $command . '] ExitStatus[' . $return . '] Output[' . implode(' ', $output) . ']', false, 'SERVCHECK'); + if ($test['notify_result'] || $test['notify_search'] || $test['notify_duration'] || $test['notify_certificate']) { + if (read_config_option('servcheck_disable_notification') == 'on') { + cacti_log('Notifications are disabled, notification will not send for test ' . $test['name'], false, 'SERVCHECK'); + servcheck_debug('Notification disabled globally'); } else { - cacti_log('WARNING: Servcheck Command for test[' . $test['id'] . '] Command[' . $command . '] Is either Not found or Not executable!', false, 'SERVCHECK'); + if ($test['notify'] != '') { + servcheck_debug('Time to send email'); + plugin_servcheck_send_notification($results, $test, $last_log); + } else { + servcheck_debug('Time to send email, but email notification for this test is disabled'); + } } - } -} else { - servcheck_debug('Nothing triggered'); -} -$rusage = getrusage(); -update_statistics($test, $results, $new_notify_expire, $rusage); + $command = read_config_option('servcheck_change_command'); + $command_enable = read_config_option('servcheck_enable_scripts'); -unregister_process('servcheck', "child:$poller_id", $test_id); + if ($command_enable && $command != '' && $test['run_script'] == 'on') { + servcheck_debug('Time to run command'); -$end = microtime(true); -$stats = sprintf('Time:%.2f, Stats:%s/%s, Down triggered:%s, Duration triggered:%s, Memory:%s MB, CPUuser:%.2f CPUsystem:%.2f', - $end - $start, - $test['stats_ok'], - $test['stats_bad'], - ($test['triggered'] == 0 ? 'No' : 'Yes'), - ($test['triggered_duration'] == 0 ? 'No' : 'Yes'), - memory_get_peak_usage(true) / 1024 / 1024, - $rusage['ru_utime.tv_sec'] + $rusage['ru_utime.tv_usec'] / 1E6, - $rusage['ru_stime.tv_sec'] + $rusage['ru_stime.tv_usec'] / 1E6); + putenv('SERVCHECK_TEST_NAME=' . $test['name']); + putenv('SERVCHECK_EXTERNAL_ID=' . $test['external_id']); + putenv('SERVCHECK_TEST_TYPE=' . $test['type']); + putenv('SERVCHECK_POLLER=' . $test['poller_id']); + putenv('SERVCHECK_RESULT=' . $results['result']); + putenv('SERVCHECK_RESULT_SEARCH=' . $results['result_search']); + putenv('SERVCHECK_CERTIFICATE_EXPIRATION=' . (isset($test['expiry_date']) ? $test['expiry_date'] : 'Not tested or unknown ')); -servcheck_debug($stats); + if (file_exists($command) && is_executable($command)) { + $output = []; + $return = 0; + + exec($command, $output, $return); + + cacti_log('Servcheck Command for test[' . $test['id'] . '] Command[' . $command . '] ExitStatus[' . $return . '] Output[' . implode(' ', $output) . ']', false, 'SERVCHECK'); + } else { + cacti_log('WARNING: Servcheck Command for test[' . $test['id'] . '] Command[' . $command . '] Is either Not found or Not executable!', false, 'SERVCHECK'); + } + } + } else { + servcheck_debug('Nothing triggered'); + } -function update_statistics(&$test, &$results, $new_notify_expire, $rusage) { servcheck_debug('Updating Statistics'); + $usage_end = getrusage(); + $user_cpu = ($usage_end['ru_utime.tv_sec'] + $usage_end['ru_utime.tv_usec']) - ($usage_start['ru_utime.tv_sec'] + $usage_start['ru_utime.tv_usec']); + $sys_cpu = ($usage_end['ru_stime.tv_sec'] + $usage_end['ru_stime.tv_usec']) - ($usage_start['ru_stime.tv_sec'] + $usage_start['ru_stime.tv_usec']); + if ($results['curl']) { if (!isset($results['curl_return'])) { $results['curl_return'] = 'N/A'; @@ -562,14 +640,14 @@ function update_statistics(&$test, &$results, $new_notify_expire, $rusage) { VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [$test['id'], $results['duration'], date('Y-m-d H:i:s', $results['time']), $save_exp, $results['result'], $results['error'], $results['result_search'], $curl, $results['x'], - $rusage['ru_utime.tv_sec'] + $rusage['ru_utime.tv_usec'] / 1E6, - $rusage['ru_stime.tv_sec'] + $rusage['ru_stime.tv_usec'] / 1E6, + $user_cpu / 1E6, + $sys_cpu / 1E6, memory_get_peak_usage(true) / 1024 / 1024, strlen($results['data']) ] ); - if ($new_notify_expire) { + if ($test['new_notify_expire']) { $exp_notify = date(date_time_format()); } else { $exp_notify = db_fetch_cell_prepared('SELECT last_exp_notify @@ -588,7 +666,7 @@ function update_statistics(&$test, &$results, $new_notify_expire, $rusage) { [$test['triggered'], $test['triggered_duration'], $test['failures'], date('Y-m-d H:i:s', $results['time']), $exp_notify, $test['stats_ok'], $test['stats_bad'], $results['data'], $results['duration'], $results['result'], $results['result_search'], $results['x'], $results['error'], - $rusage['ru_utime.tv_sec'] + $rusage['ru_utime.tv_usec'] / 1E6, $rusage['ru_stime.tv_sec'] + $rusage['ru_stime.tv_usec'] / 1E6, + $user_cpu / 1E6, $sys_cpu / 1E6, memory_get_peak_usage(true) / 1024 / 1024, $test['id'] ] @@ -606,12 +684,13 @@ function update_statistics(&$test, &$results, $new_notify_expire, $rusage) { } function plugin_servcheck_send_notification($results, $test, $last_log) { - global $httperrors, $cert_expiry_days; + global $httperrors; $notify_list = []; $notify_extra = []; $notify_account = []; - $local_tz = date_default_timezone_get(); + $cert_expiry_days = read_config_option('servcheck_certificate_expiry_days'); + $local_tz = date_default_timezone_get(); if (empty($local_tz)) { $local_tz = 'UTC'; @@ -836,14 +915,14 @@ function plugin_servcheck_send_email($to, $subject, $message) { * @return void */ function sig_handler($signo) { - global $force, $poller_id, $test_id, $taskname; + global $force, $poller_id, $process, $taskname; switch ($signo) { case SIGTERM: case SIGINT: cacti_log("WARNING: Service Check Poller 'master' is shutting down by signal!", false, 'SERVCHECK'); - unregister_process('servcheck', "child:$poller_id", $test_id, getmypid()); + unregister_process('servcheck', "child:$poller_id", $process, getmypid()); exit(1); default: @@ -873,9 +952,13 @@ function display_help() { display_version(); print PHP_EOL; - print 'usage: servcheck_process.php --id=N [--debug]' . PHP_EOL . PHP_EOL; + print 'usage: ' . PHP_EOL; + print 'servcheck_process.php --id=N [--debug] [--force]' . PHP_EOL . PHP_EOL; + print 'or: ' . PHP_EOL; + print 'servcheck_process.php --process=N [--debug] [--force]' . PHP_EOL . PHP_EOL; print 'This binary will run the Service check for the Servcheck plugin.' . PHP_EOL . PHP_EOL; - print '--id=N - The Test ID from the Servcheck database.' . PHP_EOL; - print '--force - Run even if the job is disabled or set to run less frequently than every poller cycle' . PHP_EOL; - print '--debug - Display verbose output during execution' . PHP_EOL . PHP_EOL; + print '--id=N - The Test ID from the Servcheck database.' . PHP_EOL; + print '--process=N - This process then processes (total number of tests/max. process) tests.' . PHP_EOL; + print '--force - Run even if the job is disabled or set to run less frequently than every poller cycle' . PHP_EOL; + print '--debug - Display verbose output during execution' . PHP_EOL . PHP_EOL; } diff --git a/servcheck_proxy.php b/servcheck_proxy.php index 142abbf..13f0819 100644 --- a/servcheck_proxy.php +++ b/servcheck_proxy.php @@ -44,7 +44,7 @@ break; case 'edit': top_header(); - data_proxy_edit(); + servcheck_data_edit(); bottom_footer(); break; @@ -172,7 +172,7 @@ function form_save() { exit; } -function data_proxy_edit() { +function servcheck_data_edit() { global $servcheck_proxy_fields; // ================= input validation ================= diff --git a/servcheck_test.php b/servcheck_test.php index a15c622..6ff202a 100644 --- a/servcheck_test.php +++ b/servcheck_test.php @@ -51,7 +51,7 @@ break; case 'edit': top_header(); - data_test_edit(); + servcheck_data_edit(); bottom_footer(); break; @@ -475,7 +475,7 @@ function purge_log_events($id) { raise_message('test_log_purged', __('The Service Check history was purged for %s', $name, 'servcheck'), MESSAGE_LEVEL_INFO); } -function data_test_edit() { +function servcheck_data_edit() { global $servcheck_test_fields, $service_types, $servcheck_help_test; // ================= input validation ================= @@ -937,14 +937,14 @@ function servcheck_show_history() { } if ($row['result'] == 'ok' && $row['result_search'] == 'not ok') { - $style = 'background-color: ' . $servcheck_states['warning']['color'] . ';'; + $style = 'servcheck_warning'; } elseif ($row['result'] == 'ok') { - $style = 'background-color: ' . $servcheck_states['ok']['color'] . ';'; + $style = 'servcheck_ok'; } else { - $style = 'background-color: ' . $servcheck_states['error']['color'] . ';'; + $style = 'servcheck_error'; } - print ""; + print ""; form_selectable_cell($row['last_check'], $row['id']); form_selectable_cell($row['name'], $row['id']); @@ -1147,21 +1147,21 @@ function data_list() { } if ($row['enabled'] == '') { - $style = 'background-color: ' . $servcheck_states['disabled']['color'] . ';'; + $style = 'servcheck_disabled'; } elseif ($row['last_result'] == 'ok' && $row['triggered_duration'] >= $row['duration_count']) { - $style = 'background-color: ' . $servcheck_states['duration']['color'] . ';'; + $style = 'servcheck_duration'; $long_dur = true; } elseif ($row['failures'] > 0 && $row['failures'] < $row['downtrigger']) { - $style = 'background-color: ' . $servcheck_states['failing']['color'] . ';'; + $style = 'servcheck_failing'; } elseif ($row['last_result'] == 'ok' && $row['last_result_search'] == 'not ok') { - $style = 'background-color: ' . $servcheck_states['warning']['color'] . ';'; + $style = 'servcheck_warning'; } elseif ($row['last_result'] == 'ok' && strtotime($row['last_check']) > 0) { - $style = 'background-color: ' . $servcheck_states['ok']['color'] . ';'; + $style = 'servcheck_ok'; } else { - $style = 'background-color: ' . $servcheck_states['error']['color'] . ';'; + $style = 'servcheck_error'; } - print ""; + print ""; print " @@ -1241,7 +1241,7 @@ function servcheck_show_last_data() { [get_filter_request_var('id')]); print '' . __('Last returned data of test', 'servcheck') . ' ' . html_escape($result['name']) . ':
'; - print '
' . html_escape($result['last_returned_data']) . '
'; + print '
' . html_escape($result['last_returned_data']) . '
'; } function servcheck_filter() { @@ -1254,7 +1254,7 @@ function servcheck_filter() { set_page_refresh($refresh); // When a row is selected, set the background-color as black and font color as white and when hovering over a row, the background is light grey - servcheck_print_selectable_row_css(); + // servcheck_print_selectable_row_css(); html_start_box(__('Servcheck Test Management', 'servcheck') , '100%', '', '3', 'center', htmlspecialchars(basename($_SERVER['PHP_SELF'])) . '?action=edit'); ?> @@ -1369,26 +1369,9 @@ function clearFilter() { html_end_box(); } -// Shared CSS for selectable table rows -function servcheck_print_selectable_row_css() { - print ''; -} - function servcheck_log_filter() { global $item_rows; - // When a row is selected, set the background-color as black and font color as white and when hovering over a row, the background is light grey - servcheck_print_selectable_row_css(); - html_start_box(__('Service Check History', 'servcheck') , '100%', '', '3', 'center', ''); ?> diff --git a/setup.php b/setup.php index e28c4d4..02eb236 100644 --- a/setup.php +++ b/setup.php @@ -28,6 +28,7 @@ function plugin_servcheck_install() { api_plugin_register_hook('servcheck', 'poller_bottom', 'plugin_servcheck_poller_bottom', 'setup.php'); api_plugin_register_hook('servcheck', 'replicate_out', 'servcheck_replicate_out', 'setup.php'); api_plugin_register_hook('servcheck', 'config_settings', 'servcheck_config_settings', 'setup.php'); + api_plugin_register_hook('servcheck', 'page_head', 'servcheck_page_head', 'setup.php'); api_plugin_register_realm('servcheck', 'servcheck_test.php,servcheck_restapi.php,servcheck_credential.php,servcheck_curl_code.php,servcheck_proxy.php,servcheck_ca.php', __('Service Check Admin', 'servcheck'), 1); @@ -702,5 +703,24 @@ function servcheck_config_settings() { '365' => __('%d year', 1, 'servcheck'), ] ], + 'servcheck_test_max_duration' => [ + 'friendly_name' => __('Maximum test duration in seconds', 'intropage'), + 'description' => __('The default value for tests where runtime testing is not enabled. If enabled, the max. duration is calculated as the duration threshold + 2 seconds.'), + 'method' => 'textbox', + 'max_length' => 2, + 'default' => '3', + ] ]; } + +function servcheck_page_head() { + global $config; + + $selectedTheme = get_selected_theme(); + + print ""; + + if (file_exists($config['base_path'] . '/plugins/servcheck/themes/' . $selectedTheme . '.css')) { + print ""; + } +} diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 0000000..132fa46 --- /dev/null +++ b/templates/README.md @@ -0,0 +1,10 @@ + +## How to install and use Plugin Servcheck template + +Copy file servcheck.xml to /path/to/cacti/resource/script_server/ + +Copy file ss_servcheck.php to /path/to/cacti/scripts/ + +Import cacti_data_query_servcheck_plugin.xml from Console -> Import/Export -> Import Templates + +Edit your cacti device and add Associated Data Query Plugin Servcheck \ No newline at end of file diff --git a/templates/cacti_data_query_servcheck_plugin.xml b/templates/cacti_data_query_servcheck_plugin.xml new file mode 100644 index 0000000..a96b920 --- /dev/null +++ b/templates/cacti_data_query_servcheck_plugin.xml @@ -0,0 +1,508 @@ + + + Servcheck plugin + Test result graphs + <path_cacti>/resource/script_server/servcheck.xml + hash_030103332111d8b54ac8ce939af87a7eac0c06 + + + hash_0001039ca59acaa7ac013e423d06d019e83ecc + Plugin servcheck test + + + result + hash_01010317fb3e01476bdbdc002b0ab2d4b804f3 + hash_0801036d4514586cb5f1cf1873b3bb3a3fca79 + + + duration + hash_01010317fb3e01476bdbdc002b0ab2d4b804f3 + hash_080103c020046831f9c020c998db16fa523add + + + attempt + hash_01010317fb3e01476bdbdc002b0ab2d4b804f3 + hash_0801033c32526b540ff2a720013c4f5d4825af + + + + + + + + + + + Get Script Server Data (Indexed) + 6 + + + + Index Type + + + + index_type + in + index_type + + + Index Value + + + + index_value + in + index_value + + + Output Type ID + + + + output_type + in + output_type + + + Output Value + on + + + + out + output + + + + + Plugin servcheck test + + + + + Servcheck - |query_servcheck_name| + + status/duration/attempt + + 3 + + 200 + + 700 + + 1000 + + + + on + + 2 + + + + + + + + U + + 0 + + + + + + + + + + + + + + + + 0 + + 0 + + 0 + + + + + + + + + + 0 + + 0 + + + + 7 + hash_0801033c32526b540ff2a720013c4f5d4825af + 3090C7 + FF + 1 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Attempt + + 1.00 + + 0 + 1 + + + 9 + hash_0801033c32526b540ff2a720013c4f5d4825af + 0 + FF + 4 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Cur: + + 1.00 + + 0 + 2 + + + 9 + hash_0801033c32526b540ff2a720013c4f5d4825af + 0 + FF + 1 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Avg: + + 1.00 + + 0 + 3 + + + 9 + hash_0801033c32526b540ff2a720013c4f5d4825af + 0 + FF + 3 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Max: + on + 1.00 + + 0 + 4 + + + 4 + hash_0801036d4514586cb5f1cf1873b3bb3a3fca79 + 000000 + FF + 1 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Result + + 1.00 + + 0 + 5 + + + 9 + hash_0801036d4514586cb5f1cf1873b3bb3a3fca79 + 0 + FF + 4 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Cur: + + 1.00 + + 0 + 6 + + + 9 + hash_0801036d4514586cb5f1cf1873b3bb3a3fca79 + 0 + FF + 1 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Avg: + + 1.00 + + 0 + 7 + + + 9 + hash_0801036d4514586cb5f1cf1873b3bb3a3fca79 + 0 + FF + 3 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Max: + on + 1.00 + + 0 + 8 + + + 7 + hash_080103c020046831f9c020c998db16fa523add + F88017 + FF + 1 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Duration + + 1.00 + + 0 + 9 + + + 9 + hash_080103c020046831f9c020c998db16fa523add + 0 + FF + 4 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Cur: + + 1.00 + + 0 + 10 + + + 9 + hash_080103c020046831f9c020c998db16fa523add + 0 + FF + 1 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Avg: + + 1.00 + + 0 + 11 + + + 9 + hash_080103c020046831f9c020c998db16fa523add + 0 + FF + 3 + 0 + 0 + + + hash_060103e9c43831e54eca8069317a2ce8c6f751 + + Max: + on + 1.00 + + 0 + 12 + + + + + Data Source [attempt] + + task_item_id + hash_000103ec683d0cb195fd3444c9b1c80b6bf9a6|hash_0001039d02a6b567b56833fe0fc7c683cfe0e6|hash_00010306ae72f5066b11c6063dd9f8aa8f6e4f|hash_0001033bfee14bfc566855e0f7a0b886a713ad + + + Data Source [result] + + task_item_id + hash_00010329624ca529da4696eb12a6097fd715ba|hash_0001038c01cf13eda2aa9bb29ed7f0a6f1deb1|hash_000103998f7dd8bf0200c6fb00d170dd6a4d9a|hash_000103dbc3fdef66a69c1f0c3d3211ebd46eed + + + Data Source [duration] + + task_item_id + hash_0001038d6e2c3ffdb0c960e1f6a44f24c22e8b|hash_0001039f2676c9be786ae46fdd5bb531e9fdec|hash_0001032582b0b7a3c9ed6e543da962543112f1|hash_000103f2c87914348840926440532de468b960 + + + + + Plugin Servcheck - test result + + + Servcheck - |query_servcheck_name| + hash_030103332111d8b54ac8ce939af87a7eac0c06 + + hash_2001039762df759b20fa02e03e4353e08f627c + + 300 + + on + + + + + result + + 0 + + U + + 1 + + 600 + + 0 + + + + duration + + 0 + + U + + 1 + + 600 + + 0 + + + + attempt + + 0 + + U + + 1 + + 600 + + 0 + + + + + hash_070103172b4b0eacee4948c6479f587b62e512 + on + + + + hash_07010330fb5d5bcf3d66bb5abe88596f357c26 + on + + + + hash_07010331112c85ae4ff821d3b288336288818c + on + + + + + + 5 Minute Collection + 300 + 600 + 0.5 + on + 1|3 + + + Daily (5 Minute Average) + 1 + 600 + 172800 + + + Weekly (30 Minute Average) + 6 + 700 + 1209600 + + + Monthly (2 Hour Average) + 24 + 775 + 5237568 + + + Yearly (1 Day Average) + 288 + 797 + 63072000 + + + + + Normal + %8.2lf %s + + \ No newline at end of file diff --git a/templates/servcheck.xml b/templates/servcheck.xml new file mode 100644 index 0000000..660f71a --- /dev/null +++ b/templates/servcheck.xml @@ -0,0 +1,42 @@ + + Servcheck Data Query + |path_cacti|/scripts/ss_servcheck.php + ss_servcheck + php + + index + query + get + ! + servcheck_id + numeric + Check#|chosen_order_field| + + + + Test Id + input + servcheck_id + + + Test Name + input + servcheck_name + + + Attemp + output + attempt + + + Duration + output + duration + + + Result + output + result + + + \ No newline at end of file diff --git a/templates/ss_servcheck.php b/templates/ss_servcheck.php new file mode 100755 index 0000000..48302b5 --- /dev/null +++ b/templates/ss_servcheck.php @@ -0,0 +1,106 @@ +#!/usr/bin/env php +