mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-24 12:07:31 +09:00
* data_sources: render RRDtool Tune Info as balanced div boxes rrdtool_tune() emitted an unbalanced mix of html_header() and html_end_box() calls wrapped around raw table rows inside the single box opened by data_sources.php. The premature and extra closing tags corrupted the DOM, which (depending on theme) clipped the Save button at the bottom of the Data Source edit page while in Data Source Info mode. - Separate the execute, CLI and HTML code paths in rrdtool_tune() and, for HTML, render each section as a self contained, balanced div box. - Extract and rename the helper print_leaves() to the standalone rrdtool_tune_print_leaves(), matching the rrdtool_tune_* naming convention, and emit div rows instead of table rows. - Switch the data_sources.php wrapper box to div mode to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * data_debug: require POST for the Data Source repair actions Applying RRDfile repairs and rerunning the debugger are state changing operations, so they should not be reachable with a GET. Driving them through a GET also meant the follow up redirect was pulled into the content area as a full page, losing the theme and navigation on rerender. Register data_debug.php run_repair and run_debug in the cactiIsPostActionUrl() allowlist so loadUrl() upgrades them to a token-bearing POST, and add the matching csrf_require_post() guard so the browser transport and server side guard stay in sync. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * rrd: split rrdtool_tune into testable render and execute helpers Break rrdtool_tune() into small single-purpose helpers so each path can be exercised directly by unit tests (the web display path is otherwise unreachable under CACTI_CLI): - rrdtool_tune_print_leaves() takes an explicit $html flag instead of branching on CACTI_CLI, so both the div and plain-text branches are testable. - rrdtool_tune_execute() applies the tune/resize commands for a repair. - rrdtool_tune_print_cli() prints the source commands as plain text. - rrdtool_tune_print_html() renders the balanced div boxes. - rrdtool_tune() becomes a thin dispatcher over the three. Add tests/Unit/RrdTuneRenderTest.php covering the leaf printer, the HTML and CLI printers, the execute path and the dispatcher, asserting the output is balanced div markup with no table rows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * review: post Data Source Troubleshooter actions via cactiPostAction Address PR review feedback: - Switch the Apply Suggested Fixes and Rerun Analysis links to the existing cactiPostAction/data-url idiom (as data_sources.php already does for ds_repair) instead of upgrading a loadUrl() GET through the cactiIsPostActionUrl allowlist. The allowlist path replayed the query-free mutation URL into browser history, so a refresh reopened the list instead of the selected Data Source; a full cactiPostAction submission follows the server redirect to the read-only view URL and records that in history. The run_repair / run_debug csrf_require_post guards are retained, and the data_debug.php allowlist entry (and its now redundant click handlers) are removed. - Add a callable $executor test seam to rrdtool_tune_execute() (matching rrdtool_dump_document) so the unit tests assert the tune and resize commands are actually executed, with resize.rrd produced only when the resize command runs, rather than passing on the rename alone. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * review: move RrdTuneRenderTest into the Unit/Core/Rrd category folder Per AGENTS.md, new unit tests belong in a category subfolder rather than directly under tests/Unit. Move RrdTuneRenderTest there and update the UnitStubs include depth accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: TheWitness <copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2115 lines
68 KiB
PHP
2115 lines
68 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 . '/api_data_source.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/api_device.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/api_graph.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/data_query.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/html_form_template.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/poller.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/rrd.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/template.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/utility.php');
|
|
require_once(CACTI_PATH_LIBRARY . '/CactiValidator.php');
|
|
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
|
|
$actions = [
|
|
1 => __('Delete'),
|
|
2 => __('Disable'),
|
|
3 => __('Enable'),
|
|
4 => __('Change Device'),
|
|
5 => __('Reapply Suggested Names'),
|
|
6 => __('Change Data Source Profile')
|
|
];
|
|
|
|
$actions = api_plugin_hook_function('data_source_action_array', $actions);
|
|
|
|
// set default action
|
|
set_default_action();
|
|
|
|
switch (grv('action')) {
|
|
case 'save':
|
|
form_save();
|
|
|
|
break;
|
|
case 'actions':
|
|
form_actions();
|
|
|
|
break;
|
|
case 'rrd_add':
|
|
ds_rrd_add();
|
|
|
|
break;
|
|
case 'rrd_remove':
|
|
ds_rrd_remove();
|
|
|
|
break;
|
|
case 'data_edit':
|
|
top_header();
|
|
|
|
data_edit();
|
|
|
|
bottom_footer();
|
|
|
|
break;
|
|
case 'ds_disable':
|
|
ds_disable();
|
|
|
|
break;
|
|
case 'ds_enable':
|
|
ds_enable();
|
|
|
|
break;
|
|
case 'ds_repair':
|
|
ds_repair();
|
|
|
|
break;
|
|
case 'ds_edit':
|
|
ds_edit();
|
|
|
|
break;
|
|
case 'ajax_hosts':
|
|
$sql_where = '';
|
|
|
|
if (gfrv('site_id') > 0) {
|
|
$sql_where = 'site_id = ' . gfrv('site_id');
|
|
}
|
|
|
|
get_allowed_ajax_hosts(true, true, $sql_where);
|
|
|
|
break;
|
|
case 'ajax_hosts_noany':
|
|
$sql_where = '';
|
|
|
|
if (gfrv('site_id') > 0) {
|
|
$sql_where = 'site_id = ' . gfrv('site_id');
|
|
}
|
|
|
|
get_allowed_ajax_hosts(false, true, $sql_where);
|
|
|
|
break;
|
|
default:
|
|
top_header();
|
|
|
|
data_sources();
|
|
|
|
bottom_footer();
|
|
|
|
break;
|
|
}
|
|
|
|
function form_save() : void {
|
|
if ((isrv('save_component_data_source_new')) && (!ierv('data_template_id'))) {
|
|
$save['id'] = gfrv('local_data_id');
|
|
$save['host_id'] = gfrv('host_id');
|
|
$save['data_template_id'] = gfrv('data_template_id');
|
|
|
|
$local_data_id = sql_save($save, 'data_local');
|
|
|
|
change_data_template($local_data_id, grv('data_template_id'));
|
|
|
|
// update the title cache
|
|
update_data_source_title_cache($local_data_id);
|
|
|
|
// update host data
|
|
if (!ierv('host_id')) {
|
|
push_out_host(grv('host_id'), $local_data_id);
|
|
}
|
|
|
|
if (empty($save['id'])) {
|
|
/**
|
|
* Save the last time a data source was created/updated
|
|
* for Caching.
|
|
*/
|
|
set_config_option('time_last_change_data_source', time());
|
|
}
|
|
}
|
|
|
|
if ((isrv('save_component_data')) && (!is_error_message())) {
|
|
// ================= input validation =================
|
|
gfrv('data_template_data_id');
|
|
// ====================================================
|
|
|
|
// ok, first pull out all 'input' values so we know how much to save
|
|
$input_fields = db_fetch_assoc_prepared("SELECT dtd.data_input_id, dl.host_id, dif.id, dif.input_output,
|
|
dif.data_name, dif.regexp_match, dif.allow_nulls, dif.type_code, dtd.data_template_id, dl.id AS local_data_id
|
|
FROM data_template_data AS dtd
|
|
LEFT JOIN data_input_fields AS dif
|
|
ON dif.data_input_id = dtd.data_input_id
|
|
LEFT JOIN data_local AS dl
|
|
ON dtd.local_data_id = dl.id
|
|
WHERE dtd.id = ?
|
|
AND dif.input_output='in'",
|
|
[grv('data_template_data_id')]);
|
|
|
|
if (cacti_sizeof($input_fields)) {
|
|
foreach ($input_fields as $input_field) {
|
|
if (isrv('value_' . $input_field['id'])) {
|
|
// save the data into the 'data_input_data' table
|
|
$form_value = gnrv('value_' . $input_field['id']);
|
|
|
|
// we shouldn't enforce rules on fields the user cannot see (ie. templated ones)
|
|
$data_template_id = db_fetch_cell_prepared('SELECT local_data_template_data_id
|
|
FROM data_template_data
|
|
WHERE id = ?',
|
|
[grv('data_template_data_id')]
|
|
);
|
|
|
|
$is_templated = db_fetch_cell_prepared('SELECT t_value
|
|
FROM data_input_data
|
|
WHERE data_input_field_id = ?
|
|
AND data_template_data_id = ?',
|
|
[$input_field['id'], $data_template_id]
|
|
);
|
|
|
|
if ($is_templated == '') {
|
|
$allow_nulls = true;
|
|
} elseif ($input_field['allow_nulls'] == 'on') {
|
|
$allow_nulls = true;
|
|
} elseif (empty($input_field['allow_nulls'])) {
|
|
$allow_nulls = false;
|
|
} else {
|
|
$allow_nulls = true;
|
|
}
|
|
|
|
// run regexp match on input string
|
|
$constraints = [];
|
|
|
|
if ($allow_nulls === false) {
|
|
$constraints[] = new Assert\NotBlank();
|
|
}
|
|
|
|
if ($input_field['regexp_match'] != '') {
|
|
$constraints[] = new Assert\Regex(['pattern' => '/' . $input_field['regexp_match'] . '/']);
|
|
}
|
|
|
|
$form_value = CactiValidator::validateInput($form_value, 'value_' . $input_field['id'], $constraints, 3);
|
|
|
|
if (is_error_message() == false) {
|
|
db_execute_prepared("REPLACE INTO data_input_data
|
|
(data_input_field_id, data_template_data_id, data_template_id, local_data_id, host_id, t_value, value)
|
|
VALUES
|
|
(?, ?, ?, ?, ?, '', ?)",
|
|
[
|
|
$input_field['id'],
|
|
grv('data_template_data_id'),
|
|
$input_field['data_template_id'],
|
|
$input_field['local_data_id'],
|
|
$input_field['host_id'],
|
|
$form_value
|
|
]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((isrv('save_component_data_source')) && (!is_error_message())) {
|
|
// ================= input validation =================
|
|
gfrv('current_rrd');
|
|
gfrv('rrd_step');
|
|
gfrv('data_input_id');
|
|
gfrv('data_source_profile_id');
|
|
gfrv('host_id');
|
|
gfrv('_host_id');
|
|
gfrv('_data_template_id');
|
|
// ====================================================
|
|
|
|
$save1['id'] = gfrv('local_data_id');
|
|
$save1['data_template_id'] = gfrv('data_template_id');
|
|
$save1['host_id'] = gfrv('host_id');
|
|
|
|
$save2['id'] = gfrv('data_template_data_id');
|
|
$save2['local_data_template_data_id'] = gfrv('local_data_template_data_id');
|
|
$save2['data_template_id'] = gfrv('data_template_id');
|
|
$save2['data_input_id'] = CactiValidator::validateInput(grv('data_input_id'), 'data_input_id', [new Assert\Regex('/^[0-9]+$/')], 3);
|
|
$save2['name'] = CactiValidator::validateInput(gnrv('name'), 'name', [new Assert\NotBlank()], 3);
|
|
$save2['data_source_path'] = CactiValidator::validateInput(gnrv('data_source_path'), 'data_source_path', [], 3);
|
|
$save2['active'] = CactiValidator::validateInput((isrv('active') ? gnrv('active') : ''), 'active', [], 3);
|
|
$save2['data_source_profile_id'] = CactiValidator::validateInput(grv('data_source_profile_id'), 'data_source_profile_id', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')], 3);
|
|
$save2['rrd_step'] = CactiValidator::validateInput(grv('rrd_step'), 'rrd_step', [new Assert\NotBlank(), new Assert\Regex('/^[0-9]+$/')], 3);
|
|
|
|
if (is_error_message() == false) {
|
|
$local_data_id = sql_save($save1, 'data_local');
|
|
|
|
$save2['local_data_id'] = $local_data_id;
|
|
$data_template_data_id = sql_save($save2, 'data_template_data');
|
|
|
|
if ($data_template_data_id) {
|
|
raise_message(1);
|
|
|
|
if (empty($save['id'])) {
|
|
/**
|
|
* Save the last time a data source was created/updated
|
|
* for Caching.
|
|
*/
|
|
set_config_option('time_last_change_data_source', time());
|
|
}
|
|
} else {
|
|
raise_message(2);
|
|
}
|
|
}
|
|
|
|
if (is_error_message() == false) {
|
|
/* if this is a new data source and a template has been selected, skip item creation this time
|
|
otherwise it throws off the template creation because of the NULL data */
|
|
if (!ierv('local_data_id') || ierv('data_template_id')) {
|
|
/* if no template was set before the save, there will be only one data source item to save;
|
|
otherwise there might be >1 */
|
|
if (ierv('_data_template_id')) {
|
|
$rrds[0]['id'] = gnrv('current_rrd');
|
|
} else {
|
|
$rrds = db_fetch_assoc_prepared('SELECT id
|
|
FROM data_template_rrd
|
|
WHERE local_data_id = ?',
|
|
[gfrv('local_data_id')]);
|
|
}
|
|
|
|
if (cacti_sizeof($rrds)) {
|
|
foreach ($rrds as $rrd) {
|
|
if (ierv('_data_template_id')) {
|
|
$name_modifier = '';
|
|
} else {
|
|
$name_modifier = '_' . $rrd['id'];
|
|
}
|
|
|
|
$save3['id'] = $rrd['id'];
|
|
$save3['local_data_id'] = $local_data_id;
|
|
|
|
$save3['local_data_template_rrd_id'] = db_fetch_cell_prepared('SELECT local_data_template_rrd_id
|
|
FROM data_template_rrd
|
|
WHERE id = ?',
|
|
[$rrd['id']]);
|
|
|
|
$save3['data_template_id'] = gfrv('data_template_id');
|
|
|
|
$save3['rrd_maximum'] = CactiValidator::validateInput(gnrv("rrd_maximum$name_modifier"), "rrd_maximum$name_modifier", [new Assert\Regex('/^(-?([0-9]+(\.[0-9]*)?|[0-9]*\.[0-9]+)([eE][+\-]?[0-9]+)?|U|\|query_ifSpeed\||\|query_ifHighSpeed\|)\z/')]);
|
|
|
|
$save3['rrd_minimum'] = CactiValidator::validateInput(gnrv("rrd_minimum$name_modifier"), "rrd_minimum$name_modifier", [new Assert\Regex('/^(-?([0-9]+(\.[0-9]*)?|[0-9]*\.[0-9]+)([eE][+\-]?[0-9]+)?|U|\|query_ifSpeed\||\|query_ifHighSpeed\|)\z/')]);
|
|
|
|
$save3['rrd_heartbeat'] = CactiValidator::validateInput(gnrv("rrd_heartbeat$name_modifier"), "rrd_heartbeat$name_modifier", [new Assert\Regex('/^[0-9]+$/')], 3);
|
|
|
|
$save3['data_source_type_id'] = CactiValidator::validateInput(gnrv("data_source_type_id$name_modifier"), "data_source_type_id$name_modifier", [new Assert\Regex('/^[0-9]+$/')], 3);
|
|
|
|
$save3['data_source_name'] = CactiValidator::validateInput(gnrv("data_source_name$name_modifier"), "data_source_name$name_modifier", [new Assert\Regex('/^[a-zA-Z0-9_-]{1,19}$/')], 3);
|
|
|
|
$save3['data_input_field_id'] = CactiValidator::validateInput((isrv("data_input_field_id$name_modifier") ? gnrv("data_input_field_id$name_modifier") : '0'), "data_input_field_id$name_modifier", [], 3);
|
|
|
|
if ($save3['rrd_minimum'] != 'U' && $save3['rrd_maximum'] != 'U') {
|
|
if ($save3['rrd_minimum'] >= $save3['rrd_maximum']) {
|
|
raise_message(43);
|
|
|
|
$_SESSION[SESS_ERROR_FIELDS]['rrd_maximum'] = 'rrd_maximum';
|
|
|
|
header('Location: data_sources.php?action=ds_edit&id=' . (empty($local_data_id) ? gfrv('local_data_id') : $local_data_id) . '&host_id=' . grv('host_id') . '&view_rrd=' . (isrv('current_rrd') ? gnrv('current_rrd') : '0'));
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
$data_template_rrd_id = sql_save($save3, 'data_template_rrd');
|
|
|
|
if ($data_template_rrd_id) {
|
|
raise_message(1);
|
|
} else {
|
|
raise_message(2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_error_message() == false) {
|
|
if (grv('data_template_id') != grv('_data_template_id')) {
|
|
// update all necessary template information
|
|
change_data_template($local_data_id, grv('data_template_id'));
|
|
} elseif (!ierv('data_template_id')) {
|
|
update_data_source_data_query_cache($local_data_id);
|
|
}
|
|
|
|
if (grv('host_id') != grv('_host_id')) {
|
|
// push out all necessary host information
|
|
push_out_host(grv('host_id'), $local_data_id);
|
|
|
|
// reset current host for display purposes
|
|
$_SESSION['sess_data_source_current_host_id'] = grv('host_id');
|
|
}
|
|
|
|
// if no data source path has been entered, generate one
|
|
if (ierv('data_source_path')) {
|
|
generate_data_source_path($local_data_id);
|
|
}
|
|
|
|
// update the title cache
|
|
update_data_source_title_cache($local_data_id);
|
|
}
|
|
}
|
|
|
|
// update the poller cache last to make sure everything is fresh
|
|
if ((!is_error_message()) && (!empty($local_data_id))) {
|
|
update_poller_cache($local_data_id, true);
|
|
}
|
|
|
|
if (isrv('save_component_data_source_new') && ierv('data_template_id')) {
|
|
header('Location: data_sources.php?action=ds_edit&host_id=' . grv('host_id') . '&new=1');
|
|
} elseif ((is_error_message()) || (gfrv('data_template_id') != gfrv('_data_template_id')) || (gfrv('data_input_id') != gfrv('_data_input_id')) || (gfrv('host_id') != gfrv('_host_id'))) {
|
|
header('Location: data_sources.php?action=ds_edit&id=' . (empty($local_data_id) ? gfrv('local_data_id') : $local_data_id) . '&host_id=' . grv('host_id') . '&view_rrd=' . (isrv('current_rrd') ? gnrv('current_rrd') : '0'));
|
|
} else {
|
|
header('Location: data_sources.php');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines whether the current user is authorized to modify the given data source.
|
|
* Data sources tied to a device (host_id > 0) require the caller to be authorized for
|
|
* that device; host-independent data sources (host_id = 0) are not device-scoped.
|
|
*
|
|
* @param int $local_data_id The data source to check.
|
|
*
|
|
* @return bool True if the caller may modify this data source.
|
|
*/
|
|
function data_source_authorized(int $local_data_id) : bool {
|
|
$host_id = db_fetch_cell_prepared('SELECT host_id FROM data_local WHERE id = ?', [$local_data_id]);
|
|
|
|
if (empty($host_id)) {
|
|
return true;
|
|
}
|
|
|
|
return is_device_allowed((int) $host_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') == '1') { // delete
|
|
if (!isrv('delete_type')) {
|
|
srv('delete_type', 1);
|
|
} else {
|
|
gfrv('delete_type');
|
|
}
|
|
|
|
switch (grv('delete_type')) {
|
|
case '2': // delete all graph items tied to this data source
|
|
$data_template_rrds = array_rekey(db_fetch_assoc('SELECT id
|
|
FROM data_template_rrd
|
|
WHERE ' . array_to_sql_or($selected_items, 'local_data_id')), 'id', 'id');
|
|
|
|
$poller_ids = db_fetch_assoc('SELECT DISTINCT poller_id
|
|
FROM host AS h
|
|
INNER JOIN data_local AS dl
|
|
ON dl.host_id=h.id
|
|
WHERE poller_id > 1
|
|
AND id IN (' . implode(', ', $selected_items) . ')');
|
|
|
|
api_plugin_hook_function('graph_items_remove', $data_template_rrds);
|
|
|
|
// loop through each data source item
|
|
if (cacti_sizeof($data_template_rrds) > 0) {
|
|
db_execute('DELETE FROM graph_templates_item
|
|
WHERE task_item_id IN (' . implode(',', $data_template_rrds) . ')
|
|
AND local_graph_id > 0');
|
|
|
|
if (cacti_sizeof($poller_ids)) {
|
|
foreach ($poller_ids as $poller_id) {
|
|
if (($rcnn_id = poller_push_to_remote_db_connect($poller_id, true)) !== false) {
|
|
db_execute('DELETE FROM graph_templates_item
|
|
WHERE task_item_id IN (' . implode(',', $data_template_rrds) . ')
|
|
AND local_graph_id > 0', true, $rcnn_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
case '3': // delete all graphs tied to this data source
|
|
$graphs = array_rekey(db_fetch_assoc('SELECT
|
|
graph_templates_graph.local_graph_id
|
|
FROM (data_template_rrd,graph_templates_item,graph_templates_graph)
|
|
WHERE graph_templates_item.task_item_id=data_template_rrd.id
|
|
AND graph_templates_item.local_graph_id=graph_templates_graph.local_graph_id
|
|
AND ' . array_to_sql_or($selected_items, 'data_template_rrd.local_data_id') . '
|
|
AND graph_templates_graph.local_graph_id > 0
|
|
GROUP BY graph_templates_graph.local_graph_id'), 'local_graph_id', 'local_graph_id');
|
|
|
|
if (cacti_sizeof($graphs) > 0) {
|
|
api_graph_remove_multi($graphs);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
api_data_source_remove_multi($selected_items);
|
|
} elseif (gnrv('drp_action') == '2') { // data source disable
|
|
foreach ($selected_items as $local_data_id) {
|
|
if (data_source_authorized((int) $local_data_id)) {
|
|
api_data_source_disable($local_data_id);
|
|
}
|
|
}
|
|
} elseif (gnrv('drp_action') == '3') { // data source enable
|
|
foreach ($selected_items as $local_data_id) {
|
|
if (data_source_authorized((int) $local_data_id)) {
|
|
api_data_source_enable($local_data_id);
|
|
}
|
|
}
|
|
} elseif (gnrv('drp_action') == '4') { // change host
|
|
gfrv('host_id');
|
|
|
|
api_data_source_change_host($selected_items, grv('host_id'));
|
|
} elseif (gnrv('drp_action') == '5') { // reapply suggested data source naming
|
|
for ($i = 0; ($i < cacti_count($selected_items)); $i++) {
|
|
api_reapply_suggested_data_source_data($selected_items[$i]);
|
|
update_data_source_title_cache($selected_items[$i]);
|
|
}
|
|
} elseif (get_nfilter_request_var('drp_action') == '6') { // change data source profile
|
|
get_filter_request_var('data_source_profile_id');
|
|
|
|
$new_profile = db_fetch_row_prepared('SELECT * FROM data_source_profiles WHERE id = ?',
|
|
[get_request_var('data_source_profile_id')]);
|
|
|
|
if (cacti_sizeof($new_profile)) {
|
|
$rrd_changes = 0;
|
|
$remote_conns = [];
|
|
|
|
foreach ($selected_items as $local_data_id) {
|
|
if (!data_source_authorized((int) $local_data_id)) {
|
|
continue;
|
|
}
|
|
|
|
$data = db_fetch_row_prepared('SELECT dl.host_id, h.poller_id
|
|
FROM host AS h
|
|
INNER JOIN data_local AS dl
|
|
ON dl.host_id = h.id
|
|
AND dl.id = ?',
|
|
[$local_data_id]);
|
|
|
|
if ($data['poller_id'] > 1) {
|
|
$remote_conns[$data['poller_id']] = poller_push_to_remote_db_connect($data['host_id']);
|
|
}
|
|
|
|
// Get current step value
|
|
$current_step = db_fetch_cell_prepared('SELECT rrd_step
|
|
FROM data_template_data
|
|
WHERE local_data_id = ?',
|
|
[$local_data_id]);
|
|
|
|
// Update all database tables
|
|
db_execute_prepared('UPDATE data_template_data
|
|
SET data_source_profile_id = ?, rrd_step = ?
|
|
WHERE local_data_id = ?',
|
|
[get_request_var('data_source_profile_id'), $new_profile['step'], $local_data_id]
|
|
);
|
|
|
|
db_execute_prepared('UPDATE data_template_rrd
|
|
SET rrd_heartbeat = ?
|
|
WHERE local_data_id = ?',
|
|
[$new_profile['heartbeat'], $local_data_id]
|
|
);
|
|
|
|
db_execute_prepared('UPDATE poller_item
|
|
SET rrd_step = ?
|
|
WHERE local_data_id = ?',
|
|
[$new_profile['step'], $local_data_id]
|
|
);
|
|
|
|
if ($data['poller_id'] > 1 && $remote_conns[$data['poller_id']] !== false) {
|
|
$conn = $remote_conns[$data['poller_id']];
|
|
|
|
db_execute_prepared('UPDATE data_template_data
|
|
SET data_source_profile_id = ?, rrd_step = ?
|
|
WHERE local_data_id = ?',
|
|
[get_request_var('data_source_profile_id'), $new_profile['step'], $local_data_id], true, $conn
|
|
);
|
|
|
|
db_execute_prepared('UPDATE data_template_rrd
|
|
SET rrd_heartbeat = ?
|
|
WHERE local_data_id = ?',
|
|
[$new_profile['heartbeat'], $local_data_id], true, $conn
|
|
);
|
|
|
|
db_execute_prepared('UPDATE poller_item
|
|
SET rrd_step = ?
|
|
WHERE local_data_id = ?',
|
|
[$new_profile['step'], $local_data_id], true, $conn
|
|
);
|
|
}
|
|
|
|
update_poller_cache($local_data_id, true);
|
|
|
|
// Handle RRD file if step changed
|
|
if ($current_step != $new_profile['step']) {
|
|
$rrd_path = get_data_source_path($local_data_id, true);
|
|
|
|
if (file_exists($rrd_path)) {
|
|
// Backup with timestamp and delete
|
|
$backup = $rrd_path . '.bak_' . date('Ymd-His');
|
|
|
|
if (copy($rrd_path, $backup)) {
|
|
unlink($rrd_path);
|
|
$rrd_changes++;
|
|
cacti_log("RRD backed up and removed: $rrd_path -> $backup", false, 'DATASOURCE');
|
|
} else {
|
|
// Log a warning so administrators are aware the backup did not succeed
|
|
cacti_log("WARNING: Failed to backup RRD file '$rrd_path' to '$backup'", false, 'DATASOURCE');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
raise_message(1);
|
|
|
|
if ($rrd_changes > 0) {
|
|
$_SESSION['sess_messages']['custom_info'] = [
|
|
'message' => sprintf(__('%d RRD files were backed up and will be recreated with new step value at next polling.'), $rrd_changes),
|
|
'type' => 'info'
|
|
];
|
|
}
|
|
} else {
|
|
raise_message(2);
|
|
}
|
|
} else {
|
|
api_plugin_hook_function('data_source_action_execute', gnrv('drp_action'));
|
|
}
|
|
}
|
|
|
|
// update snmpcache
|
|
snmpagent_data_source_action_bottom([gnrv('drp_action'), $selected_items]);
|
|
|
|
api_plugin_hook_function('data_source_action_bottom', [gnrv('drp_action'), $selected_items]);
|
|
|
|
header('Location: data_sources.php');
|
|
|
|
exit;
|
|
} else {
|
|
$ilist = '';
|
|
$iarray = [];
|
|
|
|
// some global defaults
|
|
$graphs = [];
|
|
$flist = '';
|
|
$hosts = array_rekey(
|
|
db_fetch_assoc("SELECT id, CONCAT_WS('',description,' (',hostname,')') AS name
|
|
FROM host
|
|
ORDER BY description, hostname"),
|
|
'id', 'name'
|
|
);
|
|
|
|
// 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(get_data_source_title(intval($matches[1]))) . '</li>';
|
|
|
|
$iarray[] = $matches[1];
|
|
}
|
|
}
|
|
|
|
if (cacti_sizeof($iarray)) {
|
|
if (gnrv('drp_action') == '1') { // delete
|
|
$graphs = db_fetch_assoc('SELECT
|
|
graph_templates_graph.local_graph_id,
|
|
graph_templates_graph.title_cache
|
|
FROM (data_template_rrd,graph_templates_item,graph_templates_graph)
|
|
WHERE graph_templates_item.task_item_id=data_template_rrd.id
|
|
AND graph_templates_item.local_graph_id=graph_templates_graph.local_graph_id
|
|
AND ' . array_to_sql_or($iarray, 'data_template_rrd.local_data_id') . '
|
|
AND graph_templates_graph.local_graph_id > 0
|
|
GROUP BY graph_templates_graph.local_graph_id
|
|
ORDER BY graph_templates_graph.title_cache');
|
|
|
|
if (cacti_sizeof($graphs)) {
|
|
foreach ($graphs as $g) {
|
|
$flist .= '<li>' . htmle($g['title_cache']) . '</li>';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// For use by plugins
|
|
$save['drp_action'] = 1;
|
|
$save['ds_list'] = $ilist;
|
|
$save['ds_array'] = $iarray;
|
|
|
|
$form_data = [
|
|
'general' => [
|
|
'page' => 'data_sources.php',
|
|
'actions' => $actions,
|
|
'optvar' => 'drp_action',
|
|
'item_array' => $iarray,
|
|
'item_list' => $ilist
|
|
],
|
|
'options' => [
|
|
1 => [
|
|
'smessage' => __('Click \'Continue\' to Delete the following Data Source.'),
|
|
'pmessage' => __('Click \'Continue\' to Delete following Data Sources.'),
|
|
'scont' => __('Delete Data Source'),
|
|
'pcont' => __('Delete Data Sources'),
|
|
'flist' => $flist,
|
|
'sfmessage' => __n('The following Graph is using this Data Source.', 'The following Graphs are using this Data Source.', cacti_sizeof($graphs)),
|
|
'pfmessage' => __n('The following Graph is using these Data Sources.', 'The following Graphs are using these Data Sources.', cacti_sizeof($graphs)),
|
|
'extra' => [
|
|
'delete_type' => [
|
|
'method' => 'radio_button',
|
|
'options' => [
|
|
'1' => [
|
|
'default' => 3,
|
|
'title' => __n('Leave the Graph Untouched', 'Leave all Graphs untouched.', cacti_sizeof($graphs))
|
|
],
|
|
'2' => [
|
|
'default' => 3,
|
|
'title' => __n('Delete all Graph Items that reference this Data Source.', 'Delete all Graph Items that reference these Data Sources', cacti_sizeof($iarray))
|
|
],
|
|
'3' => [
|
|
'default' => 3,
|
|
'title' => __n('Delete all Graphs that reference this Data Source.', 'Delete all Graphs that reference these Data Sources', cacti_sizeof($iarray))
|
|
]
|
|
]
|
|
]
|
|
]
|
|
],
|
|
2 => [
|
|
'smessage' => __('Click \'Continue\' to Disable the following Data Source.'),
|
|
'pmessage' => __('Click \'Continue\' to Disable following Data Sources.'),
|
|
'scont' => __('Disable Data Source'),
|
|
'pcont' => __('Disable Data Sources')
|
|
],
|
|
3 => [
|
|
'smessage' => __('Click \'Continue\' to Enable the following Data Source.'),
|
|
'pmessage' => __('Click \'Continue\' to Enable following Data Sources.'),
|
|
'scont' => __('Enable Data Source'),
|
|
'pcont' => __('Enable Data Sources')
|
|
],
|
|
4 => [
|
|
'smessage' => __('Click \'Continue\' to Change the Device for the following Data Source.'),
|
|
'pmessage' => __('Click \'Continue\' to Change the Device for the following Data Sources.'),
|
|
'scont' => __('Change Device for Data Source'),
|
|
'pcont' => __('Change Device for Data Sources'),
|
|
'extra' => [
|
|
'host_id' => [
|
|
'method' => 'drop_array',
|
|
'title' => __('New Device'),
|
|
'default' => '',
|
|
'array' => $hosts
|
|
]
|
|
]
|
|
],
|
|
5 => [
|
|
'smessage' => __('Click \'Continue\' to Reapply Suggested Names the following Data Source.'),
|
|
'pmessage' => __('Click \'Continue\' to Reapply Suggested Names for the following Data Sources.'),
|
|
'scont' => __('Reapply Suggested Names for Data Source'),
|
|
'pcont' => __('Reapply Suggested Names for Data Sources')
|
|
],
|
|
6 => [
|
|
'smessage' => __('Click \'Continue\' to Change the Data Source Profile for the following Data Source.'),
|
|
'pmessage' => __('Click \'Continue\' to Change the Data Source Profile for the following Data Sources.'),
|
|
'scont' => __('Change Data Source Profile'),
|
|
'pcont' => __('Change Data Source Profiles'),
|
|
'extra' => [
|
|
'data_source_profile_id' => [
|
|
'method' => 'drop_sql',
|
|
'title' => __('New Data Source Profile'),
|
|
'default' => '',
|
|
'sql' => 'SELECT id, name FROM data_source_profiles ORDER BY name'
|
|
]
|
|
]
|
|
]
|
|
]
|
|
];
|
|
|
|
$form_data = api_plugin_hook_function('data_source_confirmation_form', $form_data);
|
|
|
|
form_continue_confirmation($form_data, 'data_source_action_prepare', $save);
|
|
}
|
|
}
|
|
|
|
function data_edit(bool $incform = true) : void {
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
if (!ierv('id')) {
|
|
$data = db_fetch_row_prepared('SELECT id, data_input_id, data_template_id, name, local_data_id
|
|
FROM data_template_data
|
|
WHERE local_data_id = ?',
|
|
[grv('id')]);
|
|
|
|
$template_data = db_fetch_row_prepared('SELECT id, data_input_id
|
|
FROM data_template_data
|
|
WHERE data_template_id = ?
|
|
AND local_data_id = 0',
|
|
[$data['data_template_id']]);
|
|
|
|
$host = db_fetch_row_prepared('SELECT host.id, host.hostname
|
|
FROM (data_local, host)
|
|
WHERE data_local.host_id = host.id
|
|
AND data_local.id = ?',
|
|
[grv('id')]);
|
|
} else {
|
|
$data = [];
|
|
$host = [];
|
|
$template_data = [];
|
|
}
|
|
|
|
if ($incform) {
|
|
form_start('data_sources.php', 'data_source_edit');
|
|
}
|
|
|
|
$i = 0;
|
|
|
|
if ($data['data_input_id'] > 0) {
|
|
// get each INPUT field for this data input source
|
|
$fields = db_fetch_assoc_prepared('SELECT *
|
|
FROM data_input_fields
|
|
WHERE data_input_id = ?
|
|
AND input_output = "in"
|
|
ORDER BY name',
|
|
[$data['data_input_id']]
|
|
);
|
|
|
|
$data_input_name = db_fetch_cell_prepared('SELECT name
|
|
FROM data_input
|
|
WHERE id = ?',
|
|
[$data['data_input_id']]);
|
|
|
|
html_start_box(__('Custom Data [data input: %s]', htmle($data_input_name)), '100%', false, 3, 'center', '');
|
|
|
|
// loop through each field found
|
|
if (cacti_sizeof($fields) > 0) {
|
|
foreach ($fields as $field) {
|
|
$data_input_data = db_fetch_row_prepared('SELECT *
|
|
FROM data_input_data
|
|
WHERE data_template_data_id = ?
|
|
AND data_input_field_id = ?',
|
|
[$data['id'], $field['id']]
|
|
);
|
|
|
|
if (cacti_sizeof($data_input_data) > 0) {
|
|
$old_value = $data_input_data['value'];
|
|
} else {
|
|
$old_value = '';
|
|
}
|
|
|
|
// if data template then get t_value FROM template, else always allow user input
|
|
if (empty($data['data_template_id'])) {
|
|
$can_template = 'on';
|
|
} else {
|
|
$can_template = db_fetch_cell_prepared('SELECT t_value
|
|
FROM data_input_data
|
|
WHERE data_template_data_id = ?
|
|
AND data_input_field_id = ?',
|
|
[$template_data['id'], $field['id']]
|
|
);
|
|
}
|
|
|
|
form_alternate_row();
|
|
|
|
if ((!empty($host['id'])) && (preg_match('/^' . VALID_HOST_FIELDS . '$/i', $field['type_code']))) {
|
|
print "<td style='width:50%;'><strong>" . htmle($field['name']) . '</strong> ' . __('(From Device: %s)', htmle($host['hostname'])) . '</td>';
|
|
print '<td><em>' . htmle($old_value) . '</em></td>';
|
|
} elseif (empty($can_template)) {
|
|
print "<td style='width:50%;'><strong>" . htmle($field['name']) . '</strong> ' . __('(From Data Template)') . '</td>';
|
|
print '<td><em>' . (empty($old_value) ? __('Nothing Entered') : htmle($old_value)) . '</em></td>';
|
|
} else {
|
|
print "<td style='width:50%;'><strong>" . htmle($field['name']) . '</strong></td>';
|
|
print '<td>';
|
|
|
|
draw_custom_data_row('value_' . $field['id'], $field['id'], $data['id'], $old_value);
|
|
|
|
print '</td>';
|
|
}
|
|
|
|
print '</tr>';
|
|
|
|
$i++;
|
|
}
|
|
} else {
|
|
print '<tr class="tableRow odd"><td><em>' . __('No Input Fields for the Selected Data Input Source') . '</em></td></tr>';
|
|
}
|
|
|
|
html_end_box();
|
|
}
|
|
|
|
if ($incform) {
|
|
form_hidden_box('local_data_id', $data['local_data_id'] ?? 0, '');
|
|
form_hidden_box('data_template_data_id', $data['id'] ?? 0, '');
|
|
}
|
|
|
|
form_hidden_box('save_component_data', '1', '');
|
|
}
|
|
|
|
function ds_rrd_remove() : void {
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
db_execute_prepared('DELETE FROM data_template_rrd
|
|
WHERE id = ?',
|
|
[grv('id')]);
|
|
|
|
db_execute_prepared('UPDATE graph_templates_item
|
|
SET task_item_id = 0
|
|
WHERE task_item_id = ?',
|
|
[grv('id')]);
|
|
|
|
header('Location: data_sources.php?action=ds_edit&id=' . grv('local_data_id'));
|
|
}
|
|
|
|
function ds_rrd_add() : void {
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
db_execute_prepared("INSERT INTO data_template_rrd
|
|
(local_data_id, rrd_maximum, rrd_minimum, rrd_heartbeat, data_source_type_id, data_source_name)
|
|
VALUES (?, 100, 0, 600, 1, 'ds')",
|
|
[grv('id')]);
|
|
|
|
$data_template_rrd_id = db_fetch_insert_id();
|
|
|
|
header('Location: data_sources.php?action=ds_edit&id=' . grv('id') . "&view_rrd=$data_template_rrd_id");
|
|
}
|
|
|
|
function ds_disable() : void {
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
if (!data_source_authorized((int) grv('id'))) {
|
|
raise_message('permission_denied');
|
|
header('Location: data_sources.php');
|
|
|
|
return;
|
|
}
|
|
|
|
api_data_source_disable(grv('id'));
|
|
header('Location: data_sources.php?action=ds_edit&id=' . grv('id'));
|
|
}
|
|
|
|
function ds_enable() : void {
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
if (!data_source_authorized((int) grv('id'))) {
|
|
raise_message('permission_denied');
|
|
header('Location: data_sources.php');
|
|
|
|
return;
|
|
}
|
|
|
|
api_data_source_enable(grv('id'));
|
|
header('Location: data_sources.php?action=ds_edit&id=' . grv('id'));
|
|
}
|
|
|
|
/**
|
|
* Repairs the RRDfile for a Data Source so that it matches its Cacti
|
|
* definition. The 'Apply Suggested Fixes' link in the Data Source Info mode
|
|
* invokes this action, which runs the same rrdtool tune recommendations that
|
|
* the Info mode displays: missing Data Sources are added ('DS:...'), extra Data
|
|
* Sources are removed ('DEL:...') and the type, heartbeat, minimum and maximum
|
|
* are corrected. The repair only runs when the web server can write the
|
|
* RRDfile; otherwise the Info mode presents the commands to run by hand.
|
|
*
|
|
* @return void No value is returned.
|
|
*/
|
|
function ds_repair() : void {
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
// ====================================================
|
|
|
|
$id = (int) grv('id');
|
|
|
|
if (!data_source_authorized($id)) {
|
|
raise_message('permission_denied');
|
|
header('Location: data_sources.php');
|
|
|
|
return;
|
|
}
|
|
|
|
$rrd_info = rrdtool_function_info($id);
|
|
|
|
if (!is_array($rrd_info) || !isset($rrd_info['rra']) || !cacti_sizeof($rrd_info['rra'])) {
|
|
raise_message('ds_repair', __('The RRDfile could not be read, so no repairs were performed.'), MESSAGE_LEVEL_ERROR);
|
|
header('Location: data_sources.php?action=ds_edit&id=' . $id);
|
|
|
|
return;
|
|
}
|
|
|
|
$diff = rrdtool_cacti_compare($id, $rrd_info);
|
|
|
|
if (!isset($diff['tune']) || !cacti_sizeof($diff['tune'])) {
|
|
raise_message('ds_repair', __('No RRDfile repairs were necessary.'), MESSAGE_LEVEL_INFO);
|
|
header('Location: data_sources.php?action=ds_edit&id=' . $id);
|
|
|
|
return;
|
|
}
|
|
|
|
$path = get_data_source_path($id, true);
|
|
|
|
if (!is_writeable($path)) {
|
|
raise_message('ds_repair', __('The RRDfile is not writable by the web server. Run the suggested commands from the command line instead.'), MESSAGE_LEVEL_ERROR);
|
|
header('Location: data_sources.php?action=ds_edit&id=' . $id);
|
|
|
|
return;
|
|
}
|
|
|
|
$rrdtool_path = read_config_option('path_rrdtool');
|
|
$failures = 0;
|
|
|
|
foreach ($diff['tune'] as $options) {
|
|
// never execute a recommendation carrying control characters
|
|
if (rrd_strip_control_chars($options) !== $options) {
|
|
continue;
|
|
}
|
|
|
|
$command = $rrdtool_path . ' tune ' . $options;
|
|
$result = rrdtool_execute('tune ' . $options, false, RRDTOOL_OUTPUT_BOOLEAN);
|
|
|
|
if ($result === true) {
|
|
cacti_log("RRDfile repair command succeeded for DS[$id] Command[$command]", false, 'DSDEBUG');
|
|
} else {
|
|
cacti_log("ERROR: RRDfile repair command failed for DS[$id] Command[$command]", false, 'DSDEBUG');
|
|
$failures++;
|
|
}
|
|
}
|
|
|
|
if ($failures == 0) {
|
|
raise_message('ds_repair', __('All RRDfile repairs succeeded.'), MESSAGE_LEVEL_INFO);
|
|
} else {
|
|
raise_message('ds_repair', __('One or more RRDfile repairs failed. See Cacti log for errors.'), MESSAGE_LEVEL_ERROR);
|
|
}
|
|
|
|
header('Location: data_sources.php?action=ds_edit&id=' . $id);
|
|
}
|
|
|
|
function ds_edit() : void {
|
|
global $struct_data_source, $struct_data_source_item;
|
|
|
|
// ================= input validation =================
|
|
gfrv('id');
|
|
gfrv('host_id');
|
|
// ====================================================
|
|
|
|
api_plugin_hook('data_source_edit_top');
|
|
|
|
$use_data_template = true;
|
|
$data_template = [];
|
|
|
|
if (!ierv('id')) {
|
|
$data_local = db_fetch_row_prepared('SELECT host_id, data_template_id
|
|
FROM data_local
|
|
WHERE id = ?',
|
|
[grv('id')]);
|
|
|
|
$data = db_fetch_row_prepared('SELECT *
|
|
FROM data_template_data
|
|
WHERE local_data_id = ?',
|
|
[grv('id')]);
|
|
|
|
if (isset($data_local['data_template_id']) && $data_local['data_template_id'] >= 0) {
|
|
$data_template = db_fetch_row_prepared('SELECT id, name
|
|
FROM data_template
|
|
WHERE id = ?',
|
|
[$data_local['data_template_id']]);
|
|
|
|
$data_template_data = db_fetch_row_prepared('SELECT *
|
|
FROM data_template_data
|
|
WHERE data_template_id = ?
|
|
AND local_data_id = 0',
|
|
[$data_local['data_template_id']]);
|
|
} else {
|
|
raise_message(11);
|
|
header('Location: data_sources.php');
|
|
|
|
exit;
|
|
}
|
|
|
|
$header_label = __esc('Data Template Selection [edit: %s]', get_data_source_title(grv('id')));
|
|
|
|
if (empty($data_local['data_template_id'])) {
|
|
$use_data_template = false;
|
|
}
|
|
} else {
|
|
$header_label = __('Data Template Selection [new]');
|
|
|
|
$use_data_template = false;
|
|
}
|
|
|
|
// handle debug mode
|
|
if (isrv('debug')) {
|
|
if (gnrv('debug') == '0') {
|
|
kill_session_var('ds_debug_mode');
|
|
} elseif (gnrv('debug') == '1') {
|
|
$_SESSION['ds_debug_mode'] = true;
|
|
}
|
|
}
|
|
|
|
// handle debug mode
|
|
if (isrv('info')) {
|
|
if (gnrv('info') == '0') {
|
|
kill_session_var('ds_info_mode');
|
|
} elseif (gnrv('info') == '1') {
|
|
$_SESSION['ds_info_mode'] = true;
|
|
}
|
|
}
|
|
|
|
if (cacti_sizeof($data_template)) {
|
|
$data_sources = db_fetch_cell_prepared('SELECT
|
|
GROUP_CONCAT(DISTINCT data_source_name ORDER BY data_source_name) AS data_source_names
|
|
FROM data_template_rrd
|
|
WHERE data_template_id = ?
|
|
GROUP BY data_template_id
|
|
ORDER BY data_source_names',
|
|
[$data_template['id']]
|
|
);
|
|
|
|
$dts = db_fetch_assoc_prepared('SELECT data_template_id,
|
|
GROUP_CONCAT(DISTINCT data_source_name ORDER BY data_source_name) AS data_source_names
|
|
FROM data_template_rrd
|
|
WHERE local_data_id=0
|
|
GROUP BY data_template_id
|
|
HAVING data_source_names = ?',
|
|
[$data_sources]
|
|
);
|
|
|
|
if (cacti_sizeof($dts)) {
|
|
foreach ($dts as $dtid) {
|
|
$dtids[] = $dtid['data_template_id'];
|
|
}
|
|
|
|
$dtsql = 'SELECT id, name FROM data_template WHERE id IN(' . implode(',', $dtids) . ') ORDER BY name'; // @phpstan-ignore-line
|
|
} else {
|
|
$dtsql = 'SELECT id, name FROM data_template ORDER BY name';
|
|
}
|
|
} else {
|
|
$dtsql = 'SELECT id, name FROM data_template ORDER BY name';
|
|
}
|
|
|
|
if (grv('host_id') > 0) {
|
|
$hostDescription = db_fetch_cell_prepared('SELECT description
|
|
FROM host
|
|
WHERE id = ?',
|
|
[grv('host_id')]
|
|
);
|
|
} elseif (isset($data_local['host_id'])) {
|
|
$hostDescription = db_fetch_cell_prepared('SELECT description
|
|
FROM host
|
|
WHERE id = ?',
|
|
[$data_local['host_id']]
|
|
);
|
|
} else {
|
|
$hostDescription = '';
|
|
}
|
|
|
|
if (!ierv('id')) {
|
|
$ins_buttons = [];
|
|
|
|
$local_graph_ids = db_fetch_assoc_prepared('SELECT DISTINCT local_graph_id
|
|
FROM graph_templates_item AS gti
|
|
INNER JOIN data_template_rrd AS dtr
|
|
ON dtr.id = gti.task_item_id
|
|
WHERE local_data_id = ?',
|
|
[grv('id')]);
|
|
|
|
if (isset($_SESSION['ds_debug_mode'])) {
|
|
$debug_message = __('Turn Off Data Source Debug Mode');
|
|
$debug = true;
|
|
} else {
|
|
$debug_message = __('Turn On Data Source Debug Mode');
|
|
$debug = false;
|
|
}
|
|
|
|
$debug_url = 'data_sources.php?action=ds_edit&id=' . (isrv('id') ? grv('id') : '0') . '&debug=' . (isset($_SESSION['ds_debug_mode']) ? '0' : '1');
|
|
|
|
if (isset($_SESSION['ds_info_mode'])) {
|
|
$info_message = __('Turn Off Data Source Info Mode');
|
|
$info_on = true;
|
|
} else {
|
|
$info_message = __('Turn On Data Source Info Mode');
|
|
$info_on = false;
|
|
}
|
|
|
|
$info_url = 'data_sources.php?action=ds_edit&id=' . (isrv('id') ? grv('id') : '0') . '&info=' . (isset($_SESSION['ds_info_mode']) ? '0' : '1');
|
|
|
|
$filters = [
|
|
'links' => [
|
|
[
|
|
'display' => $debug_message,
|
|
'url' => $debug_url,
|
|
'class' => ($debug ? 'ti ti-bug disableDebug' : 'ti ti-bug enableDebug')
|
|
],
|
|
[
|
|
'display' => $info_message,
|
|
'url' => $info_url,
|
|
'class' => ($info_on ? 'ti ti-qrcode disableDebug' : 'ti ti-qrcode editTemplate')
|
|
],
|
|
]
|
|
];
|
|
|
|
if (cacti_sizeof($local_graph_ids)) {
|
|
foreach ($local_graph_ids as $id) {
|
|
$name = db_fetch_cell_prepared('SELECT title_cache
|
|
FROM graph_templates_graph
|
|
WHERE local_graph_id = ?',
|
|
[$id['local_graph_id']]);
|
|
|
|
$ins_buttons[] = [
|
|
'display' => __esc('Edit Graph: \'%s\'', $name),
|
|
'url' => 'graphs.php?action=graph_edit&id=' . $id['local_graph_id'],
|
|
'class' => 'ti ti-chart-area-line-filled editGraph'
|
|
];
|
|
}
|
|
}
|
|
|
|
if (!ierv('host_id') || !empty($data_local['host_id'])) {
|
|
$ins_buttons[] = [
|
|
'display' => __('Edit Device'),
|
|
'url' => 'host.php?action=edit&id=' . ($data_local['host_id'] > 0 ? $data_local['host_id'] : grv('host_id')),
|
|
'class' => 'ti ti-server editDevice'
|
|
];
|
|
}
|
|
|
|
if (!empty($data_template['id'])) {
|
|
$ins_buttons[] = [
|
|
'display' => __('Edit Data Template'),
|
|
'url' => 'data_templates.php?action=template_edit&id=' . ($data_template['id'] > 0 ? $data_template['id'] : 0),
|
|
'class' => 'ti ti-copy editTemplate'
|
|
];
|
|
}
|
|
|
|
if (cacti_sizeof($ins_buttons)) {
|
|
foreach ($ins_buttons as $button) {
|
|
$filters['links'][] = $button;
|
|
}
|
|
}
|
|
|
|
if (isrv('id') && grv('id') > 0) {
|
|
$filters['links'][] = [
|
|
'display' => ($data['active'] == 'on' ? __('Disable Data Source') : __('Enable Data Source')),
|
|
'url' => 'data_sources.php?action=ds_' . ($data['active'] == 'on' ? 'dis' : 'en') . 'able&id=' . grv('id'),
|
|
'class' => ($data['active'] == 'on' ? 'ti ti-circle-check deviceRecovering' : 'ti ti-circle-check deviceUp')
|
|
];
|
|
}
|
|
} else {
|
|
$filters = [];
|
|
}
|
|
|
|
top_header();
|
|
|
|
form_start('data_sources.php', 'data_source');
|
|
|
|
$pageFilter = new CactiTableFilter($header_label, 'data_sources.php?action=ds_edit&id=' . grv('id'), 'data_source', 'sess_ds_edit', '', '', false);
|
|
$pageFilter->set_filter_array($filters);
|
|
$pageFilter->render();
|
|
|
|
html_start_box('', '100%', true, 3, 'center', '');
|
|
|
|
$form_array = [
|
|
'data_template_id' => [
|
|
'method' => 'drop_sql',
|
|
'friendly_name' => __('Data Template'),
|
|
'description' => __('The name given to this data template. Please note that you may only change Graph Templates to a 100%$ compatible Graph Template, which means that it includes identical Data Sources.'),
|
|
'value' => (cacti_sizeof($data_template) ? $data_template['id'] : '0'),
|
|
'none_value' => (cacti_sizeof($data_template) ? '' : 'None'),
|
|
'sql' => $dtsql
|
|
],
|
|
'host_id' => [
|
|
'method' => 'drop_callback',
|
|
'friendly_name' => __('Device'),
|
|
'description' => __('Choose the Device that this Data Source belongs to.'),
|
|
'none_value' => __('None'),
|
|
'sql' => 'SELECT id, description AS name FROM host ORDER BY name',
|
|
'action' => 'ajax_hosts_noany',
|
|
'id' => (isset($data_local['host_id']) ? $data_local['host_id'] : 0),
|
|
'value' => $hostDescription
|
|
],
|
|
'_data_template_id' => [
|
|
'method' => 'hidden',
|
|
'value' => (isset($data_template['id']) ? $data_template['id'] : '0')
|
|
],
|
|
'_host_id' => [
|
|
'method' => 'hidden',
|
|
'value' => (isset($data_local['host_id']) ? $data_local['host_id'] : '0')
|
|
],
|
|
'_data_input_id' => [
|
|
'method' => 'hidden',
|
|
'value' => (isset($data['data_input_id']) ? $data['data_input_id'] : '0')
|
|
],
|
|
'data_template_data_id' => [
|
|
'method' => 'hidden',
|
|
'value' => (isset($data['id']) ? $data['id'] : '0')
|
|
],
|
|
'local_data_template_data_id' => [
|
|
'method' => 'hidden',
|
|
'value' => (isset($data['local_data_template_data_id']) ? $data['local_data_template_data_id'] : '0')
|
|
],
|
|
'local_data_id' => [
|
|
'method' => 'hidden',
|
|
'value' => (isset($data['local_data_id']) ? $data['local_data_id'] : '0')
|
|
],
|
|
];
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => $form_array
|
|
]
|
|
);
|
|
|
|
html_end_box(true, true);
|
|
|
|
// only display the "inputs" area if we are using a data template for this data source
|
|
if (!empty($data['data_template_id'])) {
|
|
$template_data_rrds = db_fetch_assoc_prepared('SELECT *
|
|
FROM data_template_rrd
|
|
WHERE local_data_id = ?
|
|
ORDER BY data_source_name',
|
|
[grv('id')]);
|
|
|
|
html_start_box(__('Supplemental Data Template Data'), '100%', true, 3, 'center', '');
|
|
|
|
draw_nontemplated_fields_data_source($data['data_template_id'], $data['local_data_id'], $data, '|field|', __('Data Source Fields'), true, true, 0);
|
|
draw_nontemplated_fields_data_source_item($data['data_template_id'], $template_data_rrds, '|field|_|id|', __('Data Source Item Fields'), true, true, true, 0);
|
|
draw_nontemplated_fields_custom_data($data['id'], 'value_|id|', __('Custom Data'), true, true, 0);
|
|
|
|
form_hidden_box('save_component_data','1','');
|
|
|
|
html_end_box(true, true);
|
|
}
|
|
|
|
if (((isrv('id')) || (isrv('new'))) && (empty($data['data_template_id']))) {
|
|
html_start_box(__('Data Source'), '100%', true, 3, 'center', '');
|
|
|
|
$form_array = [];
|
|
|
|
foreach ($struct_data_source as $field_name => $field_array) {
|
|
$form_array += [$field_name => $struct_data_source[$field_name]];
|
|
|
|
if (($field_array['method'] != 'header') && ($field_array['method'] != 'spacer')) {
|
|
if (!(($use_data_template == false) || (!empty($data_template_data['t_' . $field_name])) || ($field_array['flags'] == 'NOTEMPLATE'))) {
|
|
$form_array[$field_name]['description'] = '';
|
|
}
|
|
|
|
$form_array[$field_name]['value'] = (isset($data[$field_name]) ? $data[$field_name] : '');
|
|
$form_array[$field_name]['form_id'] = (empty($data['id']) ? '0' : $data['id']);
|
|
|
|
if (!(($use_data_template == false) || (!empty($data_template_data['t_' . $field_name])) || ($field_array['flags'] == 'NOTEMPLATE'))) {
|
|
$form_array[$field_name]['method'] = 'template_' . $form_array[$field_name]['method'];
|
|
}
|
|
}
|
|
}
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => inject_form_variables($form_array, (isset($data) ? $data : []))
|
|
]
|
|
);
|
|
|
|
html_end_box(true, true);
|
|
|
|
// fetch ALL rrd's for this data source
|
|
if (!ierv('id')) {
|
|
$template_data_rrds = db_fetch_assoc_prepared('SELECT id, data_source_name
|
|
FROM data_template_rrd
|
|
WHERE local_data_id = ?
|
|
ORDER BY data_source_name',
|
|
[grv('id')]);
|
|
}
|
|
|
|
// select the first "rrd" of this data source by default
|
|
if (ierv('view_rrd')) {
|
|
srv('view_rrd', (isset($template_data_rrds[0]['id']) ? $template_data_rrds[0]['id'] : '0'));
|
|
}
|
|
|
|
// get more information about the rrd we chose
|
|
if (!ierv('view_rrd')) {
|
|
$local_data_template_rrd_id = db_fetch_cell_prepared('SELECT local_data_template_rrd_id
|
|
FROM data_template_rrd
|
|
WHERE id = ?',
|
|
[grv('view_rrd')]);
|
|
|
|
$rrd = db_fetch_row_prepared('SELECT *
|
|
FROM data_template_rrd
|
|
WHERE id = ?',
|
|
[grv('view_rrd')]);
|
|
|
|
$rrd_template = db_fetch_row_prepared('SELECT *
|
|
FROM data_template_rrd
|
|
WHERE id = ?',
|
|
[$local_data_template_rrd_id]);
|
|
|
|
$header_label = __('[edit: %s]', $rrd['data_source_name']);
|
|
} else {
|
|
$header_label = '';
|
|
|
|
$local_data_template_rrd_id = 0;
|
|
|
|
$rrd = [];
|
|
$rrd_template = [];
|
|
}
|
|
|
|
$i = 0;
|
|
|
|
if (isset($template_data_rrds)) {
|
|
if (cacti_sizeof($template_data_rrds)) {
|
|
// draw the data source tabs on the top of the page
|
|
print "<div class='tabs' style='float:left;'><nav><ul role='tablist'>";
|
|
|
|
foreach ($template_data_rrds as $template_data_rrd) {
|
|
print "<li class='subTab'><a " . (($template_data_rrd['id'] == grv('view_rrd')) ? "class='pic selected'" : "class='pic'") . " href='" . htmle('data_sources.php?action=ds_edit&id=' . grv('id') . '&view_rrd=' . $template_data_rrd['id']) . "'>$i: " . htmle($template_data_rrd['data_source_name']) . '</a>' . ($use_data_template == false ? " <a class='pic deleteMarker ti ti-x cactiPostAction' href='#' data-url='" . html_escape_url('data_sources.php?action=rrd_remove&id=' . $template_data_rrd['id'] . '&local_data_id=' . grv('id')) . "' title='" . __esc('Delete') . "'></a>" : '') . '</li>';
|
|
|
|
$i++;
|
|
}
|
|
|
|
print '</ul></nav></div>';
|
|
} elseif (cacti_sizeof($template_data_rrds) === 1) {
|
|
srv('view_rrd', $template_data_rrds[0]['id']);
|
|
}
|
|
}
|
|
|
|
html_start_box('', '100%', true, 3, 'center', '');
|
|
|
|
print "<div class='tableHeader'>
|
|
<div class='tableSubHeaderColumn left'>
|
|
" . __esc('Data Source Item %s', $header_label) . "
|
|
</div>
|
|
<div class='tableSubHeaderColumn right'>
|
|
" . ((!ierv('id') && (empty($data_template['id']))) ? "<a class='linkOverDark cactiPostAction' href='#' data-url='" . html_escape_url('data_sources.php?action=rrd_add&id=' . grv('id')) . "'>" . __('New') . '</a> ' : '') . '
|
|
</div>
|
|
</div>';
|
|
|
|
// data input fields list
|
|
if ((empty($data['data_input_id'])) || (db_fetch_cell_prepared('SELECT type_id FROM data_input WHERE id = ?', [$data['data_input_id']]) > '1')) {
|
|
unset($struct_data_source_item['data_input_field_id']);
|
|
} else {
|
|
$struct_data_source_item['data_input_field_id']['sql'] = "SELECT id,CONCAT(data_name,' - ',name) as name FROM data_input_fields WHERE data_input_id=" . $data['data_input_id'] . " and input_output='out' and update_rra='on' order by data_name,name";
|
|
}
|
|
|
|
$form_array = [];
|
|
|
|
foreach ($struct_data_source_item as $field_name => $field_array) {
|
|
$form_array += [$field_name => $struct_data_source_item[$field_name]];
|
|
|
|
if (($field_array['method'] != 'header') && ($field_array['method'] != 'spacer')) {
|
|
if (!(($use_data_template == false) || ($rrd_template['t_' . $field_name] == 'on'))) {
|
|
$form_array[$field_name]['description'] = '';
|
|
}
|
|
|
|
$form_array[$field_name]['value'] = $rrd[$field_name] ?? '';
|
|
|
|
if (!(($use_data_template == false) || ($rrd_template['t_' . $field_name] == 'on'))) {
|
|
$form_array[$field_name]['method'] = 'template_' . $form_array[$field_name]['method'];
|
|
}
|
|
}
|
|
}
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => [
|
|
'data_template_rrd_id' => [
|
|
'method' => 'hidden',
|
|
'value' => $rrd['id'] ?? 0
|
|
],
|
|
'local_data_template_rrd_id' => [
|
|
'method' => 'hidden',
|
|
'value' => $rrd['local_data_template_rrd_id'] ?? 0
|
|
]
|
|
] + $form_array
|
|
]
|
|
);
|
|
|
|
html_end_box(true, true);
|
|
|
|
// data source data goes here
|
|
data_edit(false);
|
|
|
|
form_hidden_box('current_rrd', grv('view_rrd'), '0');
|
|
}
|
|
|
|
// display the debug mode box if the user wants it
|
|
if ((isset($_SESSION['ds_debug_mode'])) && (isrv('id'))) {
|
|
print "<div class='cactiTable'>";
|
|
|
|
print "<div class='tableHeader'>";
|
|
print __('Data Source Debug');
|
|
print '</div>';
|
|
|
|
print "<div class='tableRow'>";
|
|
print '<pre>' . htmle(rrdtool_function_create(grv('id'), true)) . '</pre>';
|
|
print '</div>';
|
|
|
|
print '</div>';
|
|
}
|
|
|
|
// display the debug mode box if the user wants it
|
|
if (isset($_SESSION['ds_info_mode']) && isrv('id')) {
|
|
print "<div class='cactiTable'><div class='tableRow'>";
|
|
|
|
$rrd_info = rrdtool_function_info(grv('id'));
|
|
|
|
if (cacti_sizeof($rrd_info) && cacti_sizeof($rrd_info['rra'])) {
|
|
$diff = rrdtool_cacti_compare(grv('id'), $rrd_info);
|
|
|
|
rrdtool_info2html($rrd_info, $diff);
|
|
|
|
if (cacti_sizeof($diff)) {
|
|
$has_tune = isset($diff['tune']) && cacti_sizeof($diff['tune']);
|
|
|
|
// offer a one-click repair when the web server can write the
|
|
// RRDfile, otherwise present the commands to run by hand
|
|
if ($has_tune && is_writeable(get_data_source_path(grv('id'), true))) {
|
|
html_start_box(__('RRDtool Tune Info [ %s ]', '<a href="#" class="cactiPostAction linkEditMain" data-url="' . html_escape_url('data_sources.php?action=ds_repair&id=' . grv('id')) . '" style="cursor:pointer;">' . __('Apply Suggested Fixes') . '</a>'), '100%', true, 3, 'center', '');
|
|
} elseif ($has_tune) {
|
|
html_start_box(__('RRDtool Tune Info [ %s ]', __('Run Fix from Command Line')), '100%', true, 3, 'center', '');
|
|
} else {
|
|
html_start_box(__('RRDtool Tune Info'), '100%', true, 3, 'center', '');
|
|
}
|
|
|
|
rrdtool_tune($rrd_info['filename'], $diff, true);
|
|
|
|
html_end_box(true, true);
|
|
}
|
|
}
|
|
|
|
print '</div></div>';
|
|
}
|
|
|
|
if ((isrv('id')) || (isrv('new'))) {
|
|
form_hidden_box('save_component_data_source','1','');
|
|
} else {
|
|
form_hidden_box('save_component_data_source_new','1','');
|
|
}
|
|
|
|
form_save_button('data_sources.php');
|
|
|
|
api_plugin_hook('data_source_edit_bottom');
|
|
|
|
bottom_footer();
|
|
}
|
|
|
|
function get_poller_interval(int $seconds, int $data_source_profile_id) : string {
|
|
if ($seconds == 0 || $data_source_profile_id == 0) {
|
|
return '<em>' . __('External') . '</em>';
|
|
}
|
|
|
|
if ($seconds < 60) {
|
|
return '<em>' . __('%d Seconds', $seconds) . '</em>';
|
|
}
|
|
|
|
if ($seconds == 60) {
|
|
return __('1 Minute');
|
|
} else {
|
|
return '<em>' . __('%d Minutes', ($seconds / 60)) . '</em>';
|
|
}
|
|
}
|
|
|
|
function data_sources() : void {
|
|
global $actions, $item_rows, $sampling_intervals;
|
|
|
|
draw_data_source_filter(true);
|
|
|
|
if (grv('rows') == -1) {
|
|
$rows = read_config_option('num_rows_table');
|
|
} else {
|
|
$rows = grv('rows');
|
|
}
|
|
|
|
if (gfrv('host_id') > 0) {
|
|
$host = db_fetch_row_prepared('SELECT hostname
|
|
FROM host
|
|
WHERE id = ?',
|
|
[grv('host_id')]);
|
|
} else {
|
|
$host = [];
|
|
}
|
|
|
|
$sql_where1 = '';
|
|
$sql_where2 = '';
|
|
$sql_params1 = [];
|
|
$sql_params2 = [];
|
|
|
|
// form the 'where' clause for our main sql query
|
|
if (grv('rfilter') != '') {
|
|
$sql_where1 = 'WHERE
|
|
(
|
|
dtd.name_cache RLIKE ? OR
|
|
dtd.local_data_id RLIKE ? OR
|
|
dt.name RLIKE ? OR
|
|
dl.id = ?
|
|
)';
|
|
|
|
$sql_params1[] = grv('rfilter');
|
|
$sql_params1[] = grv('rfilter');
|
|
$sql_params1[] = grv('rfilter');
|
|
$sql_params1[] = grv('rfilter');
|
|
}
|
|
|
|
if (ierv('host_id')) {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' (dl.host_id = 0 OR dl.host_id IS NULL)';
|
|
$sql_where2 .= ' AND (gl.host_id = 0 OR gl.host_id IS NULL)';
|
|
} elseif (grv('host_id') > 0) {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' dl.host_id = ?';
|
|
$sql_params1[] = grv('host_id');
|
|
$sql_where2 .= ' AND gl.host_id = ?';
|
|
$sql_params2[] = grv('host_id');
|
|
}
|
|
|
|
if (isrv('errored') && grv('errored') == 'true') {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' dl.errored = 1';
|
|
}
|
|
|
|
if (ierv('site_id')) {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' (h.site_id=0 OR h.site_id IS NULL)';
|
|
$sql_where2 .= ' AND (h.site_id=0 OR h.site_id IS NULL)';
|
|
} elseif (grv('site_id') > 0) {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' h.site_id = ?';
|
|
$sql_params1[] = grv('site_id');
|
|
$sql_where2 .= ' AND h.site_id = ?';
|
|
$sql_params2[] = grv('site_id');
|
|
}
|
|
|
|
if (grv('template_id') == '0') {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' dtd.data_template_id = 0';
|
|
} elseif (grv('template_id') > 0) {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' dtd.data_template_id = ?';
|
|
$sql_params1[] = grv('template_id');
|
|
}
|
|
|
|
if (grv('profile') > 0) {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' dtd.data_source_profile_id = ?';
|
|
$sql_params1[] = grv('profile');
|
|
}
|
|
|
|
if (grv('status') == '1') {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' (dtd.active = "on" AND h.disabled = "")';
|
|
} elseif (grv('status') == '2') {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' (dtd.active = "" OR h.disabled != "")';
|
|
} elseif (grv('status') == '3') {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND' : 'WHERE') . ' (dl.snmp_index = "" AND dl.snmp_query_id > 0)';
|
|
}
|
|
|
|
$sql_order = get_order_string();
|
|
$sql_limit = ' LIMIT ' . ($rows * (grv('page') - 1)) . ',' . $rows;
|
|
|
|
if (grv('orphans') == 'true') {
|
|
$sql_where1 .= ($sql_where1 != '' ? ' AND ' : 'WHERE ') . '((dl.snmp_index = "" AND dl.snmp_query_id > 0) OR graph_items = 0 IS NULL OR dl.orphan = 1)';
|
|
|
|
$orphan_join = "LEFT JOIN (
|
|
SELECT dtr.local_data_id, COUNT(DISTINCT gti.task_item_id) AS graph_items
|
|
FROM data_template_rrd AS dtr
|
|
LEFT JOIN graph_templates_item AS gti
|
|
ON dtr.id = gti.task_item_id
|
|
INNER JOIN graph_local AS gl
|
|
ON gl.id = gti.local_graph_id
|
|
LEFT JOIN host AS h
|
|
ON h.id = gl.host_id
|
|
WHERE dtr.local_data_id > 0
|
|
$sql_where2
|
|
GROUP BY local_data_id
|
|
) AS dtr
|
|
ON dl.id = dtr.local_data_id";
|
|
|
|
$sql = "SELECT COUNT(*)
|
|
FROM data_local AS dl
|
|
INNER JOIN data_template_data AS dtd
|
|
ON dl.id = dtd.local_data_id
|
|
INNER JOIN data_template AS dt
|
|
ON dt.id = dl.data_template_id
|
|
LEFT JOIN host AS h
|
|
ON h.id = dl.host_id
|
|
$orphan_join
|
|
$sql_where1";
|
|
|
|
if (cacti_sizeof($sql_params2)) {
|
|
$merged_params = array_merge($sql_params2, $sql_params1);
|
|
} else {
|
|
$merged_params = $sql_params1;
|
|
}
|
|
|
|
$total_rows = get_total_row_data($_SESSION[SESS_USER_ID], $sql, $merged_params, 'data_source');
|
|
|
|
$data_sources = db_fetch_assoc_prepared("SELECT dtd.local_data_id,
|
|
dtd.name_cache, dtd.active, dtd.rrd_step, dt.name AS data_template_name,
|
|
dl.host_id, dtd.data_source_profile_id
|
|
FROM data_local AS dl
|
|
INNER JOIN data_template_data AS dtd
|
|
ON dl.id = dtd.local_data_id
|
|
INNER JOIN data_template AS dt
|
|
ON dt.id = dl.data_template_id
|
|
LEFT JOIN host AS h
|
|
ON h.id = dl.host_id
|
|
$orphan_join
|
|
$sql_where1
|
|
$sql_order
|
|
$sql_limit",
|
|
$merged_params);
|
|
} else {
|
|
$sql = "SELECT COUNT(*)
|
|
FROM data_local AS dl
|
|
INNER JOIN data_template_data AS dtd
|
|
ON dl.id=dtd.local_data_id
|
|
LEFT JOIN data_template AS dt
|
|
ON dt.id = dl.data_template_id
|
|
LEFT JOIN host AS h
|
|
ON h.id = dl.host_id
|
|
$sql_where1";
|
|
|
|
$total_rows = get_total_row_data($_SESSION[SESS_USER_ID], $sql, $sql_params1, 'data_source');
|
|
|
|
$data_sources = db_fetch_assoc_prepared("SELECT dtd.local_data_id, dtd.name_cache, dtd.active,
|
|
dtd.rrd_step, dt.name AS data_template_name, dl.host_id, dtd.data_source_profile_id
|
|
FROM data_local AS dl
|
|
INNER JOIN data_template_data AS dtd
|
|
ON dl.id=dtd.local_data_id
|
|
LEFT JOIN data_template AS dt
|
|
ON dt.id = dl.data_template_id
|
|
LEFT JOIN host AS h
|
|
ON h.id = dl.host_id
|
|
$sql_where1
|
|
$sql_order
|
|
$sql_limit",
|
|
$sql_params1);
|
|
}
|
|
|
|
$nav = html_nav_bar('data_sources.php', MAX_DISPLAY_PAGES, grv('page'), $rows, $total_rows, 7, __('Data Sources'), 'page', 'main');
|
|
|
|
form_start('data_sources.php', 'chk');
|
|
|
|
print $nav;
|
|
|
|
html_start_box('', '100%', false, 3, 'center', '');
|
|
|
|
$display_text = [
|
|
'name_cache' => [
|
|
'display' => __('Data Source Name'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The name of this Data Source. Generally programmatically generated from the Data Template definition.')
|
|
],
|
|
'local_data_id' => [
|
|
'display' => __('ID'),
|
|
'align' => 'right',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The internal database ID for this Data Source. Useful when performing automation or debugging.')
|
|
],
|
|
'nosort0' => [
|
|
'display' => __('Graphs'),
|
|
'align' => 'center',
|
|
'sort' => '',
|
|
'tip' => __('The number of Graphs and Aggregate Graphs that are using the Data Source.')
|
|
],
|
|
'nosort1' => [
|
|
'display' => __('Poller Interval'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The frequency that data is collected for this Data Source.')
|
|
],
|
|
'nosort2' => [
|
|
'display' => __('Deletable'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('If this Data Source is no long in use by Graphs, it can be Deleted.')
|
|
],
|
|
'active' => [
|
|
'display' => __('Active'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('Whether or not data will be collected for this Data Source. Controlled at the Data Template level.')
|
|
],
|
|
'data_template_name' => [
|
|
'display' => __('Template Name'),
|
|
'align' => 'left',
|
|
'sort' => 'ASC',
|
|
'tip' => __('The Data Template that this Data Source was based upon.')
|
|
]
|
|
];
|
|
|
|
html_header_sort_checkbox($display_text, grv('sort_column'), grv('sort_direction'), false);
|
|
|
|
$i = 0;
|
|
|
|
if (cacti_sizeof($data_sources)) {
|
|
foreach ($data_sources as $data_source) {
|
|
if (api_data_source_deletable($data_source['local_data_id'])) {
|
|
$disabled = false;
|
|
} else {
|
|
$disabled = true;
|
|
}
|
|
|
|
$data_source['data_template_name'] = htmle($data_source['data_template_name']);
|
|
|
|
// keep copy of data source for comparison
|
|
$data_source_orig = $data_source;
|
|
$data_source = api_plugin_hook_function('data_sources_table', $data_source);
|
|
|
|
// we're escaping strings here, so no need to escape them on form_selectable_cell
|
|
if (empty($data_source['data_template_name'])) {
|
|
$data_template_name = '<em>' . __('None') . '</em>';
|
|
} elseif ($data_source_orig['data_template_name'] != $data_source['data_template_name']) {
|
|
// was changed by plugin, plugin has to take care for html-escaping
|
|
$data_template_name = $data_source['data_template_name'];
|
|
} elseif (grv('rfilter') != '') {
|
|
$data_template_name = filter_value($data_source['data_template_name'], grv('rfilter'));
|
|
} else {
|
|
$data_template_name = htmle($data_source['data_template_name']);
|
|
}
|
|
|
|
$graphs_aggregates_url = get_graphs_aggregates_url($data_source['local_data_id']);
|
|
|
|
if ($data_source['name_cache'] == '') {
|
|
$name = __('Damaged Data Source Name');
|
|
} else {
|
|
$name = $data_source['name_cache'];
|
|
}
|
|
|
|
form_alternate_row('line' . $data_source['local_data_id'], true, $disabled);
|
|
|
|
$url = 'data_sources.php?action=ds_edit&id=' . $data_source['local_data_id'];
|
|
|
|
form_selectable_cell(filter_value($name, grv('rfilter'), $url), $data_source['local_data_id']);
|
|
form_selectable_cell($data_source['local_data_id'], $data_source['local_data_id'], '', 'right');
|
|
|
|
// Show link to Graphs and Aggregates
|
|
form_selectable_cell($graphs_aggregates_url, $data_source['local_data_id'], '', 'center');
|
|
|
|
form_selectable_cell(get_poller_interval($data_source['rrd_step'], $data_source['data_source_profile_id']), $data_source['local_data_id']);
|
|
form_selectable_cell(api_data_source_deletable($data_source['local_data_id']) ? __('Yes') : __('No'), $data_source['local_data_id']);
|
|
form_selectable_cell(($data_source['active'] == 'on' ? __('Yes') : __('No')), $data_source['local_data_id']);
|
|
form_selectable_cell($data_template_name, $data_source['local_data_id']);
|
|
form_checkbox_cell($name, $data_source['local_data_id'], $disabled);
|
|
|
|
form_end_row();
|
|
}
|
|
} else {
|
|
print "<tr class='tableRow odd'><td colspan='" . (cacti_sizeof($display_text) + 1) . "'><em>" . __('No Data Sources Found') . '</em></td></tr>';
|
|
}
|
|
|
|
html_end_box(false);
|
|
|
|
if (cacti_sizeof($data_sources)) {
|
|
print $nav;
|
|
}
|
|
|
|
// draw the dropdown containing a list of available actions for this form
|
|
draw_actions_dropdown($actions);
|
|
|
|
form_end();
|
|
}
|
|
|
|
function get_graphs_aggregates_url(int $local_data_id) : string {
|
|
$graphs = db_fetch_row_prepared('SELECT GROUP_CONCAT(DISTINCT gl.id) AS graphs, COUNT(DISTINCT gl.id) AS total
|
|
FROM data_local AS dl
|
|
INNER JOIN data_template_rrd AS dtr
|
|
ON dl.id = dtr.local_data_id
|
|
INNER JOIN graph_templates_item AS gti
|
|
ON gti.task_item_id = dtr.id
|
|
INNER JOIN graph_local AS gl
|
|
ON gl.id = gti.local_graph_id
|
|
LEFT JOIN aggregate_graphs AS ag
|
|
ON ag.local_graph_id = gl.id
|
|
WHERE dl.id = ?
|
|
AND ag.local_graph_id IS NULL',
|
|
[$local_data_id]);
|
|
|
|
$aggregates = db_fetch_row_prepared('SELECT GROUP_CONCAT(DISTINCT gl.id) AS graphs, COUNT(DISTINCT gl.id) AS total
|
|
FROM data_local AS dl
|
|
INNER JOIN data_template_rrd AS dtr
|
|
ON dl.id = dtr.local_data_id
|
|
INNER JOIN graph_templates_item AS gti
|
|
ON gti.task_item_id = dtr.id
|
|
INNER JOIN graph_local AS gl
|
|
ON gl.id = gti.local_graph_id
|
|
INNER JOIN aggregate_graphs AS ag
|
|
ON ag.local_graph_id = gl.id
|
|
WHERE dl.id = ?',
|
|
[$local_data_id]);
|
|
|
|
$url = '';
|
|
|
|
if (cacti_sizeof($graphs) && $graphs['total'] > 0) {
|
|
$url .= '<a class="linkEditMain" title="' . __('Graphs') . '" href="graphs.php?reset=1&custom=true&local_graph_ids=' . $graphs['graphs'] . '">' . $graphs['total'] . '</a>';
|
|
} else {
|
|
$url .= '<span title="' . __('No Graphs') . '">0</span>';
|
|
}
|
|
|
|
if (cacti_sizeof($aggregates) && $aggregates['total'] > 0) {
|
|
$url .= ' / <a class="linkEditMain" title="' . __('Aggregates') . '" href="aggregate_graphs.php?reset=1&custom=true&local_graph_ids=' . $aggregates['graphs'] . '">' . $aggregates['total'] . '</a>';
|
|
} else {
|
|
$url .= ' / <span title="' . __('No Aggregates') . '">0</span>';
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
function create_data_sources_filter(string $session_var) : array {
|
|
global $item_rows, $page_refresh_interval;
|
|
|
|
$all = ['-1' => __('All')];
|
|
$any = ['-1' => __('Any')];
|
|
$none = ['0' => __('None')];
|
|
$deleted = ['-2' => __('Deleted/Invalid')];
|
|
|
|
$sites = array_rekey(
|
|
db_fetch_assoc('SELECT id, name
|
|
FROM sites
|
|
ORDER BY name'),
|
|
'id', 'name'
|
|
);
|
|
$sites = $any + $sites;
|
|
|
|
$profiles = array_rekey(
|
|
db_fetch_assoc('SELECT id, name
|
|
FROM data_source_profiles
|
|
ORDER BY name'),
|
|
'id', 'name'
|
|
);
|
|
$profiles = $all + $profiles;
|
|
|
|
$status = [
|
|
'-1' => __('All'),
|
|
'1' => __('Enabled'),
|
|
'2' => __('Disabled'),
|
|
'3' => __('Bad Indexes')
|
|
];
|
|
|
|
$sql_where = '';
|
|
$sql_params = [];
|
|
|
|
if (isrv('host_id')) {
|
|
$host_id = gfrv('host_id');
|
|
} elseif (isset($_SESSION[$session_var . '_host_id'])) {
|
|
$host_id = $_SESSION[$session_var . '_host_id'];
|
|
} else {
|
|
$host_id = '-1';
|
|
}
|
|
|
|
if ($host_id > 0) {
|
|
// for the templates dropdown
|
|
$sql_where = 'AND h.id = ?';
|
|
$sql_params[] = $host_id;
|
|
|
|
$hostname = db_fetch_cell_prepared('SELECT description
|
|
FROM host
|
|
WHERE id = ?',
|
|
[$host_id]);
|
|
} elseif ($host_id == 0) {
|
|
$host_id = '0';
|
|
$hostname = __('None');
|
|
} else {
|
|
$host_id = '-1';
|
|
$hostname = __('Any');
|
|
}
|
|
|
|
if (gfrv('site_id') > 0) {
|
|
$sql_where = 'AND site_id = ?';
|
|
$sql_params[] = grv('site_id');
|
|
}
|
|
|
|
$templates = array_rekey(
|
|
db_fetch_assoc_prepared("SELECT DISTINCT dt.id, dt.name
|
|
FROM data_template AS dt
|
|
INNER JOIN data_template_data AS dtd
|
|
ON dt.id = dtd.data_template_id
|
|
LEFT JOIN data_local AS dl
|
|
ON dtd.local_data_id = dl.id
|
|
LEFT JOIN host AS h
|
|
ON dl.host_id = h.id
|
|
WHERE dtd.local_data_id > 0
|
|
$sql_where
|
|
ORDER BY dt.name",
|
|
$sql_params),
|
|
'id', 'name'
|
|
);
|
|
|
|
$templates = $any + $templates;
|
|
|
|
return [
|
|
'rows' => [
|
|
[
|
|
'site_id' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Site'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'array' => $sites,
|
|
'value' => '-1'
|
|
],
|
|
'host_id' => [
|
|
'method' => 'drop_callback',
|
|
'friendly_name' => __('Device'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'sql' => 'SELECT DISTINCT id, description AS name FROM host ORDER BY description',
|
|
'action' => 'ajax_hosts',
|
|
'id' => $host_id,
|
|
'value' => $hostname,
|
|
'on_change' => 'applyFilter()'
|
|
],
|
|
'template_id' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Template'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'array' => $templates,
|
|
'value' => '-1'
|
|
]
|
|
],
|
|
[
|
|
'profile' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Profile'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'array' => $profiles,
|
|
'value' => '-1'
|
|
],
|
|
'status' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Status'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'array' => $status,
|
|
'value' => '-1'
|
|
],
|
|
'orphans' => [
|
|
'method' => 'filter_checkbox',
|
|
'friendly_name' => __('Orphaned'),
|
|
'filter' => FILTER_VALIDATE_REGEXP,
|
|
'filter_options' => ['options' => ['regexp' => '(true|false)']],
|
|
'default' => '',
|
|
'pageset' => true,
|
|
'value' => gnrv('orphans')
|
|
],
|
|
'errored' => [
|
|
'method' => 'filter_checkbox',
|
|
'friendly_name' => __('Errored'),
|
|
'filter' => FILTER_VALIDATE_REGEXP,
|
|
'filter_options' => ['options' => ['regexp' => '(true|false)']],
|
|
'default' => '',
|
|
'pageset' => true,
|
|
'value' => gnrv('errored')
|
|
]
|
|
],
|
|
[
|
|
'rfilter' => [
|
|
'method' => 'textbox',
|
|
'friendly_name' => __('Search'),
|
|
'filter' => FILTER_VALIDATE_IS_REGEX,
|
|
'placeholder' => __('Enter a search term'),
|
|
'size' => '55',
|
|
'default' => '',
|
|
'pageset' => true,
|
|
'max_length' => '120',
|
|
'value' => ''
|
|
],
|
|
'rows' => [
|
|
'method' => 'drop_array',
|
|
'friendly_name' => __('Data Sources'),
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1',
|
|
'pageset' => true,
|
|
'array' => $item_rows,
|
|
'value' => '-1'
|
|
]
|
|
]
|
|
],
|
|
'buttons' => [
|
|
'go' => [
|
|
'method' => 'submit',
|
|
'display' => __('Go'),
|
|
'title' => __('Apply filter to table'),
|
|
],
|
|
'clear' => [
|
|
'method' => 'button',
|
|
'display' => __('Clear'),
|
|
'title' => __('Reset filter to default values'),
|
|
]
|
|
],
|
|
'sort' => [
|
|
'sort_column' => 'name_cache',
|
|
'sort_direction' => 'DESC'
|
|
]
|
|
];
|
|
}
|
|
|
|
function draw_data_source_filter(bool $render = false) : void {
|
|
$filters = create_data_sources_filter('sess_ds');
|
|
|
|
if (read_config_option('grds_creation_method') == 1) {
|
|
if (gfrv('host_id') == '-1') {
|
|
$new_host_id = 0;
|
|
} else {
|
|
$new_host_id = grv('host_id');
|
|
}
|
|
|
|
$add_url = htmle('data_sources.php?action=ds_edit&host_id=' . $new_host_id);
|
|
} else {
|
|
$add_url = '';
|
|
}
|
|
|
|
if (gfrv('host_id') == -1) {
|
|
$header = __('Data Sources [ All Devices ]');
|
|
} elseif (grv('host_id') == 0) {
|
|
$header = __('Data Sources [ Non Device Based ]');
|
|
} elseif (grv('host_id') > 0) {
|
|
$description = db_fetch_cell_prepared('SELECT description FROM host WHERE id = ?', [grv('host_id')]);
|
|
$header = __esc('Data Sources [ %s ]', $description);
|
|
} else {
|
|
$header = __('Data Sources [ All Devices ]');
|
|
}
|
|
|
|
// create the page filter
|
|
$pageFilter = new CactiTableFilter($header, 'data_sources.php', 'form_data_sources', 'sess_ds', $add_url);
|
|
|
|
$pageFilter->rows_label = __('Data Sources');
|
|
$pageFilter->set_filter_array($filters);
|
|
|
|
if ($render) {
|
|
$pageFilter->render();
|
|
} else {
|
|
$pageFilter->sanitize();
|
|
}
|
|
}
|