mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
* style(php): wrap CLI and poller calls without changing execution Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(ci): review security inventories after poller wrapping Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(boost): compare handoff source contracts by PHP tokens Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * style(php): wrap poller constant strings without changing values Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * style(php): wrap poller concatenations with token-preserving source contracts Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(cli): assert CDEF repair queries after constant folding Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: verify PHP formatting preserves parsed behavior Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
1194 lines
37 KiB
PHP
1194 lines
37 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
| |
|
|
| This program is free software; you can redistribute it and/or |
|
|
| modify it under the terms of the GNU General Public License |
|
|
| as published by the Free Software Foundation; either version 2 |
|
|
| of the License, or (at your option) any later version. |
|
|
| |
|
|
| This program is distributed in the hope that it will be useful, |
|
|
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
| GNU General Public License for more details. |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
| This code is designed, written, and maintained by the Cacti Group. See |
|
|
| about.php and/or the AUTHORS file for specific developer information. |
|
|
+-------------------------------------------------------------------------+
|
|
| http://www.cacti.net/ |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
require('./include/auth.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/poller.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/CactiValidator.php');
|
|
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
|
|
// performing a full sync can take a lot of memory and time
|
|
ini_set('memory_limit', '-1');
|
|
ini_set('max_execution_time', '900');
|
|
|
|
global $logfile_verbosity, $poller_sync_intervals;
|
|
global $database_default, $database_username, $database_password, $database_port;
|
|
global $database_retries, $database_ssl, $database_ssl_key, $database_ssl_cert;
|
|
global $database_ssl_ca, $database_ssl_capath, $database_ssl_verify_server_cert;
|
|
|
|
$actions = [
|
|
POLLER_DELETE => __('Delete'),
|
|
POLLER_DISABLE => __('Disable'),
|
|
POLLER_ENABLE => __('Enable'),
|
|
POLLER_CLEAR_STATS => __('Clear Statistics'),
|
|
];
|
|
|
|
if (POLLER_ID == 1) {
|
|
$actions += [POLLER_RESYNC => __('Full Sync')];
|
|
$actions += [POLLER_AUTHSYNC => __('Auth Sync')];
|
|
}
|
|
|
|
$poller_status = [
|
|
POLLER_STATUS_NEW => '<div class="deviceUnknown">' . __('New/Idle') . '</div>',
|
|
POLLER_STATUS_RUNNING => '<div class="deviceUp">' . __('Running') . '</div>',
|
|
POLLER_STATUS_IDLE => '<div class="deviceRecovering">' . __('Idle') . '</div>',
|
|
POLLER_STATUS_DOWN => '<div class="deviceDown">' . __('Down') . '</div>',
|
|
POLLER_STATUS_DISABLED => '<div class="deviceDisabled">' . __('Disabled') . '</div>',
|
|
POLLER_STATUS_RECOVERING => '<div class="deviceDown">' . __('Recovering') . '</div>',
|
|
POLLER_STATUS_HEARTBEAT => '<div class="deviceDown">' . __('Heartbeat') . '</div>',
|
|
];
|
|
|
|
$logfile_verbosity = array_merge([-1 => __('Use Cacti Log Level')], $logfile_verbosity);
|
|
|
|
// file: pollers.php, action: edit
|
|
$fields_poller_edit = [
|
|
'spacer0' => [
|
|
'method' => 'spacer',
|
|
'friendly_name' => __('Data Collector Information'),
|
|
],
|
|
'name' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Name'),
|
|
'description' => __('The primary name for this Data Collector.'),
|
|
'value' => '|arg1:name|',
|
|
'size' => '50',
|
|
'default' => __('New Data Collector'),
|
|
'max_length' => '100'
|
|
],
|
|
'hostname' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Data Collector Hostname'),
|
|
'description' => __('The hostname for Data Collector. It may have to be a Fully Qualified ' .
|
|
'Domain name for the remote Pollers to contact it for activities such ' .
|
|
'as re-indexing, Real-time graphing, etc.'),
|
|
'value' => '|arg1:hostname|',
|
|
'size' => '50',
|
|
'default' => '',
|
|
'max_length' => '100'
|
|
],
|
|
'log_level' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Custom Log Level'),
|
|
'description' => __('In Cases where you need to perform debugging for a single Data ' .
|
|
'Collector Only, you can change it\'s log level here.'),
|
|
'value' => '|arg1:log_level|',
|
|
'default' => '-1',
|
|
'array' => $logfile_verbosity,
|
|
],
|
|
'timezone' => [
|
|
'method' => 'drop_callback',
|
|
'friendly_name' => __('TimeZone'),
|
|
'description' => __('The TimeZone for the Data Collector.'),
|
|
'sql' => 'SELECT Name AS id, Name AS name FROM mysql.time_zone_name ORDER BY name',
|
|
'action' => 'ajax_tz',
|
|
'id' => '|arg1:timezone|',
|
|
'value' => '|arg1:timezone|'
|
|
],
|
|
'notes' => [
|
|
'method' => 'textarea',
|
|
'friendly_name' => __('Notes'),
|
|
'description' => __('Notes for this Data Collectors Database.'),
|
|
'value' => '|arg1:notes|',
|
|
'textarea_rows' => 4,
|
|
'textarea_cols' => 50
|
|
],
|
|
'spacer_collection' => [
|
|
'method' => 'spacer',
|
|
'friendly_name' => __('Collection Settings'),
|
|
],
|
|
'processes' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Processes'),
|
|
'description' => __('The number of Data Collector processes to use to spawn.'),
|
|
'value' => '|arg1:processes|',
|
|
'size' => '10',
|
|
'default' => read_config_option('concurrent_processes'),
|
|
'max_length' => '4'
|
|
],
|
|
'threads' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Threads'),
|
|
'description' => __('The number of Spine Threads to use per Data Collector process.'),
|
|
'value' => '|arg1:threads|',
|
|
'size' => '10',
|
|
'default' => read_config_option('max_threads'),
|
|
'max_length' => '4'
|
|
],
|
|
'sync_interval' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Sync Interval'),
|
|
'description' => __('The polling sync interval in use. This setting will affect how often ' .
|
|
'this poller is checked and updated.'),
|
|
'value' => '|arg1:sync_interval|',
|
|
'default' => read_config_option('poller_sync_interval'),
|
|
'array' => $poller_sync_intervals,
|
|
],
|
|
'spacer_remotedb' => [
|
|
'method' => 'spacer',
|
|
'friendly_name' => __('Remote Database Connection'),
|
|
],
|
|
'dbhost' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Hostname'),
|
|
'description' => __('The hostname for the remote database server.'),
|
|
'value' => '|arg1:dbhost|',
|
|
'size' => '50',
|
|
'default' => '',
|
|
'max_length' => '100'
|
|
],
|
|
'dbdefault' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Remote Database Name'),
|
|
'description' => __('The name of the remote database.'),
|
|
'value' => '|arg1:dbdefault|',
|
|
'size' => '20',
|
|
'default' => $database_default,
|
|
'max_length' => '20'
|
|
],
|
|
'dbuser' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Remote Database User'),
|
|
'description' => __('The user name to use to connect to the remote database.'),
|
|
'value' => '|arg1:dbuser|',
|
|
'size' => '20',
|
|
'default' => $database_username,
|
|
'max_length' => '20'
|
|
],
|
|
'dbpass' => [
|
|
'method' => 'textbox_password',
|
|
'friendly_name' => __('Remote Database Password'),
|
|
'description' => __('The user password to use to connect to the remote database.'),
|
|
'value' => '|arg1:dbpass|',
|
|
'size' => '40',
|
|
'default' => $database_password,
|
|
'max_length' => '64'
|
|
],
|
|
'dbport' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Remote Database Port'),
|
|
'description' => __('The TCP port to use to connect to the remote database.'),
|
|
'value' => '|arg1:dbport|',
|
|
'size' => '5',
|
|
'default' => $database_port,
|
|
'max_length' => '5'
|
|
],
|
|
'dbretries' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Remote Database Retries'),
|
|
'description' => __('The number of times to attempt to retry to connect to the remote database.'),
|
|
'value' => '|arg1:dbretries|',
|
|
'size' => '5',
|
|
'default' => $database_retries,
|
|
'max_length' => '5'
|
|
],
|
|
'spacerssl' => [
|
|
'method' => 'spacer',
|
|
'friendly_name' => __('Remote Database SSL Information'),
|
|
'description' => __('Starting in MariaDB 11.4 and above, SSL is autonegotiated between the ' .
|
|
'client and server. So, this option may not be required')
|
|
],
|
|
'dbssl' => [
|
|
'method' => 'checkbox',
|
|
'friendly_name' => __('Remote Database SSL'),
|
|
'description' => __('If the remote database uses SSL to connect, and it\'s prior to ' .
|
|
'MariaDB 11.4, check the checkbox below to enter the details.'),
|
|
'value' => '|arg1:dbssl|',
|
|
'default' => $database_ssl ? 'on' : ''
|
|
],
|
|
'dbsslkey' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Remote Database SSL Key'),
|
|
'description' => __('The file holding the SSL Key to use to connect to the remote database.'),
|
|
'value' => '|arg1:dbsslkey|',
|
|
'size' => '50',
|
|
'default' => $database_ssl_key,
|
|
'max_length' => '255'
|
|
],
|
|
'dbsslcert' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Remote Database SSL Certificate'),
|
|
'description' => __('The file holding the SSL Certificate to use to connect to the remote database.'),
|
|
'value' => '|arg1:dbsslcert|',
|
|
'size' => '50',
|
|
'default' => $database_ssl_cert,
|
|
'max_length' => '255'
|
|
],
|
|
'dbsslca' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Remote Database SSL Authority'),
|
|
'description' => __('The file holding the SSL Certificate Authority to use to connect to ' .
|
|
'the remote database. This is an optional parameter that can be ' .
|
|
'required by the database provider if they have started SSL using the ' .
|
|
'--ssl-mode=VERIFY_CA option.'),
|
|
'value' => '|arg1:dbsslca|',
|
|
'size' => '50',
|
|
'default' => $database_ssl_ca,
|
|
'max_length' => '255'
|
|
],
|
|
'dbsslcapath' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Remote Database SSL Authorities directory'),
|
|
'description' => __('The file path to the directory that contains the trusted SSL ' .
|
|
'Certificate Authority certificates. This is an optional parameter ' .
|
|
'that can used instead of giving the path to an individual Certificate ' .
|
|
'Authority file. This parameter can be required by the database ' .
|
|
'provider if they have started SSL using the --ssl-mode=VERIFY_CA ' .
|
|
'option.'),
|
|
'value' => '|arg1:dbsslcapath|',
|
|
'size' => '50',
|
|
'default' => $database_ssl_capath,
|
|
'max_length' => '255'
|
|
],
|
|
'dbsslverifyservercert' => [
|
|
'method' => 'checkbox',
|
|
'friendly_name' => __('Remote Database SSL'),
|
|
'description' => __("Provides a way to disable verification of the server's SSL " .
|
|
"certificate Common Name against the server's hostname when " .
|
|
'connecting. This verification is enabled by default.'),
|
|
'value' => '|arg1:dbsslverifyservercert|',
|
|
'default' => $database_ssl_verify_server_cert ? 'on' : ''
|
|
],
|
|
'spacertest' => [
|
|
'method' => 'spacer',
|
|
'friendly_name' => __('Test Connection'),
|
|
],
|
|
'id' => [
|
|
'method' => 'hidden',
|
|
'value' => '|arg1:id|',
|
|
],
|
|
'save_component_poller' => [
|
|
'method' => 'hidden',
|
|
'value' => '1'
|
|
]
|
|
];
|
|
|
|
// set default action
|
|
set_default_action();
|
|
|
|
switch (grv('action')) {
|
|
case 'save':
|
|
form_save();
|
|
|
|
break;
|
|
case 'actions':
|
|
form_actions();
|
|
|
|
break;
|
|
case 'ajax_tz':
|
|
print json_encode(db_fetch_assoc_prepared('SELECT Name AS label, Name AS `value`
|
|
FROM mysql.time_zone_name
|
|
WHERE Name LIKE ?
|
|
ORDER BY Name
|
|
LIMIT ' . read_config_option('autocomplete_rows'),
|
|
['%' . gnrv('term') . '%']
|
|
));
|
|
|
|
break;
|
|
case 'ping':
|
|
test_database_connection();
|
|
|
|
break;
|
|
case 'edit':
|
|
top_header();
|
|
|
|
poller_edit();
|
|
|
|
bottom_footer();
|
|
|
|
break;
|
|
default:
|
|
top_header();
|
|
|
|
pollers();
|
|
|
|
bottom_footer();
|
|
|
|
break;
|
|
}
|
|
|
|
function form_save() : void {
|
|
if (isrv('save_component_poller')) {
|
|
// Common data
|
|
$save['id'] = gfrv('id');
|
|
$save['name'] = CactiValidator::validateInput(gnrv('name'), 'name', [new Assert\NotBlank()]);
|
|
$save['hostname'] = CactiValidator::validateInput(gnrv('hostname'), 'hostname', [new Assert\NotBlank()]);
|
|
$save['log_level'] = CactiValidator::validateInput(gnrv('log_level'), 'log_level', [new Assert\NotBlank()]);
|
|
$save['timezone'] = CactiValidator::validateInput(gnrv('timezone'), 'timezone', [new Assert\NotBlank()]);
|
|
$save['notes'] = CactiValidator::validateInput(gnrv('notes'), 'notes', []);
|
|
|
|
// Process settings
|
|
$save['processes'] = CactiValidator::validateInput(gnrv('processes'),
|
|
'processes',
|
|
[new Assert\NotBlank(),
|
|
new Assert\Regex(pattern: '/^[0-9]+$/')]);
|
|
$save['threads'] = CactiValidator::validateInput(gnrv('threads'),
|
|
'threads',
|
|
[new Assert\NotBlank(),
|
|
new Assert\Regex(pattern: '/^[0-9]+$/')]);
|
|
|
|
if ($save['id'] != 1) {
|
|
$save['sync_interval'] = CactiValidator::validateInput(gnrv('sync_interval'),
|
|
'sync_interval',
|
|
[new Assert\NotBlank(),
|
|
new Assert\Regex(pattern: '/^[0-9]+$/')]);
|
|
|
|
// Database settings
|
|
$save['dbdefault'] = CactiValidator::validateInput(gnrv('dbdefault'), 'dbdefault', []);
|
|
$save['dbhost'] = CactiValidator::validateInput(gnrv('dbhost'), 'dbhost', []);
|
|
$save['dbuser'] = CactiValidator::validateInput(gnrv('dbuser'), 'dbuser', []);
|
|
$save['dbpass'] = CactiValidator::validateInput(gnrv('dbpass'), 'dbpass', []);
|
|
$save['dbport'] = CactiValidator::validateInput(gnrv('dbport'),
|
|
'dbport',
|
|
[new Assert\Regex(pattern: '/^[0-9]+$/')]);
|
|
$save['dbretries'] = CactiValidator::validateInput(gnrv('dbretries'),
|
|
'dbretries',
|
|
[new Assert\Regex(pattern: '/^[0-9]+$/')]);
|
|
$save['dbssl'] = isrv('dbssl') ? 'on' : '';
|
|
$save['dbsslkey'] = CactiValidator::validateInput(gnrv('dbsslkey'), 'dbsslkey', []);
|
|
$save['dbsslcert'] = CactiValidator::validateInput(gnrv('dbsslcert'), 'dbsslcert', []);
|
|
$save['dbsslca'] = CactiValidator::validateInput(gnrv('dbsslca'), 'dbsslca', []);
|
|
$save['dbsslcapath'] = CactiValidator::validateInput(gnrv('dbsslcapath'), 'dbsslcapath', []);
|
|
$save['dbsslverifyservercert'] = isrv('dbsslverifyservercert') ? 'on' : '';
|
|
}
|
|
|
|
// Check for duplicate hostname
|
|
$error = false;
|
|
|
|
if (poller_check_duplicate_poller_id($save['id'], $save['hostname'], 'hostname')) {
|
|
raise_message('dupe_hostname',
|
|
__esc('You have already used this hostname \'%s\'. Please enter a non-duplicate hostname.',
|
|
$save['hostname']),
|
|
MESSAGE_LEVEL_ERROR);
|
|
$error = true;
|
|
}
|
|
|
|
if (isset($save['dbhost'])) {
|
|
if (poller_check_duplicate_poller_id($save['id'], $save['dbhost'], 'dbhost')) {
|
|
raise_message('dupe_dbhost',
|
|
__esc('You have already used this database hostname \'%s\'. Please enter a non-duplicate database hostname.',
|
|
$save['hostname']),
|
|
MESSAGE_LEVEL_ERROR);
|
|
$error = true;
|
|
}
|
|
}
|
|
|
|
if (isset($save['dbhost']) && $save['dbhost'] == 'localhost' && $save['id'] > 1) {
|
|
raise_message('poller_dbhost');
|
|
} elseif ($save['id'] > 1 && poller_host_duplicate($save['id'], $save['dbhost'])) {
|
|
raise_message('poller_nodupe');
|
|
} elseif (!is_error_message() && $error == false) {
|
|
$poller_id = sql_save($save, 'poller');
|
|
|
|
if ($poller_id) {
|
|
raise_message(1);
|
|
} else {
|
|
raise_message(2);
|
|
}
|
|
}
|
|
|
|
header('Location: pollers.php?action=edit&id=' . (empty($poller_id) ? gnrv('id') : $poller_id));
|
|
}
|
|
}
|
|
|
|
function poller_check_duplicate_poller_id(int $poller_id, string $hostname, string $column) : bool {
|
|
$ip_addresses = [];
|
|
$ip_hostnames = [];
|
|
|
|
if (is_ipaddress($hostname)) {
|
|
$address = @gethostbyaddr($hostname);
|
|
|
|
if ($address != $hostname) {
|
|
$ip_hostnames[$address] = $address;
|
|
} else {
|
|
$ip_addresses[$address] = $address;
|
|
}
|
|
|
|
$ip_addresses[$hostname] = $hostname;
|
|
} elseif (str_contains($hostname, '.')) {
|
|
$addresses = @dns_get_record($hostname);
|
|
$ip = @gethostbyname($hostname);
|
|
|
|
if ($ip != $hostname) {
|
|
$ip_addresses[$ip] = $ip;
|
|
}
|
|
|
|
$ip_hostnames[$hostname] = $hostname;
|
|
|
|
if (cacti_sizeof($addresses)) {
|
|
foreach (($addresses ?: []) as $address) {
|
|
if (isset($address['target'])) {
|
|
$ip_hostnames[$address['host']] = $address['host'];
|
|
}
|
|
|
|
if (isset($address['host'])) {
|
|
$ip_hostnames[$address['host']] = $address['host'];
|
|
}
|
|
|
|
if (isset($address['ip'])) {
|
|
$ip_addresses[$address['ip']] = $address['ip'];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$ip_hostname[$hostname] = $hostname;
|
|
|
|
$address = @gethostbyname($hostname);
|
|
|
|
if ($address != $hostname) {
|
|
$ip_addresses[$address] = $address;
|
|
}
|
|
}
|
|
|
|
$sql_where1 = '';
|
|
|
|
if (cacti_sizeof($ip_addresses)) {
|
|
$sql_where1 = "$column IN ('" . implode("','", $ip_addresses) . "')";
|
|
}
|
|
|
|
$sql_where2 = '';
|
|
|
|
if (cacti_sizeof($ip_hostnames)) {
|
|
foreach ($ip_hostnames as $host) {
|
|
$parts = explode('.', $host);
|
|
$sql_where2 .= ($sql_where2 != '' ? ' OR ' : ' (') .
|
|
"($column = " . db_qstr($parts[0]) .
|
|
" OR $column = " . db_qstr($host) . ')';
|
|
}
|
|
$sql_where2 .= ')';
|
|
}
|
|
|
|
if ($sql_where1 != '' || $sql_where2 != '') {
|
|
$sql_where = ' AND (' . $sql_where1 . ($sql_where1 != '' && $sql_where2 != '' ? ' OR ' : '') . $sql_where2 . ')';
|
|
} else {
|
|
$sql_where = '';
|
|
}
|
|
|
|
$duplicate = db_fetch_cell_prepared("SELECT id
|
|
FROM poller
|
|
WHERE id != ?
|
|
$sql_where",
|
|
[$poller_id]
|
|
);
|
|
|
|
if (empty($duplicate)) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function poller_host_duplicate(int $poller_id, string $host) : mixed {
|
|
if ($host == 'localhost') {
|
|
return true;
|
|
} else {
|
|
return db_fetch_cell_prepared('SELECT COUNT(*)
|
|
FROM poller
|
|
WHERE dbhost LIKE ?
|
|
AND id != ?',
|
|
[$host . '%', $poller_id]);
|
|
}
|
|
}
|
|
|
|
function form_actions() : void {
|
|
global $actions;
|
|
|
|
// ================= input validation =================
|
|
gfrv('drp_action', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^([a-zA-Z0-9_]+)$/']]);
|
|
// ====================================================
|
|
|
|
// if we are to save this form, instead of display it
|
|
if (isrv('selected_items')) {
|
|
$selected_items = sanitize_unserialize_selected_items(gnrv('selected_items'));
|
|
|
|
if ($selected_items != false) {
|
|
if (gnrv('drp_action') == POLLER_DELETE) { // delete
|
|
db_execute('DELETE FROM poller WHERE ' . array_to_sql_or($selected_items, 'id'));
|
|
db_execute('UPDATE host SET poller_id=1 WHERE deleted="" AND ' . array_to_sql_or($selected_items, 'poller_id'));
|
|
db_execute('UPDATE automation_networks SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
|
|
db_execute('UPDATE automation_processes SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
|
|
db_execute('UPDATE poller_command SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
|
|
db_execute('UPDATE poller_item SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
|
|
db_execute('UPDATE poller_output_realtime SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
|
|
db_execute('UPDATE poller_time SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
|
|
|
|
cacti_log('NOTE: The poller(s) with the id(s): ' . implode(',',
|
|
$selected_items) . ' deleted by user ' . $_SESSION[SESS_USER_ID],
|
|
false,
|
|
'WEBUI');
|
|
} elseif (grv('drp_action') == POLLER_DISABLE) { // disable
|
|
db_execute('UPDATE poller SET disabled="on" WHERE ' . array_to_sql_or($selected_items, 'id'));
|
|
|
|
cacti_log('NOTE: The poller(s) with the id(s): ' . implode(',',
|
|
$selected_items) . ' disabled by user ' . $_SESSION[SESS_USER_ID],
|
|
false,
|
|
'WEBUI');
|
|
} elseif (grv('drp_action') == POLLER_ENABLE) { // enable
|
|
db_execute('UPDATE poller SET disabled="" WHERE ' . array_to_sql_or($selected_items, 'id'));
|
|
|
|
cacti_log('NOTE: The poller(s) with the id(s): ' . implode(',',
|
|
$selected_items) . ' enabled by user ' . $_SESSION[SESS_USER_ID],
|
|
false,
|
|
'WEBUI');
|
|
} elseif (grv('drp_action') == POLLER_RESYNC || grv('drp_action') == POLLER_AUTHSYNC) { // full or auth sync
|
|
cacti_session_close();
|
|
|
|
if (grv('drp_action') == POLLER_RESYNC) {
|
|
$class = 'all';
|
|
} else {
|
|
$class = 'auth';
|
|
}
|
|
|
|
$success = [];
|
|
$failed = [];
|
|
$ids = [];
|
|
|
|
foreach ($selected_items as $item) {
|
|
// Operation not allowed on the main poller
|
|
if ($item == 1) {
|
|
continue;
|
|
}
|
|
|
|
$ids[] = $item;
|
|
|
|
$poller = db_fetch_row_prepared('SELECT *
|
|
FROM poller
|
|
WHERE id = ?',
|
|
[$item]
|
|
);
|
|
|
|
if ($poller['dbhost'] == 'localhost') {
|
|
raise_message('poller_dbhost');
|
|
|
|
continue;
|
|
}
|
|
|
|
if ($item == 1) {
|
|
raise_message('poller_nomain');
|
|
|
|
continue;
|
|
} else {
|
|
if (replicate_out($item, $class)) {
|
|
$success[] = $item;
|
|
|
|
if ($class == 'all') {
|
|
db_execute_prepared('UPDATE poller
|
|
SET last_sync = NOW()
|
|
WHERE id = ?',
|
|
[$item]
|
|
);
|
|
}
|
|
} else {
|
|
$failed[] = $item;
|
|
}
|
|
}
|
|
}
|
|
|
|
cacti_session_start();
|
|
|
|
if (cacti_sizeof($failed)) {
|
|
cacti_log('WARNING: Some Selected Remote Data Collectors in [' . implode(', ',
|
|
$ids) .
|
|
'] failed synchronization by user ' .
|
|
get_username($_SESSION[SESS_USER_ID]) .
|
|
', Successful/Failed[' .
|
|
cacti_sizeof($success) .
|
|
'/' .
|
|
cacti_sizeof($failed) .
|
|
']. See log for details.',
|
|
false,
|
|
'WEBUI');
|
|
} else {
|
|
cacti_log('NOTE: All Selected Remote Data Collectors in [' . implode(', ',
|
|
$ids) . '] synchronized correctly by user ' . get_username($_SESSION[SESS_USER_ID]),
|
|
false,
|
|
'WEBUI');
|
|
}
|
|
} elseif (grv('drp_action') == '5') { // clear statistics
|
|
foreach ($selected_items as $item) {
|
|
db_execute_prepared('UPDATE poller
|
|
SET total_time = 0, max_time = 0, min_time = 9999999, avg_time = 0, total_polls = 0
|
|
WHERE id = ?',
|
|
[$item]
|
|
);
|
|
}
|
|
|
|
raise_message('poller_clear', __('Data Collector Statistics cleared.'), MESSAGE_LEVEL_INFO);
|
|
}
|
|
}
|
|
|
|
header('Location: pollers.php');
|
|
|
|
exit;
|
|
} else {
|
|
$ilist = '';
|
|
$iarray = [];
|
|
|
|
// loop through each of the graphs selected on the previous page and get more info about them
|
|
foreach ($_POST as $var => $val) {
|
|
if (preg_match('/^chk_([0-9]+)$/', $var, $matches)) {
|
|
// ================= input validation =================
|
|
input_validate_input_number($matches[1], 'chk[1]');
|
|
// ====================================================
|
|
|
|
$ilist .= '<li>' . htmle(db_fetch_cell_prepared('SELECT name FROM poller WHERE id = ?', [$matches[1]])) . '</li>';
|
|
|
|
$iarray[] = $matches[1];
|
|
}
|
|
}
|
|
|
|
$form_data = [
|
|
'general' => [
|
|
'page' => 'pollers.php',
|
|
'actions' => $actions,
|
|
'optvar' => 'drp_action',
|
|
'item_array' => $iarray,
|
|
'item_list' => $ilist
|
|
],
|
|
'options' => [
|
|
POLLER_DELETE => [
|
|
'smessage' => __('Click \'Continue\' to Delete the following Data Collector.'),
|
|
'pmessage' => __('Click \'Continue\' to Delete the following Data Collectors.'),
|
|
'scont' => __('Delete Data Collector'),
|
|
'pcont' => __('Delete Data Collectors')
|
|
],
|
|
POLLER_DISABLE => [
|
|
'smessage' => __('Click \'Continue\' to Disable the following Data Collector.'),
|
|
'pmessage' => __('Click \'Continue\' to Disable the following Data Collectors.'),
|
|
'scont' => __('Disable Data Collector'),
|
|
'pcont' => __('Disable Data Collectors')
|
|
],
|
|
POLLER_ENABLE => [
|
|
'smessage' => __('Click \'Continue\' to Enable the following Data Collector.'),
|
|
'pmessage' => __('Click \'Continue\' to Enable the following Data Collectors.'),
|
|
'scont' => __('Enable Data Collector'),
|
|
'pcont' => __('Enable Data Collectors')
|
|
],
|
|
POLLER_RESYNC => [
|
|
'smessage' => __('Click \'Continue\' to Synchronize the Remote Data Collector for Offline Operation.'),
|
|
'pmessage' => __('Click \'Continue\' to Synchronize the Remote Data Collectors for Offline Operation.'),
|
|
'scont' => __('Resync Data Collector'),
|
|
'pcont' => __('Resync Data Collectors')
|
|
],
|
|
POLLER_AUTHSYNC => [
|
|
'smessage' => __('Click \'Continue\' to Synchronize the Authentication Data to Remote Data Collector.'),
|
|
'pmessage' => __('Click \'Continue\' to Synchronize the Authentication Data to Remote Data Collectors.'),
|
|
'scont' => __('Resync Auth Data to Collector'),
|
|
'pcont' => __('Resync Auth Data to Collectors')
|
|
],
|
|
POLLER_CLEAR_STATS => [
|
|
'smessage' => __('Click \'Continue\' to Clear Statistics for the following Data Collector.'),
|
|
'pmessage' => __('Click \'Continue\' to Clear Statistics for following Data Collectors.'),
|
|
'scont' => __('Resync Data Collector'),
|
|
'pcont' => __('Resync Data Collectors')
|
|
]
|
|
]
|
|
];
|
|
|
|
form_continue_confirmation($form_data);
|
|
}
|
|
}
|
|
|
|
function poller_edit() : void {
|
|
global $fields_poller_edit;
|
|
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
if (!ierv('id')) {
|
|
$poller = db_fetch_row_prepared('SELECT *
|
|
FROM poller
|
|
WHERE id = ?',
|
|
[grv('id')]
|
|
);
|
|
|
|
$header_label = __esc('Site [edit: %s]', $poller['name']);
|
|
} else {
|
|
$poller = [];
|
|
|
|
$header_label = __('Site [new]');
|
|
}
|
|
|
|
form_start('pollers.php', 'poller');
|
|
|
|
html_start_box($header_label, '100%', true, 3, 'center', '');
|
|
|
|
if (cacti_sizeof($poller)) {
|
|
if ($poller['id'] == 1) {
|
|
unset($fields_poller_edit['sync_interval']);
|
|
unset($fields_poller_edit['spacer_remotedb']);
|
|
unset($fields_poller_edit['dbdefault']);
|
|
unset($fields_poller_edit['dbhost']);
|
|
unset($fields_poller_edit['dbuser']);
|
|
unset($fields_poller_edit['dbpass']);
|
|
unset($fields_poller_edit['dbport']);
|
|
unset($fields_poller_edit['dbretries']);
|
|
unset($fields_poller_edit['spacerssl']);
|
|
unset($fields_poller_edit['dbssl']);
|
|
unset($fields_poller_edit['dbsslkey']);
|
|
unset($fields_poller_edit['dbsslcert']);
|
|
unset($fields_poller_edit['dbsslca']);
|
|
unset($fields_poller_edit['dbsslcapath']);
|
|
unset($fields_poller_edit['dbsslverifyservercert']);
|
|
|
|
$fields_poller_edit['log_level']['method'] = 'hidden';
|
|
}
|
|
|
|
if ($poller['timezone'] == '') {
|
|
$poller['timezone'] = ini_get('date.timezone');
|
|
}
|
|
}
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => inject_form_variables($fields_poller_edit, $poller)
|
|
]
|
|
);
|
|
|
|
$tip_text = __('Remote Data Collectors must be able to communicate to the Main Data ' .
|
|
'Collector, and vice versa. Use this button to verify that the Main ' .
|
|
'Data Collector can communicate to this Remote Data Collector.');
|
|
|
|
if (read_config_option('hide_form_description') == 'on') {
|
|
$tooltip = '<br><span class="formFieldDescription">' . $tip_text . '</span>';
|
|
} else {
|
|
$tooltip = '<div class="formTooltip">' . str_replace("\n", '', display_tooltip($tip_text)) . '</div>';
|
|
}
|
|
|
|
$row_html = ('<div class="formRow odd"><div class="formColumnLeft"><div ' .
|
|
'class="formFieldName">') .
|
|
__('Test Database Connection') .
|
|
$tooltip .
|
|
('</div></div><div class="formColumnRight"><button type="button" ' .
|
|
'class="ui-button ui-corner-all ui-widget" id="dbtest">') .
|
|
__esc('Test Connection') .
|
|
'</button><span id="results"></span></div></div>';
|
|
|
|
$pt = read_config_option('poller_type');
|
|
|
|
if (cacti_sizeof($poller)) {
|
|
if ($poller['id'] > 1) {
|
|
?>
|
|
<script type='text/javascript'>
|
|
pt = <?php print (int) $pt; ?>;
|
|
|
|
function showHideRemoteDB() {
|
|
var hasSSL = $('#dbssl').is(':checked');
|
|
toggleFields({
|
|
dbsslkey: hasSSL,
|
|
dbsslcert: hasSSL,
|
|
dbsslca: hasSSL,
|
|
dbsslcapath: hasSSL,
|
|
dbsslverifyservercert: hasSSL,
|
|
});
|
|
}
|
|
|
|
$(function() {
|
|
$('#row_spacertest').after('<?php print $row_html; ?>');
|
|
$('#dbssl').click(function() {
|
|
showHideRemoteDB();
|
|
});
|
|
|
|
$('#dbtest').click(function(e) {
|
|
e.preventDefault();
|
|
ping_database();
|
|
});
|
|
|
|
showHideRemoteDB();
|
|
|
|
if (pt == 1) {
|
|
$('#row_threads').hide();
|
|
}
|
|
});
|
|
|
|
function ping_database() {
|
|
dbssl = $('#dbssl').is(':checked') ? 'on' : '';
|
|
dbsslverifyservercert = $('#dbsslverifyservercert').is(':checked') ? 'on' : '';
|
|
|
|
var options = {
|
|
url: 'pollers.php',
|
|
funcEnd: 'pingDatabaseFinalize',
|
|
handle: false
|
|
};
|
|
|
|
var data = {
|
|
__csrf_magic: csrfMagicToken,
|
|
action: 'ping',
|
|
dbdefault: $('#dbdefault').val(),
|
|
dbhost: $('#dbhost').val(),
|
|
dbuser: $('#dbuser').val(),
|
|
dbpass: $('#dbpass').val(),
|
|
dbport: $('#dbport').val(),
|
|
dbretries: $('#dbretries').val(),
|
|
dbssl: dbssl,
|
|
dbsslkey: $('#dbsslkey').val(),
|
|
dbsslcert: $('#dbsslcert').val(),
|
|
dbsslca: $('#dbsslca').val(),
|
|
dbsslcapath: $('#dbsslcapath').val(),
|
|
dbsslverifyservercert: dbsslverifyservercert
|
|
};
|
|
|
|
postUrl(options, data);
|
|
}
|
|
|
|
function pingDatabaseFinalize(options, data) {
|
|
$('#results').empty().show().html(data).fadeOut(5000);
|
|
}
|
|
</script>
|
|
<?php
|
|
} else {
|
|
?>
|
|
<script type='text/javascript'>
|
|
pt = <?php print (int) $pt; ?>;
|
|
|
|
$(function() {
|
|
if (pt == 1) {
|
|
$('#row_threads').hide();
|
|
}
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
html_end_box(true, true);
|
|
|
|
$form_buttons = [];
|
|
|
|
if ($poller['id'] > 1) {
|
|
$form_buttons[] = [
|
|
'id' => 'delete',
|
|
'value' => __esc('Delete'),
|
|
'method' => 'post',
|
|
'url' => 'pollers.php',
|
|
'data' => json_encode(
|
|
[
|
|
'action' => 'actions',
|
|
'drp_action' => POLLER_DELETE,
|
|
'chk_' . $poller['id'] => 'on',
|
|
'__csrf_magic' => csrf_get_tokens(),
|
|
]
|
|
),
|
|
];
|
|
}
|
|
|
|
$form_buttons = array_merge($form_buttons, [
|
|
[
|
|
'id' => 'return',
|
|
'value' => __esc('Return'),
|
|
'url' => 'pollers.php',
|
|
'method' => 'return',
|
|
],
|
|
[
|
|
'id' => 'save',
|
|
'value' => __esc('Save'),
|
|
'type' => 'submit'
|
|
]
|
|
]);
|
|
|
|
form_save_buttons($form_buttons);
|
|
}
|
|
|
|
function test_database_connection(array $poller = []) : bool {
|
|
if (!cacti_sizeof($poller)) {
|
|
$poller['dbtype'] = 'mysql';
|
|
|
|
$fields = [
|
|
'dbhost',
|
|
'dbuser',
|
|
'dbpass',
|
|
'dbdefault',
|
|
'dbport',
|
|
'dbretries',
|
|
'dbssl',
|
|
'dbsslkey',
|
|
'dbsslcert',
|
|
'dbsslca',
|
|
'dbsslcapath',
|
|
'dbsslverifyservercert'
|
|
];
|
|
|
|
foreach ($fields as $field) {
|
|
if ($field == 'dbssl') {
|
|
if (isrv('dbssl') && gnrv('dbssl') == 'on') {
|
|
$poller['dbssl'] = 'on';
|
|
} else {
|
|
$poller['dbssl'] = '';
|
|
}
|
|
} elseif ($field == 'dbsslverifyservercert') {
|
|
if (isrv('dbsslverifyservercert') && gnrv('dbsslverifyservercert') == 'on') {
|
|
$poller['dbsslverifyservercert'] = 'on';
|
|
} else {
|
|
$poller['dbsslverifyservercert'] = '';
|
|
}
|
|
} elseif (isrv($field)) {
|
|
$poller[$field] = gnrv($field);
|
|
} else {
|
|
print 'ERROR: DB Connection Column ' . $field . ' Missing';
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
$connection = db_connect_real(
|
|
$poller['dbhost'],
|
|
$poller['dbuser'],
|
|
$poller['dbpass'],
|
|
$poller['dbdefault'],
|
|
$poller['dbtype'],
|
|
$poller['dbport'],
|
|
$poller['dbretries'],
|
|
$poller['dbssl'],
|
|
$poller['dbsslkey'],
|
|
$poller['dbsslcert'],
|
|
$poller['dbsslca'],
|
|
$poller['dbsslcapath'],
|
|
$poller['dbsslverifyservercert']
|
|
);
|
|
|
|
if (is_object($connection)) {
|
|
db_close($connection);
|
|
print ' <i class="ti ti-check"></i> ' . __('Connection Successful');
|
|
} else {
|
|
print ' <i class="ti ti-x"></i> ' . __('Connection Failed');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function pollers() : void {
|
|
global $actions, $poller_status, $item_rows;
|
|
|
|
// create the page filter
|
|
$pageFilter = new CactiTableFilter(__('Data Collectors'), 'pollers.php', 'form_poller', 'sess_pollers');
|
|
|
|
$pageFilter->rows_label = __('Collectors');
|
|
$pageFilter->has_refresh = true;
|
|
$pageFilter->def_refresh = 20;
|
|
$pageFilter->render();
|
|
|
|
if (grv('rows') == '-1') {
|
|
$rows = read_config_option('num_rows_table');
|
|
} else {
|
|
$rows = grv('rows');
|
|
}
|
|
|
|
// form the 'where' clause for our main sql query
|
|
if (grv('filter') != '') {
|
|
$sql_where = 'WHERE name LIKE ' . db_qstr('%' . grv('filter') . '%');
|
|
} else {
|
|
$sql_where = '';
|
|
}
|
|
|
|
$total_rows = db_fetch_cell("SELECT COUNT(*) FROM poller $sql_where");
|
|
|
|
$sql_order = get_order_string();
|
|
$sql_limit = ' LIMIT ' . ($rows * (grv('page') - 1)) . ',' . $rows;
|
|
|
|
$pollers = db_fetch_assoc("SELECT poller.*, UNIX_TIMESTAMP() - UNIX_TIMESTAMP(poller.last_status) as heartbeat, devices AS hosts
|
|
FROM poller
|
|
$sql_where
|
|
$sql_order
|
|
$sql_limit");
|
|
|
|
$nav = html_nav_bar('pollers.php?filter=' . grv('filter'),
|
|
MAX_DISPLAY_PAGES,
|
|
grv('page'),
|
|
$rows,
|
|
$total_rows,
|
|
5,
|
|
__('Pollers'),
|
|
'page',
|
|
'main');
|
|
|
|
form_start('pollers.php', 'chk');
|
|
|
|
print $nav;
|
|
|
|
html_start_box('', '100%', false, 3, 'center', '');
|
|
|
|
$display_text = [
|
|
'name' => [
|
|
'display' => __('Collector Name'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The Name of this Data Collector.')
|
|
],
|
|
'id' => [
|
|
'display' => __('ID'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The unique id associated with this Data Collector.')
|
|
],
|
|
'poller.hostname' => [
|
|
'display' => __('Hostname'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The Hostname where the Data Collector is running.')
|
|
],
|
|
'status' => [
|
|
'display' => __('Status'),
|
|
'align' => 'center',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The Status of this Data Collector.')
|
|
],
|
|
'nosort0' => [
|
|
'display' => __('Proc/Threads'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The Number of Poller Processes and Threads for this Data Collector.')
|
|
],
|
|
'total_time' => [
|
|
'display' => __('Polling Time'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The last data collection time for this Data Collector.')
|
|
],
|
|
'nosort1' => [
|
|
'display' => __('Avg/Max'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The Average and Maximum Collector timings for this Data Collector.')
|
|
],
|
|
'hosts' => [
|
|
'display' => __('Devices'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The number of Devices associated with this Data Collector.')
|
|
],
|
|
'snmp' => [
|
|
'display' => __('SNMP Gets'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The number of SNMP gets associated with this Collector.')
|
|
],
|
|
'script' => [
|
|
'display' => __('Scripts'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The number of script calls associated with this Data Collector.')
|
|
],
|
|
'server' => [
|
|
'display' => __('Servers'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The number of script server calls associated with this Data Collector.')
|
|
],
|
|
'last_update' => [
|
|
'display' => __('Last Finished'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The last time this Data Collector completed.')
|
|
],
|
|
'last_status' => [
|
|
'display' => __('Last Update'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The last time this Data Collector checked in with the main Cacti site.')
|
|
],
|
|
'last_sync' => [
|
|
'display' => __('Last Sync'),
|
|
'align' => 'right',
|
|
'sort' => 'DESC',
|
|
'tip' => __('The last time this Data Collector was full synced with main Cacti site.')
|
|
]
|
|
];
|
|
|
|
html_header_sort_checkbox($display_text, grv('sort_column'), grv('sort_direction'), false);
|
|
|
|
$i = 0;
|
|
|
|
if (cacti_sizeof($pollers)) {
|
|
foreach ($pollers as $poller) {
|
|
if ($poller['id'] == 1) {
|
|
$disabled = true;
|
|
} else {
|
|
$disabled = false;
|
|
}
|
|
|
|
if ($poller['disabled'] == 'on') {
|
|
$poller['status'] = 4;
|
|
} elseif ($poller['heartbeat'] > 310) {
|
|
$poller['status'] = 6;
|
|
}
|
|
|
|
$mma = round($poller['avg_time'] ?: 0, 2) . '/' . round(max($poller['max_time'] ?: 1, 1), 2);
|
|
|
|
if (empty($poller['name'])) {
|
|
$poller['name'] = '<no name>';
|
|
}
|
|
|
|
$pt = read_config_option('poller_type');
|
|
|
|
form_alternate_row('line' . $poller['id'], true, $disabled);
|
|
|
|
form_selectable_cell(filter_value($poller['name'],
|
|
grv('filter'),
|
|
'pollers.php?action=edit&id=' . $poller['id']),
|
|
$poller['id']);
|
|
form_selectable_cell($poller['id'], $poller['id'], '', 'right');
|
|
form_selectable_ecell($poller['hostname'], $poller['id'], '', 'right');
|
|
form_selectable_cell($poller_status[$poller['status']], $poller['id'], '', 'center');
|
|
form_selectable_cell($poller['processes'] . '/' . ($pt == 2 ? $poller['threads'] : '-'), $poller['id'], '', 'right');
|
|
form_selectable_cell(number_format_i18n($poller['total_time'], 2), $poller['id'], '', 'right');
|
|
form_selectable_cell($mma, $poller['id'], '', 'right');
|
|
form_selectable_cell(number_format_i18n($poller['hosts'], -1), $poller['id'], '', 'right');
|
|
form_selectable_cell(number_format_i18n($poller['snmp'], -1), $poller['id'], '', 'right');
|
|
form_selectable_cell(number_format_i18n($poller['script'], -1), $poller['id'], '', 'right');
|
|
form_selectable_cell(number_format_i18n($poller['server'], -1), $poller['id'], '', 'right');
|
|
form_selectable_cell(substr($poller['last_update'], 5), $poller['id'], '', 'right');
|
|
form_selectable_cell(substr($poller['last_status'], 5), $poller['id'], '', 'right');
|
|
|
|
if ($poller['id'] == 1) {
|
|
form_selectable_cell(__('N/A'), $poller['id'], '', 'right');
|
|
} else {
|
|
form_selectable_cell(substr($poller['last_sync'], 5), $poller['id'], '', 'right');
|
|
}
|
|
|
|
form_checkbox_cell($poller['name'], $poller['id'], $disabled);
|
|
|
|
form_end_row();
|
|
}
|
|
} else {
|
|
print "<tr class='tableRow odd'><td colspan='" .
|
|
(cacti_sizeof($display_text) + 1) .
|
|
"'><em>" .
|
|
__('No Data Collectors Found') .
|
|
"</em></td></tr>\n";
|
|
}
|
|
|
|
html_end_box(false);
|
|
|
|
if (cacti_sizeof($pollers)) {
|
|
print $nav;
|
|
}
|
|
|
|
// draw the dropdown containing a list of available actions for this form
|
|
draw_actions_dropdown($actions);
|
|
|
|
form_end();
|
|
}
|