mirror of
https://github.com/Cacti/plugin_thold.git
synced 2026-10-23 03:27:05 +09:00
* issue: shorten thold VRULE graph drilldown button title The graph action menu now renders each drilldown button's title as a visible label, so 'Toggle Threshold VRULES %s' reads as an overlong menu entry. Shorten it to 'Toggle VRULES %s', add a unit test exercising thold_graph_button()'s VRULE branch (with the two missing request-var stubs it needs) so the changed line stays covered, and regenerate locales/po/cacti.pot. * Update copyright notice in TholdGraphButtonTest.php Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix Last-Translator entry in cacti.pot Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * test: cover the VRULEs-enabled 'Off' title branch for patch coverage --------- Co-authored-by: TheWitness <1439914+TheWitness@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2429 lines
82 KiB
PHP
2429 lines
82 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/ |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
/**
|
|
* Registers all of this plugin's hooks (settings, breadcrumbs, poller
|
|
* output/bottom, device/data-source/graph management actions, graph
|
|
* buttons, SNMP agent cache, clog regex, and more) and permission
|
|
* realms with Cacti, then creates/updates its database schema and SNMP
|
|
* agent cache. Cacti plugin API entry point invoked when the plugin is
|
|
* installed or re-registered during an upgrade. A no-op returning false
|
|
* on Cacti versions older than 1.2.
|
|
*
|
|
* @param bool $upgrade Whether this call is for an in-place upgrade of
|
|
* an already-installed plugin (true) rather than
|
|
* a fresh install (false, default); affects
|
|
* whether the database is upgraded vs. freshly
|
|
* created.
|
|
*
|
|
* @return bool|void False when the Cacti version is too old; otherwise
|
|
* no explicit return value.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* check the Cacti version and locate the database
|
|
* include file.
|
|
*/
|
|
function plugin_thold_install($upgrade = false) {
|
|
global $config;
|
|
|
|
if (version_compare($config['cacti_version'], '1.2') < 0) {
|
|
return false;
|
|
}
|
|
|
|
// The reinstall below registers the plugin's hooks/realms and provisions
|
|
// its schema against a live Cacti database (include_once database.php plus
|
|
// thold_setup_database()/thold_upgrade_database()), so it cannot run in the
|
|
// isolated unit process.
|
|
// @codeCoverageIgnoreStart
|
|
$plugin = 'thold';
|
|
|
|
// Insert CSS and JavaScript
|
|
api_plugin_register_hook($plugin, 'page_head', 'thold_page_head', 'setup.php');
|
|
|
|
// Add the Thold tab
|
|
api_plugin_register_hook($plugin, 'top_header_tabs', 'thold_show_tab', 'includes/tab.php');
|
|
api_plugin_register_hook($plugin, 'top_graph_header_tabs', 'thold_show_tab', 'includes/tab.php');
|
|
|
|
// Settings forms and arrays
|
|
api_plugin_register_hook($plugin, 'config_insert', 'thold_config_insert', 'includes/settings.php');
|
|
api_plugin_register_hook($plugin, 'config_arrays', 'thold_config_arrays', 'includes/settings.php');
|
|
api_plugin_register_hook($plugin, 'config_form', 'thold_config_form', 'includes/settings.php');
|
|
api_plugin_register_hook($plugin, 'config_settings', 'thold_config_settings', 'includes/settings.php');
|
|
|
|
// Breadcrumbs
|
|
api_plugin_register_hook($plugin, 'draw_navigation_text', 'thold_draw_navigation_text', 'includes/settings.php');
|
|
|
|
// Inline thold checks
|
|
api_plugin_register_hook($plugin, 'poller_output', 'thold_poller_output', 'includes/polling.php');
|
|
|
|
// Device Hooks
|
|
api_plugin_register_hook($plugin, 'device_action_array', 'thold_device_action_array', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'device_action_execute', 'thold_device_action_execute', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'device_action_prepare', 'thold_device_action_prepare', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'api_device_save', 'thold_api_device_save', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'host_edit_bottom', 'thold_host_edit_bottom', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'device_threshold_autocreate', 'thold_device_autocreate', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'device_template_change', 'thold_device_template_change', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'device_change_javascript', 'thold_host_edit_bottom', 'setup.php');
|
|
|
|
// Automation Hooks
|
|
api_plugin_register_hook($plugin, 'create_complete_graph_from_template', 'thold_create_graph_thold', 'setup.php');
|
|
|
|
// Hooks to enable thold maintenance
|
|
api_plugin_register_hook($plugin, 'poller_bottom', 'thold_poller_bottom', 'includes/polling.php');
|
|
|
|
// Setup buttons on Graph Pages
|
|
api_plugin_register_hook($plugin, 'graph_buttons', 'thold_graph_button', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'graph_buttons_thumbnails', 'thold_graph_button', 'setup.php');
|
|
|
|
// Data Source Hooks
|
|
api_plugin_register_hook($plugin, 'data_source_action_array', 'thold_data_source_action_array', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'data_source_action_prepare', 'thold_data_source_action_prepare', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'data_source_action_execute', 'thold_data_source_action_execute', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'data_source_remove', 'thold_data_source_remove', 'setup.php');
|
|
|
|
// Create Threshold from Data Source table
|
|
api_plugin_register_hook($plugin, 'data_sources_table', 'thold_data_sources_table', 'setup.php');
|
|
|
|
// Follow Graph Actions
|
|
api_plugin_register_hook($plugin, 'graphs_action_array', 'thold_graphs_action_array', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'graphs_action_prepare', 'thold_graphs_action_prepare', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'graphs_action_execute', 'thold_graphs_action_execute', 'setup.php');
|
|
|
|
// Follow Device Template Actions
|
|
api_plugin_register_hook($plugin, 'device_template_edit', 'thold_device_template_edit', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'device_template_top', 'thold_device_template_top', 'setup.php');
|
|
|
|
// Display Threshold Templates in Devices
|
|
api_plugin_register_hook($plugin, 'device_edit_pre_bottom', 'thold_device_edit_pre_bottom', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'device_top', 'thold_device_top', 'setup.php');
|
|
|
|
// Follow New Graph Actions
|
|
api_plugin_register_hook($plugin, 'api_device_new', 'thold_api_device_new', 'setup.php');
|
|
|
|
// Allow settings javascript hooks
|
|
api_plugin_register_hook('thold', 'settings_bottom', 'thold_settings_bottom', 'setup.php', true);
|
|
|
|
// Miscellaneous hooks
|
|
api_plugin_register_hook($plugin, 'graphs_new_top_links', 'thold_graphs_new', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'update_host_status', 'thold_update_host_status', 'includes/polling.php');
|
|
api_plugin_register_hook($plugin, 'user_admin_setup_sql_save', 'thold_user_admin_setup_sql_save', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'rrd_graph_graph_options', 'thold_rrd_graph_graph_options', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'snmpagent_cache_install', 'thold_snmpagent_cache_install', 'setup.php');
|
|
api_plugin_register_hook($plugin, 'clog_regex_array', 'thold_clog_regex_array', 'setup.php');
|
|
|
|
// Setup permissions
|
|
api_plugin_register_realm($plugin, 'thold.php', 'Configure Thresholds', 1);
|
|
api_plugin_register_realm($plugin, 'thold_templates.php', 'Configure Threshold Templates', 1);
|
|
api_plugin_register_realm($plugin, 'notify_lists.php,notify_queue.php', 'Manage Notification', 1);
|
|
api_plugin_register_realm($plugin, 'thold_graph.php,graph_thold.php,thold_view_failures.php,thold_view_normal.php,thold_view_recover.php,thold_view_recent.php,thold_view_host.php', 'View Thresholds', 1);
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/database.php');
|
|
|
|
if ($upgrade) {
|
|
thold_upgrade_database();
|
|
|
|
if (api_plugin_is_enabled($plugin)) {
|
|
api_plugin_enable_hooks($plugin);
|
|
}
|
|
} else {
|
|
thold_setup_database();
|
|
thold_snmpagent_cache_install();
|
|
}
|
|
// @codeCoverageIgnoreEnd
|
|
}
|
|
|
|
/**
|
|
* Removes this plugin's SNMP agent cache entries and all of its
|
|
* settings table rows. Cacti plugin API entry point invoked when the
|
|
* plugin is uninstalled.
|
|
*
|
|
* @return void
|
|
*/
|
|
function plugin_thold_uninstall() {
|
|
// Do any extra Uninstall stuff here
|
|
thold_snmpagent_cache_uninstall();
|
|
|
|
// Remove items from the settings table
|
|
db_execute('DELETE FROM settings
|
|
WHERE name LIKE "%thold%"');
|
|
}
|
|
|
|
/**
|
|
* Ensures the plugin's configuration/schema is up to date by delegating
|
|
* to plugin_thold_upgrade(). Cacti plugin API entry point invoked on
|
|
* relevant page loads to catch pending upgrades.
|
|
*
|
|
* @return bool Always true.
|
|
*/
|
|
function plugin_thold_check_config() {
|
|
// Here we will check to ensure everything is configured
|
|
plugin_thold_upgrade();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Detects a version mismatch between the running plugin code and the
|
|
* version recorded in plugin_config, and when found (and only on a
|
|
* small set of pages, to limit overhead), re-runs
|
|
* plugin_thold_install() in upgrade mode to re-register hooks and
|
|
* upgrade the database schema.
|
|
*
|
|
* @return bool True (whether or not an upgrade was actually needed);
|
|
* false only when called from a page that doesn't need
|
|
* this check.
|
|
*
|
|
* @global array $config Cacti global configuration array (declared but
|
|
* not used directly here).
|
|
*/
|
|
function plugin_thold_upgrade() {
|
|
// Here we will upgrade to the newest version
|
|
global $config;
|
|
|
|
// Let's only run this check if we are on a page that actually needs the data
|
|
$files = ['thold.php', 'thold_graph.php', 'thold_templates.php', 'poller.php', 'plugins.php', 'clog.php'];
|
|
|
|
if (!in_array(get_current_page(), $files, true)) {
|
|
return false;
|
|
}
|
|
|
|
$info = plugin_thold_version();
|
|
$current = $info['version'];
|
|
$old = db_fetch_cell('SELECT version
|
|
FROM plugin_config
|
|
WHERE directory="thold"');
|
|
|
|
if ($current != $old) {
|
|
plugin_thold_install(true);
|
|
thold_prune_files();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Reads this plugin's version and metadata from its INFO file. Cacti
|
|
* plugin API entry point used throughout the plugin to display version
|
|
* information.
|
|
*
|
|
* @return array The parsed INFO file's 'info' section (name, author,
|
|
* homepage, version, etc.).
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the plugin's INFO file.
|
|
*/
|
|
function plugin_thold_version() {
|
|
global $config;
|
|
$info = parse_ini_file($config['base_path'] . '/plugins/thold/INFO', true);
|
|
|
|
return $info['info'];
|
|
}
|
|
|
|
/**
|
|
* Checks whether this plugin's dependencies are satisfied. Cacti
|
|
* plugin API hook point; currently always reports dependencies as
|
|
* satisfied.
|
|
*
|
|
* @return bool Always true.
|
|
*/
|
|
function thold_check_dependencies() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the database session is running in MySQL/MariaDB
|
|
* STRICT SQL mode, which this plugin (or its schema) is incompatible
|
|
* with.
|
|
*
|
|
* @return bool False if STRICT mode is active, true otherwise.
|
|
*/
|
|
function plugin_thold_check_strict() {
|
|
$mode = db_fetch_cell('select @@global.sql_mode', false);
|
|
|
|
if (stristr($mode, 'strict') !== false) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Renders this plugin's graph-page action buttons (toggle threshold
|
|
* VRULE display, create-threshold link) for a graph, shown only to
|
|
* users with the relevant view/edit permissions. Registered as the
|
|
* 'graph_buttons' and 'graph_buttons_thumbnails' Cacti hooks.
|
|
*
|
|
* @param array $data The graph button hook payload; $data[1] contains
|
|
* the graph's local_graph_id and rra selection.
|
|
*
|
|
* @return void
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* build the Create Threshold link's URL.
|
|
*/
|
|
function thold_graph_button($data) {
|
|
global $config;
|
|
|
|
$local_graph_id = $data[1]['local_graph_id'];
|
|
|
|
$thold_id = db_fetch_cell_prepared('SELECT id
|
|
FROM thold_data
|
|
WHERE local_graph_id = ?',
|
|
[$local_graph_id]);
|
|
|
|
$rra_id = $data[1]['rra'];
|
|
|
|
if (isset_request_var('view_type') && !isempty_request_var('view_type')) {
|
|
$view_type = get_request_var('view_type');
|
|
} else {
|
|
set_request_var('view_type', 'tree');
|
|
$view_type = 'preview';
|
|
}
|
|
|
|
if (isset_request_var('graph_start') && !isempty_request_var('graph_start')) {
|
|
$start = get_filter_request_var('graph_start');
|
|
} else {
|
|
set_request_var('graph_start', '');
|
|
$start = time() - 3600;
|
|
}
|
|
|
|
if (isset_request_var('graph_end') && !isempty_request_var('graph_end')) {
|
|
$end = get_filter_request_var('graph_end');
|
|
} else {
|
|
set_request_var('graph_end', '');
|
|
$end = time();
|
|
}
|
|
|
|
if (isset_request_var('thold_vrule') || isset($_SESSION['sess_config_array']['thold_draw_vrules'])) {
|
|
if (isset_request_var('thold_vrule')) {
|
|
if (get_nfilter_request_var('thold_vrule') == 'on') {
|
|
$vrules = 'off';
|
|
} else {
|
|
$vrules = 'on';
|
|
}
|
|
|
|
$_SESSION['sess_config_array']['thold_draw_vrules'] = $vrules;
|
|
} else {
|
|
$vrules = $_SESSION['sess_config_array']['thold_draw_vrules'];
|
|
}
|
|
} else {
|
|
$vrules = 'off';
|
|
$_SESSION['sess_config_array']['thold_draw_vrules'] = $vrules;
|
|
}
|
|
|
|
$url = $_SERVER['REQUEST_URI'];
|
|
$url = str_replace('&thold_vrule=on', '', $url);
|
|
$url = str_replace('&thold_vrule=off', '', $url);
|
|
|
|
if (!substr_count($url, '?')) {
|
|
$separator = '?';
|
|
} else {
|
|
$separator = '&';
|
|
}
|
|
|
|
if (api_user_realm_auth('thold_graph.php') && !empty($thold_id)) {
|
|
// Two complete strings rather than a trailing On/Off fragment so the
|
|
// label stays grammatical when a translation reorders the words.
|
|
if ($vrules == 'on') {
|
|
$vrule_title = __esc('Toggle Threshold VRULES Off', 'thold');
|
|
} else {
|
|
$vrule_title = __esc('Toggle Threshold VRULES On', 'thold');
|
|
}
|
|
|
|
print '<a class="iconLink tholdVRule" href="' . html_escape($url . $separator . 'thold_vrule=' . $vrules) . '" title="' . $vrule_title . '"><i class="tholdVRules far fa-chart-bar"></i></a><br>';
|
|
}
|
|
|
|
// Add Threshold Creation button
|
|
if (api_user_realm_auth('thold.php')) {
|
|
if (isset_request_var('tree_id')) {
|
|
get_filter_request_var('tree_id');
|
|
}
|
|
|
|
if (isset_request_var('leaf_id')) {
|
|
get_filter_request_var('leaf_id');
|
|
}
|
|
|
|
$is_aggregate = db_fetch_cell_prepared('SELECT id
|
|
FROM aggregate_graphs
|
|
WHERE local_graph_id = ?',
|
|
[$local_graph_id]);
|
|
|
|
if (empty($is_aggregate)) {
|
|
print '<a class="iconLink" href="' . html_escape($config['url_path'] . 'plugins/thold/thold.php?action=add' . '&usetemplate=1&local_graph_id=' . $local_graph_id) . '" title="' . __esc('Create Threshold', 'thold') . '"><i class="tholdEdit fas fa-wrench"></i></a><br>';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Splits a string on any of several delimiter characters at once, by
|
|
* first collapsing all given delimiters down to the first one and then
|
|
* exploding on it.
|
|
*
|
|
* @param array $delimiters The list of delimiter characters/strings
|
|
* to split on (the first entry is used as
|
|
* the unified delimiter).
|
|
* @param string $string The string to split.
|
|
*
|
|
* @return array|false The resulting array of substrings, or false on
|
|
* failure.
|
|
*/
|
|
function thold_multiexplode($delimiters, $string) {
|
|
$ready = str_replace($delimiters, $delimiters[0], $string);
|
|
|
|
return @explode($delimiters[0], $ready);
|
|
}
|
|
|
|
/**
|
|
* Substitutes this plugin's `|thold:hi:<ds>|` / `|thold:low:<ds>|`
|
|
* graph text-item placeholder variables with each referenced data
|
|
* source's current threshold hi/low values, by mapping the graph's RRD
|
|
* definitions' data source names to their underlying local_data_id and
|
|
* looking up the matching threshold. Registered as the
|
|
* 'rrd_graph_graph_options' Cacti hook.
|
|
*
|
|
* @param array $g The graph rendering options array, including
|
|
* 'graph_defs' and 'txt_graph_items'.
|
|
*
|
|
* @return array The graph rendering options array with thold
|
|
* placeholder variables substituted in
|
|
* 'txt_graph_items'.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include.
|
|
*/
|
|
function thold_rrd_graph_graph_options($g) {
|
|
global $config;
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
// handle thold replacement variables
|
|
$needles = [];
|
|
$replacements = [];
|
|
|
|
// map the data_template_rrd_id's to the datasource names
|
|
$defs = explode("\\\n", $g['graph_defs'], -1);
|
|
|
|
if (is_array($defs)) {
|
|
foreach ($defs as $def) {
|
|
if (!substr_count($def, 'CDEF') && !substr_count($def, 'VDEF')) {
|
|
$ddef = thold_multiexplode(['"', "'"], $def);
|
|
$kdef = explode(':', $def);
|
|
$dsname = $kdef[2];
|
|
$temp1 = str_replace('.rrd', '', basename($ddef[1]));
|
|
|
|
if (substr_count(basename($ddef[1]), '_') == 0) {
|
|
$local_data_id = $temp1;
|
|
} else {
|
|
$temp2 = explode('_', $temp1);
|
|
$local_data_id = $temp2[sizeof($temp2) - 1];
|
|
}
|
|
$dsname = trim($dsname, "'\" ");
|
|
$data_template_rrd[$dsname] = $local_data_id;
|
|
|
|
// Map the dsnames to def id's for percentile
|
|
$ndef = explode('=', $kdef[1]);
|
|
$data_defs[$dsname] = $ndef[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
// look for any variables to replace
|
|
$txt_items = explode("\\\n", $g['txt_graph_items']);
|
|
|
|
foreach ($txt_items as $item) {
|
|
if (substr_count($item, '|thold')) {
|
|
preg_match("/\|thold\\\:(hi|low)\\\:(.+)\|/", $item, $matches);
|
|
|
|
if (count($matches) == 3) {
|
|
$needles[] = $matches[0];
|
|
$data_source = explode('|', $matches[2]);
|
|
|
|
// look up the data_id from the data source name and data_template_rrd
|
|
$data_template_rrd_id = db_fetch_cell_prepared('SELECT id
|
|
FROM data_template_rrd
|
|
WHERE local_data_id = ?
|
|
AND data_source_name = ?',
|
|
[$data_template_rrd[$data_source[0]], $data_source[0]]);
|
|
|
|
$thold_type = db_fetch_cell_prepared('SELECT thold_type
|
|
FROM thold_data
|
|
WHERE thold_enabled="on"
|
|
AND data_template_rrd_id = ?',
|
|
[$data_template_rrd_id]);
|
|
|
|
// fetch the value from thold
|
|
if ($thold_type == '') {
|
|
$value = '';
|
|
} elseif ($thold_type == 0 || $thold_type == 1) { // Hi/Low & Baseline
|
|
$value = db_fetch_cell_prepared('SELECT thold_' . $matches[1] . '
|
|
FROM thold_data
|
|
WHERE data_template_rrd_id = ?',
|
|
[$data_template_rrd_id]);
|
|
} elseif ($thold_type == 1) { // Time Based
|
|
$value = db_fetch_cell_prepared('SELECT time_' . $matches[1] . '
|
|
FROM thold_data
|
|
WHERE data_template_rrd_id = ?',
|
|
[$data_template_rrd_id]);
|
|
}
|
|
|
|
if ($value == '' || !is_numeric($value)) {
|
|
$replacements['|thold\:' . $matches[1] . '\:' . $data_source[0] . '|'] = 'strip';
|
|
} else {
|
|
$replacements['|thold\:' . $matches[1] . '\:' . $data_source[0] . '|'] = $value;
|
|
}
|
|
}
|
|
|
|
preg_match("/\|thold\\\:(warning_hi|warning_low)\\\:(.+)\|/", $item, $matches);
|
|
|
|
if (count($matches) == 3) {
|
|
$needles[] = $matches[0];
|
|
$data_source = explode('|', $matches[2]);
|
|
|
|
// look up the data_id from the data source name and data_template_rrd_id
|
|
$data_template_rrd_id = db_fetch_cell_prepared('SELECT id
|
|
FROM data_template_rrd
|
|
WHERE local_data_id = ?
|
|
AND data_source_name = ?',
|
|
[$data_template_rrd[$data_source[0]], $data_source[0]]);
|
|
|
|
$thold_type = db_fetch_cell_prepared('SELECT thold_type
|
|
FROM thold_data
|
|
WHERE thold_enabled="on"
|
|
AND data_template_rrd_id = ?',
|
|
[$data_template_rrd_id]);
|
|
|
|
// fetch the value from thold
|
|
if ($thold_type == '') {
|
|
$value = '';
|
|
} elseif ($thold_type == 0 || $thold_type == 1) { // Hi/Low & Baseline
|
|
$value = db_fetch_cell_prepared('SELECT thold_' . $matches[1] . '
|
|
FROM thold_data
|
|
WHERE data_template_rrd_id = ?',
|
|
[$data_template_rrd_id]);
|
|
} elseif ($thold_type == 1) { // Time Based
|
|
$value = db_fetch_cell_prepared('SELECT time_' . $matches[1] . '
|
|
FROM thold_data
|
|
WHERE data_template_rrd_id = ?',
|
|
[$data_template_rrd_id]);
|
|
}
|
|
|
|
if ($value == '' || !is_numeric($value)) {
|
|
$replacements['|thold\:' . $matches[1] . '\:' . $data_source[0] . '|'] = 'strip';
|
|
} else {
|
|
$replacements['|thold\:' . $matches[1] . '\:' . $data_source[0] . '|'] = $value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// do we have any needles to replace?
|
|
$i = 0;
|
|
$unsets = [];
|
|
|
|
if (is_array($replacements)) {
|
|
foreach ($txt_items as $item) {
|
|
foreach ($replacements as $key => $replace) {
|
|
// cacti_log('Key:' . $key . ', Replace:' . $replace, false);
|
|
if (substr_count($item, $key)) {
|
|
if ($replace == 'strip') {
|
|
$unsets[] = $i;
|
|
} else {
|
|
$txt_items[$i] = str_replace($key, $replace, $item);
|
|
}
|
|
}
|
|
}
|
|
|
|
$i++;
|
|
}
|
|
|
|
if (cacti_sizeof($unsets)) {
|
|
foreach ($unsets as $i) {
|
|
unset($txt_items[$i]);
|
|
}
|
|
}
|
|
|
|
$g['txt_graph_items'] = implode("\\\n", $txt_items);
|
|
}
|
|
|
|
$id = $g['graph_id'];
|
|
|
|
// print "<pre>"; print_r($g);print "</pre>";
|
|
|
|
if (isset($_SESSION['sess_config_array']['thold_draw_vrules']) && $_SESSION['sess_config_array']['thold_draw_vrules'] == 'on') {
|
|
$end = $g['end'];
|
|
|
|
if ($end < 0) {
|
|
$end = time() + $end;
|
|
}
|
|
$end++;
|
|
|
|
$start = $g['start'];
|
|
|
|
if ($start < 0) {
|
|
$start = $end + $start;
|
|
}
|
|
$start--;
|
|
|
|
if ($id) {
|
|
$rows = db_fetch_assoc_prepared('SELECT time, status
|
|
FROM plugin_thold_log
|
|
WHERE local_graph_id = ?
|
|
AND type = 0
|
|
AND time > ?
|
|
AND time < ?',
|
|
[$id, $start, $end]);
|
|
|
|
if (cacti_sizeof($rows)) {
|
|
$color = '';
|
|
|
|
foreach ($rows as $row) {
|
|
switch($row['status']) {
|
|
case '3':
|
|
$color = '#CC6600';
|
|
|
|
break;
|
|
case '4':
|
|
$color = '#FF0000';
|
|
|
|
break;
|
|
case '5':
|
|
$color = '#00FF00';
|
|
|
|
break;
|
|
default:
|
|
$color = '';
|
|
|
|
break;
|
|
}
|
|
|
|
if ($color != '') {
|
|
$g['graph_defs'] .= 'VRULE:' . $row['time'] . $color . ' \\' . "\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$tholds_w_hrule = db_fetch_assoc_prepared('SELECT *
|
|
FROM thold_data
|
|
WHERE thold_enabled = 1
|
|
AND data_type IN (0, 1, 2)
|
|
AND (thold_hrule_alert > 0 || thold_hrule_warning > 0)
|
|
&& local_graph_id = ?',
|
|
[$id]);
|
|
|
|
$thold_id = 0;
|
|
$txt_graph_items = '';
|
|
|
|
if (cacti_sizeof($tholds_w_hrule)) {
|
|
foreach ($tholds_w_hrule as $t) {
|
|
// Adjust number for graph
|
|
thold_modify_values_by_cdef($t);
|
|
|
|
$baseu = db_fetch_cell_prepared('SELECT base_value
|
|
FROM graph_templates_graph
|
|
WHERE local_graph_id = ?',
|
|
[$t['local_graph_id']]);
|
|
|
|
if ($t['data_type'] == 2) {
|
|
$suffix = false;
|
|
} else {
|
|
$suffix = true;
|
|
}
|
|
|
|
$show_units = ($t['show_units'] ? true : false);
|
|
$skipscale = ($t['skipscale'] == 'on' ? ':skipscale' : '');
|
|
|
|
switch($t['data_type']) {
|
|
case '0': // Exact value
|
|
case '1': // CDEF
|
|
case '3': // Upper+Lower
|
|
if ($t['thold_hrule_alert'] > 0) {
|
|
$color = db_fetch_cell_prepared('SELECT hex
|
|
FROM colors
|
|
WHERE id = ?',
|
|
[$t['thold_hrule_alert']]);
|
|
|
|
switch($t['thold_type']) {
|
|
case '0': // Hi / Low
|
|
if ($t['thold_hi'] != '') {
|
|
$txt_graph_items .= 'LINE1:' . $t['thold_hi'] . '#' . $color . ':' . thold_prep_rrd_string(__esc('Alert Hi for %s (%s)', $t['name_cache'], thold_format_number($t['thold_hi'], 2, $baseu, $suffix, $show_units), 'thold')) . $skipscale . ' \\' . "\n";
|
|
}
|
|
|
|
if ($t['thold_low'] != '') {
|
|
$txt_graph_items .= 'LINE1:' . $t['thold_low'] . '#' . $color . ':' . thold_prep_rrd_string(__esc('Alert Low for %s (%s)', $t['name_cache'], thold_format_number($t['thold_low'], 2, $baseu, $suffix, $show_units), 'thold')) . $skipscale . ' \\' . "\n";
|
|
}
|
|
|
|
break;
|
|
case '2': // Time Based
|
|
if ($t['time_hi'] != '') {
|
|
$txt_graph_items .= 'LINE1:' . $t['time_hi'] . '#' . $color . ':' . thold_prep_rrd_string(__esc('Alert Hi for %s (%s)', $t['name_cache'], thold_format_number($t['time_hi'], 2, $baseu, $suffix, $show_units), 'thold')) . $skipscale . ' \\' . "\n";
|
|
}
|
|
|
|
if ($t['time_low'] != '') {
|
|
$txt_graph_items .= 'LINE1:' . $t['time_low'] . '#' . $color . ':' . thold_prep_rrd_string(__esc('Alert Low for %s (%s)', $t['name_cache'], thold_format_number($t['time_low'], 2, $baseu, $suffix, $show_units), 'thold')) . $skipscale . ' \\' . "\n";
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($t['thold_hrule_warning'] > 0) {
|
|
$color = db_fetch_cell_prepared('SELECT hex
|
|
FROM colors
|
|
WHERE id = ?',
|
|
[$t['thold_hrule_warning']]);
|
|
|
|
switch($t['thold_type']) {
|
|
case '0': // Hi / Low
|
|
if ($t['thold_warning_hi'] != '') {
|
|
$txt_graph_items .= 'LINE1:' . $t['thold_warning_hi'] . '#' . $color . ':' . thold_prep_rrd_string(__esc('Warning Hi for %s (%s)', $t['name_cache'], thold_format_number($t['thold_warning_hi'], 2, $baseu, $suffix, $show_units), 'thold')) . $skipscale . ' \\' . "\n";
|
|
}
|
|
|
|
if ($t['thold_warning_low'] != '') {
|
|
$txt_graph_items .= 'LINE1:' . $t['thold_warning_low'] . '#' . $color . ':' . thold_prep_rrd_string(__esc('Warning Low for %s (%s)', $t['name_cache'], thold_format_number($t['thold_warning_low'], 2, $baseu, $suffix, $show_units), 'thold')) . $skipscale . ' \\' . "\n";
|
|
}
|
|
|
|
break;
|
|
case '2': // Time Based
|
|
if ($t['time_warning_hi'] != '') {
|
|
$txt_graph_items .= 'LINE1:' . $t['time_warning_hi'] . '#' . $color . ':' . thold_prep_rrd_string(__esc('Warning Hi for %s (%s)', $t['name_cache'], thold_format_number($t['time_warning_hi'], 2, $baseu, $suffix, $show_units), 'thold')) . $skipscale . ' \\' . "\n";
|
|
}
|
|
|
|
if ($t['time_warning_low'] != '') {
|
|
$txt_graph_items .= 'LINE1:' . $t['time_warning_low'] . '#' . $color . ':' . thold_prep_rrd_string(__esc('Warning Low for %s (%s)', $t['name_cache'], thold_format_number($t['time_warning_low'], 2, $baseu, $suffix, $show_units), 'thold')) . $skipscale . ' \\' . "\n";
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case '2': // Percentage
|
|
if (isset($data_defs[$t['percent_ds']])) {
|
|
if ($t['thold_hrule_alert'] > 0) {
|
|
$color = db_fetch_cell_prepared('SELECT hex
|
|
FROM colors
|
|
WHERE id = ?',
|
|
[$t['thold_hrule_alert']]);
|
|
|
|
switch($t['thold_type']) {
|
|
case '0': // Hi / Low
|
|
if ($t['thold_hi'] != '') {
|
|
$g['graph_defs'] .= 'CDEF:th' . $thold_id . 'ahi=' . $data_defs[$t['percent_ds']] . ',' . $t['thold_hi'] . ',100,/,* \\' . "\n";
|
|
$txt_graph_items .= 'LINE1:th' . $thold_id . 'ahi#' . $color . ':' . thold_prep_rrd_string(__esc('Alert Hi for %s (%s %%%)', $t['name_cache'], number_format_i18n($t['thold_hi']), 'thold')) . $skipscale . ' \\' . "\n";
|
|
$thold_id++;
|
|
}
|
|
|
|
if ($t['thold_low'] != '') {
|
|
$g['graph_defs'] .= 'CDEF:th' . $thold_id . 'alow=' . $data_defs[$t['percent_ds']] . ',' . $t['thold_low'] . ',100,/,* \\' . "\n";
|
|
$txt_graph_items .= 'LINE1:th' . $thold_id . 'alow#' . $color . ':' . thold_prep_rrd_string(__esc('Alert Low for %s (%s %%%)', $t['name_cache'], number_format_i18n($t['thold_low']), 'thold')) . $skipscale . ' \\' . "\n";
|
|
$thold_id++;
|
|
}
|
|
|
|
break;
|
|
case '2': // Time Based
|
|
if ($t['time_hi'] != '') {
|
|
$g['graph_defs'] .= 'CDEF:th' . $thold_id . 'ahi=' . $data_defs[$t['percent_ds']] . ',' . $t['time_hi'] . ',100,/,* \\' . "\n";
|
|
$txt_graph_items .= 'LINE1:th' . $thold_id . 'ahi#' . $color . ':' . thold_prep_rrd_string(__esc('Alert Hi for %s (%s %%%)', $t['name_cache'], number_format_i18n($t['time_hi']), 'thold')) . $skipscale . ' \\' . "\n";
|
|
$thold_id++;
|
|
}
|
|
|
|
if ($t['time_low'] != '') {
|
|
$g['graph_defs'] .= 'CDEF:th' . $thold_id . 'alow=' . $data_defs[$t['percent_ds']] . ',' . $t['time_low'] . ',100,/,* \\' . "\n";
|
|
$txt_graph_items .= 'LINE1:th' . $thold_id . 'alow#' . $color . ':' . thold_prep_rrd_string(__esc('Alert Low for %s (%s %%%)', $t['name_cache'], number_format_i18n($t['time_low']), 'thold')) . $skipscale . ' \\' . "\n";
|
|
$thold_id++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($t['thold_hrule_warning'] > 0) {
|
|
$color = db_fetch_cell_prepared('SELECT hex
|
|
FROM colors
|
|
WHERE id = ?',
|
|
[$t['thold_hrule_warning']]);
|
|
|
|
switch($t['thold_type']) {
|
|
case '0': // Hi / Low
|
|
if ($t['thold_warning_hi'] != '') {
|
|
$g['graph_defs'] .= 'CDEF:th' . $thold_id . 'whi=' . $data_defs[$t['percent_ds']] . ',' . $t['thold_warning_hi'] . ',100,/,* \\' . "\n";
|
|
$txt_graph_items .= 'LINE1:th' . $thold_id . 'whi#' . $color . ':' . thold_prep_rrd_string(__esc('Warning Hi for %s (%s %%%)', $t['name_cache'], number_format_i18n($t['thold_warning_hi']), 'thold')) . $skipscale . ' \\' . "\n";
|
|
$thold_id++;
|
|
}
|
|
|
|
if ($t['thold_warning_low'] != '') {
|
|
$g['graph_defs'] .= 'CDEF:th' . $thold_id . 'wlow=' . $data_defs[$t['percent_ds']] . ',' . $t['thold_warning_low'] . ',100,/,* \\' . "\n";
|
|
$txt_graph_items .= 'LINE1:th' . $thold_id . 'wlow#' . $color . ':' . thold_prep_rrd_string(__esc('Warning Low for %s (%s %%%)', $t['name_cache'], number_format_i18n($t['thold_warning_low']), 'thold')) . $skipscale . ' \\' . "\n";
|
|
$thold_id++;
|
|
}
|
|
|
|
break;
|
|
case '2': // Time Based
|
|
if ($t['time_warning_hi'] != '') {
|
|
$g['graph_defs'] .= 'CDEF:th' . $thold_id . 'whi=' . $data_defs[$t['percent_ds']] . ',' . $t['time_warning_hi'] . ',100,/,* \\' . "\n";
|
|
$txt_graph_items .= 'LINE1:th' . $thold_id . 'whi#' . $color . ':' . thold_prep_rrd_string(__esc('Warning Hi for %s (%s %%%)', $t['name_cache'], number_format_i18n($t['time_warning_hi']), 'thold')) . $skipscale . ' \\' . "\n";
|
|
$thold_id++;
|
|
}
|
|
|
|
if ($t['time_warning_low'] != '') {
|
|
$g['graph_defs'] .= 'CDEF:th' . $thold_id . 'wlow=' . $data_defs[$t['percent_ds']] . ',' . $t['time_warning_low'] . ',100,/,* \\' . "\n";
|
|
$txt_graph_items .= 'LINE1:th' . $thold_id . 'wlow#' . $color . ':' . thold_prep_rrd_string(__esc('Warning Low for %s (%s %%%)', $t['name_cache'], number_format_i18n($t['time_warning_low']), 'thold')) . $skipscale . ' \\' . "\n";
|
|
$thold_id++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($txt_graph_items) {
|
|
$g['txt_graph_items'] .= ' \\' . "\n" . 'COMMENT:\' ' . '\\n' . '\' \\' . "\n" . 'COMMENT:\'<u><b>' . __('Threshold Alert/Warning Values', 'thold') . '</b></u>' . '\\n' . '\' \\' . "\n" . $txt_graph_items;
|
|
}
|
|
|
|
return $g;
|
|
}
|
|
|
|
/**
|
|
* Prepares a string value for safe embedding in an RRDtool command
|
|
* line argument by shell-escaping it, then RRDtool-escaping it, then
|
|
* wrapping it in single quotes.
|
|
*
|
|
* @param string $string The raw string value to prepare.
|
|
*
|
|
* @return string The quoted, escaped string ready for RRDtool command
|
|
* construction.
|
|
*/
|
|
function thold_prep_rrd_string($string) {
|
|
return '\'' . trim(cacti_escapeshellarg(rrdtool_escape_string($string)), "'") . '\'';
|
|
}
|
|
|
|
/**
|
|
* Executes the "Apply Thresholds" bulk device action: auto-creates
|
|
* thresholds for each selected device using its device template's
|
|
* associated threshold templates. Registered as the
|
|
* 'device_action_execute' Cacti hook; a no-op passthrough for any
|
|
* other action.
|
|
*
|
|
* @param string $action The bulk action identifier being executed.
|
|
*
|
|
* @return string The unmodified $action, for hook chaining.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include.
|
|
*/
|
|
function thold_device_action_execute($action) {
|
|
global $config;
|
|
|
|
if ($action != 'thold') {
|
|
return $action;
|
|
}
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
$selected_items = sanitize_unserialize_selected_items(get_nfilter_request_var('selected_items'));
|
|
|
|
if ($selected_items != false) {
|
|
for ($i = 0; ($i < cacti_sizeof($selected_items)); $i++) {
|
|
autocreate($selected_items[$i]);
|
|
}
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
/**
|
|
* Auto-creates thresholds for a newly created device when the global
|
|
* thold_autocreate setting is enabled. Registered as the
|
|
* 'api_device_new' Cacti hook.
|
|
*
|
|
* @param array $save The newly created device's saved field data,
|
|
* including its 'id'.
|
|
*
|
|
* @return array The unmodified $save array, for hook chaining.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include.
|
|
*/
|
|
function thold_api_device_new($save) {
|
|
global $config;
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
if (read_config_option('thold_autocreate') == 'on') {
|
|
if (!empty($save['id'])) {
|
|
autocreate($save['id']);
|
|
}
|
|
}
|
|
|
|
return $save;
|
|
}
|
|
|
|
/**
|
|
* Renders the confirmation box listing the devices selected for the
|
|
* "Apply Thresholds" bulk device action. Registered as the
|
|
* 'device_action_prepare' Cacti hook; a no-op passthrough for any
|
|
* other action.
|
|
*
|
|
* @param array $save The bulk-action form submission data, including
|
|
* 'drp_action' and a pre-built 'host_list' HTML
|
|
* fragment.
|
|
*
|
|
* @return array The unmodified $save array, for hook chaining.
|
|
*
|
|
* @global array $host_list Reserved/declared for parity with other
|
|
* functions in this file; not used directly
|
|
* here.
|
|
*/
|
|
function thold_device_action_prepare($save) {
|
|
global $host_list;
|
|
|
|
if ($save['drp_action'] != 'thold') {
|
|
return $save;
|
|
}
|
|
|
|
print "<tr>
|
|
<td colspan='2' class='textArea'>
|
|
<p>" . __('Click \'Continue\' to apply all appropriate Thresholds to these Device(s).', 'thold') . "</p>
|
|
<div class='itemlist'><ul>" . $save['host_list'] . '</ul></div>
|
|
</td>
|
|
</tr>';
|
|
|
|
return $save;
|
|
}
|
|
|
|
/**
|
|
* Adds the "Apply Thresholds" entry to the device management bulk-
|
|
* actions dropdown. Registered as the 'device_action_array' Cacti
|
|
* hook.
|
|
*
|
|
* @param array $device_action_array The existing bulk-action label
|
|
* map, keyed by action id.
|
|
*
|
|
* @return array The action map with this plugin's entry added.
|
|
*/
|
|
function thold_device_action_array($device_action_array) {
|
|
$device_action_array['thold'] = 'Apply Thresholds';
|
|
|
|
return $device_action_array;
|
|
}
|
|
|
|
/**
|
|
* Reacts to a device being saved: when the device's disabled state
|
|
* changed, triggers this plugin's device up/down/enable/disable
|
|
* notification handling; and (elsewhere in this function, per the
|
|
* broader save flow) keeps this plugin's per-device data in sync with
|
|
* the saved device fields. Registered as the 'api_device_save' Cacti
|
|
* hook.
|
|
*
|
|
* @param array $save The device's saved field data, including 'id' and
|
|
* 'disabled'.
|
|
*
|
|
* @return array The unmodified $save array, for hook chaining.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include.
|
|
*/
|
|
function thold_api_device_save($save) {
|
|
global $config;
|
|
|
|
$result = db_fetch_row_prepared('SELECT disabled
|
|
FROM host
|
|
WHERE id = ?',
|
|
[$save['id']]);
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
if ($save['id'] > 0) {
|
|
if ($save['disabled'] != $result['disabled']) {
|
|
if ($save['disabled'] == '') {
|
|
plugin_thold_log_changes($save['id'], 'enabled_host');
|
|
|
|
db_execute_prepared('UPDATE thold_data
|
|
SET thold_enabled = "on"
|
|
WHERE host_id = ?',
|
|
[$save['id']]);
|
|
} else {
|
|
plugin_thold_log_changes($save['id'], 'disabled_host');
|
|
|
|
db_execute_prepared('UPDATE thold_data
|
|
SET thold_enabled = "off"
|
|
WHERE host_id = ?',
|
|
[$save['id']]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset_request_var('thold_send_email')) {
|
|
$save['thold_send_email'] = form_input_validate(get_nfilter_request_var('thold_send_email'), 'thold_send_email', '', true, 3);
|
|
} else {
|
|
$save['thold_send_email'] = form_input_validate('', 'thold_send_email', '', true, 3);
|
|
}
|
|
|
|
if (isset_request_var('thold_host_email')) {
|
|
$save['thold_host_email'] = form_input_validate(get_nfilter_request_var('thold_host_email'), 'thold_host_email', '', true, 3);
|
|
} else {
|
|
$save['thold_host_email'] = form_input_validate('', 'thold_host_email', '', true, 3);
|
|
}
|
|
|
|
if (isset_request_var('thold_failure_count')) {
|
|
$save['thold_failure_count'] = form_input_validate(get_nfilter_request_var('thold_failure_count'), 'thold_failure_count', '', true, 3);
|
|
} else {
|
|
$save['thold_failure_count'] = form_input_validate('', 'thold_failure_count', '', true, 3);
|
|
}
|
|
|
|
return $save;
|
|
}
|
|
|
|
/**
|
|
* Turns a data source's name cell in the Console data sources table
|
|
* into a link to either edit its existing threshold, or (when no graph
|
|
* exists yet for it) create a new one, when applicable. Registered as
|
|
* the 'data_sources_table' Cacti hook.
|
|
*
|
|
* @param array $ds The data source row being rendered.
|
|
*
|
|
* @return array The $ds row, with 'data_template_name' replaced by a
|
|
* linked HTML fragment when applicable.
|
|
*
|
|
* @global array $config Cacti global configuration array (declared but
|
|
* not used directly here).
|
|
*/
|
|
function thold_data_sources_table($ds) {
|
|
global $config;
|
|
|
|
if (isset($ds['local_data_id'])) {
|
|
$exists = db_fetch_cell_prepared('SELECT id
|
|
FROM thold_data
|
|
WHERE local_data_id = ?
|
|
LIMIT 1',
|
|
[$ds['local_data_id']]);
|
|
|
|
if ($exists) {
|
|
$ds['data_template_name'] = "<a title='" . __esc('Create Threshold from Data Source', 'thold') . "' class='linkEditMain' href='" . html_escape('plugins/thold/thold.php?action=edit&id=' . $exists) . "'>" . ((empty($ds['data_template_name'])) ? '<em>' . __('None', 'thold') . '</em>' : html_escape($ds['data_template_name'])) . '</a>';
|
|
} else {
|
|
$graph_exists = db_fetch_cell_prepared('SELECT DISTINCT gl.id
|
|
FROM graph_local AS gl
|
|
INNER JOIN graph_templates_item AS gti
|
|
ON gl.id = gti.local_graph_id
|
|
INNER JOIN data_template_rrd AS dtr
|
|
ON gti.task_item_id = dtr.id
|
|
WHERE dtr.local_data_id = ?
|
|
LIMIT 1',
|
|
[$ds['local_data_id']]);
|
|
|
|
$data_template_id = db_fetch_cell_prepared('SELECT data_template_id
|
|
FROM data_local
|
|
WHERE id = ?',
|
|
[$ds['local_data_id']]);
|
|
|
|
if ($graph_exists) {
|
|
$ds['data_template_name'] = "<a title='" . __esc('Create Threshold from Data Source', 'thold') . "' class='linkEditMain' href='" . html_escape('plugins/thold/thold.php?action=edit&local_data_id=' . $ds['local_data_id'] . '&host_id=' . $ds['host_id'] . '&data_template_id=' . $data_template_id . '&data_template_rrd_id=&local_graph_id=' . $graph_exists . '&thold_template_id=0') . "'>" . ((empty($ds['data_template_name'])) ? '<em>' . __('None', 'thold') . '</em>' : html_escape($ds['data_template_name'])) . '</a>';
|
|
}
|
|
}
|
|
}
|
|
|
|
return $ds;
|
|
}
|
|
|
|
/**
|
|
* Prints an "Auto-create Thresholds" link on the "new graphs" page for
|
|
* the current host. Registered as the 'graphs_new_top_links' Cacti
|
|
* hook.
|
|
*
|
|
* @return void
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* build the link URL.
|
|
*/
|
|
function thold_graphs_new() {
|
|
global $config;
|
|
|
|
print '<span class="linkMarker">*</span><a class="autocreate linkEditMain" href="' . html_escape($config['url_path'] . 'plugins/thold/thold.php?action=autocreate&host_id=' . get_filter_request_var('host_id')) . '">' . __('Auto-create Thresholds', 'thold') . '</a><br>';
|
|
}
|
|
|
|
/**
|
|
* Saves (or clears) a user's notification email address into this
|
|
* plugin's contacts table whenever a Cacti user account is saved with
|
|
* an email/email_address field. Registered as the
|
|
* 'user_admin_setup_sql_save' Cacti hook; a no-op passthrough when the
|
|
* user save already failed validation.
|
|
*
|
|
* @param array $save The user account's saved field data, including
|
|
* 'id'.
|
|
*
|
|
* @return array The unmodified (or id-populated) $save array, for hook
|
|
* chaining.
|
|
*/
|
|
function thold_user_admin_setup_sql_save($save) {
|
|
if (is_error_message()) {
|
|
return $save;
|
|
}
|
|
|
|
if (isset_request_var('email') || isset_request_var('email_address')) {
|
|
$email = form_input_validate(get_nfilter_request_var('email_address'), 'email_address', '', true, 3);
|
|
|
|
if ($save['id'] == 0) {
|
|
$save['id'] = sql_save($save, 'user_auth');
|
|
}
|
|
|
|
$cid = db_fetch_cell_prepared('SELECT id
|
|
FROM plugin_thold_contacts
|
|
WHERE type = "email"
|
|
AND user_id = ?',
|
|
[$save['id']]);
|
|
|
|
if ($cid) {
|
|
db_execute_prepared('REPLACE INTO plugin_thold_contacts
|
|
(id, user_id, type, data) VALUES
|
|
(?, ?, "email", ?)',
|
|
[$cid, $save['id'], $email]);
|
|
} else {
|
|
db_execute_prepared('REPLACE INTO plugin_thold_contacts
|
|
(user_id, type, data) VALUES
|
|
(?, "email", ?)',
|
|
[$save['id'], $email]);
|
|
}
|
|
}
|
|
|
|
return $save;
|
|
}
|
|
|
|
/**
|
|
* Executes the "Create Threshold from Data Source" bulk data source
|
|
* action: creates thresholds for each selected data source using the
|
|
* threshold template chosen on the confirmation page. Registered as
|
|
* the 'data_source_action_execute' Cacti hook; a no-op passthrough for
|
|
* any other action.
|
|
*
|
|
* @param string $action The bulk action identifier being executed.
|
|
*
|
|
* @return string The unmodified $action, for hook chaining.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include.
|
|
* @global array $form_array Reserved/declared for parity with other
|
|
* functions in this file; not used directly
|
|
* here.
|
|
*/
|
|
function thold_data_source_action_execute($action) {
|
|
global $config, $form_array;
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
if ($action == 'plugin_thold_create') {
|
|
$selected_items = sanitize_unserialize_selected_items(get_nfilter_request_var('selected_items'));
|
|
|
|
if ($selected_items != false) {
|
|
$message = '';
|
|
$created = 0;
|
|
|
|
get_filter_request_var('thold_template_id');
|
|
|
|
$template = db_fetch_row_prepared('SELECT *
|
|
FROM thold_template
|
|
WHERE id = ?',
|
|
[get_request_var('thold_template_id')]);
|
|
|
|
if (cacti_sizeof($template)) {
|
|
foreach ($selected_items as $local_data_id) {
|
|
$data_sources = db_fetch_assoc_prepared('SELECT DISTINCT
|
|
dtr.id, gl.id AS local_graph_id, dtr.local_data_id
|
|
FROM data_template_rrd AS dtr
|
|
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
|
|
WHERE dtr.local_data_id = ?
|
|
AND dtr.data_source_name = ?',
|
|
[$local_data_id, $template['data_source_name']]);
|
|
|
|
if (cacti_sizeof($data_sources)) {
|
|
foreach ($data_sources as $data_source) {
|
|
$local_data_id = $data_source['local_data_id'];
|
|
$local_graph_id = $data_source['local_graph_id'];
|
|
$data_template_rrd_id = $data_source['id'];
|
|
|
|
if (thold_create_from_template($local_data_id, $local_graph_id, $data_template_rrd_id, $template, $message)) {
|
|
$created++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strlen($message)) {
|
|
thold_raise_message(__('Created %s thresholds', $created) . '<br>' . $message, MESSAGE_LEVEL_INFO);
|
|
} else {
|
|
thold_raise_message(__('No Threshold(s) Created. Either they already exist, or no suitable matches found.', 'thold'), MESSAGE_LEVEL_INFO);
|
|
}
|
|
} else {
|
|
thold_raise_message(__('No Threshold(s) Created. Threshold(s) Template not found.', 'thold'), MESSAGE_LEVEL_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
/**
|
|
* Renders the confirmation page for the "Create Threshold from
|
|
* Template" bulk data source action, listing which selected data
|
|
* sources have at least one applicable threshold template (and
|
|
* offering a template selection), while filtering out any that don't.
|
|
* Registered as the 'data_source_action_prepare' Cacti hook; a no-op
|
|
* passthrough for any other action.
|
|
*
|
|
* @param array $save The bulk-action form submission data, including
|
|
* 'drp_action' and 'ds_array' (selected data source
|
|
* ids).
|
|
*
|
|
* @return array|void The unmodified $save array (for hook chaining)
|
|
* when the action isn't 'plugin_thold_create';
|
|
* otherwise this renders the confirmation UI
|
|
* directly and returns nothing.
|
|
*
|
|
* @global array $config Cacti global configuration array (declared but
|
|
* not used directly here).
|
|
*/
|
|
function thold_data_source_action_prepare($save) {
|
|
global $config;
|
|
|
|
if ($save['drp_action'] == 'plugin_thold_create') {
|
|
/* get the valid thold templates
|
|
* remove those hosts that do not have any valid templates
|
|
*/
|
|
$templates = '';
|
|
$found_list = '';
|
|
$not_found = '';
|
|
|
|
if (cacti_sizeof($save['ds_array'])) {
|
|
foreach ($save['ds_array'] as $item) {
|
|
$data_template_id = db_fetch_cell_prepared('SELECT data_template_id
|
|
FROM data_local
|
|
WHERE id = ?',
|
|
[$item]);
|
|
|
|
if ($data_template_id != '') {
|
|
$templates_ids = db_fetch_assoc_prepared('SELECT id
|
|
FROM thold_template
|
|
WHERE data_template_id = ?',
|
|
[$data_template_id]);
|
|
|
|
if (cacti_sizeof($templates_ids)) {
|
|
$found_list .= '<li>' . html_escape(get_data_source_title($item)) . '</li>';
|
|
|
|
if (strlen($templates)) {
|
|
$templates .= ", $data_template_id";
|
|
} else {
|
|
$templates = "$data_template_id";
|
|
}
|
|
} else {
|
|
$not_found .= '<li>' . html_escape(get_data_source_title($item)) . '</li>';
|
|
}
|
|
} else {
|
|
$not_found .= '<li>' . html_escape(get_data_source_title($item)) . '</li>';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strlen($templates)) {
|
|
$sql = 'SELECT id, name FROM thold_template WHERE data_template_id IN (' . $templates . ') ORDER BY name';
|
|
} else {
|
|
$sql = 'SELECT id, name FROM thold_template ORDER BY name';
|
|
}
|
|
|
|
print "<tr><td colspan='2' class='textArea'>";
|
|
|
|
if (strlen($found_list)) {
|
|
if (strlen($not_found)) {
|
|
print '<p>' . __('The following Data Sources have no Threshold Templates associated with them', 'thold') . '</p>';
|
|
print '<div class="itemlist"><ul>' . $not_found . '</ul></div>';
|
|
}
|
|
|
|
print '<p>' . __('Click \'Continue\' to create Thresholds for these Data Sources?', 'thold') . '</p>
|
|
<div class="itemlist"><ul>' . $found_list . "</ul></div>
|
|
</td>
|
|
</tr></table><table class='cactiTable'><tr><td>";
|
|
|
|
$form_array = [
|
|
'general_header' => [
|
|
'friendly_name' => __('Available Threshold Templates', 'thold'),
|
|
'method' => 'spacer',
|
|
],
|
|
'thold_template_id' => [
|
|
'method' => 'drop_sql',
|
|
'friendly_name' => __('Select a Threshold Template', 'thold'),
|
|
'description' => '',
|
|
'none_value' => __('None', 'thold'),
|
|
'value' => __('None', 'thold'),
|
|
'sql' => $sql
|
|
]
|
|
];
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => $form_array
|
|
]
|
|
);
|
|
} else {
|
|
if (strlen($not_found)) {
|
|
print '<p>' . __('There are no Threshold Templates associated with the following Data Sources', 'thold') . '</p>';
|
|
print '<div class="itemlist"><ul>' . $not_found . '</ul></div>';
|
|
}
|
|
}
|
|
|
|
print '</td></tr></table><table class="cactiTable"><tr><td class="saveRow">';
|
|
} else {
|
|
return $save;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds the "Create Threshold from Template" entry to the data source
|
|
* management bulk-actions dropdown. Registered as the
|
|
* 'data_source_action_array' Cacti hook.
|
|
*
|
|
* @param array $action The existing bulk-action label map, keyed by
|
|
* action id.
|
|
*
|
|
* @return array The action map with this plugin's entry added.
|
|
*/
|
|
function thold_data_source_action_array($action) {
|
|
$action['plugin_thold_create'] = __('Create Threshold from Template', 'thold');
|
|
|
|
return $action;
|
|
}
|
|
|
|
/**
|
|
* Executes the "Create Threshold from Template" bulk graph action:
|
|
* applies the selected threshold template to each selected graph's
|
|
* matching data source(s), creating a new threshold for each one that
|
|
* doesn't already have one from that template. Registered as the
|
|
* 'graphs_action_execute' Cacti hook; a no-op passthrough for any
|
|
* other action.
|
|
*
|
|
* @param string $action The bulk action identifier being executed.
|
|
*
|
|
* @return string The unmodified $action, for hook chaining.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include.
|
|
* @global array $form_array Reserved/declared for parity with other
|
|
* functions in this file; not used directly
|
|
* here.
|
|
*/
|
|
function thold_graphs_action_execute($action) {
|
|
global $config, $form_array;
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
if ($action == 'plugin_thold_create') {
|
|
$selected_items = sanitize_unserialize_selected_items(get_nfilter_request_var('selected_items'));
|
|
|
|
if ($selected_items != false) {
|
|
$message = '';
|
|
$created = 0;
|
|
|
|
get_filter_request_var('thold_template_id');
|
|
|
|
$template = db_fetch_row_prepared('SELECT *
|
|
FROM thold_template
|
|
WHERE id = ?',
|
|
[get_request_var('thold_template_id')]);
|
|
|
|
if (cacti_sizeof($template)) {
|
|
foreach ($selected_items as $local_graph_id) {
|
|
$data_sources = db_fetch_assoc_prepared('SELECT DISTINCT
|
|
dtr.id, gl.id AS local_graph_id, dtr.local_data_id
|
|
FROM data_template_rrd AS dtr
|
|
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
|
|
WHERE gl.id = ?
|
|
AND dtr.data_source_name = ?',
|
|
[$local_graph_id, $template['data_source_name']]);
|
|
|
|
if (cacti_sizeof($data_sources)) {
|
|
foreach ($data_sources as $data_source) {
|
|
$local_data_id = $data_source['local_data_id'];
|
|
$local_graph_id = $data_source['local_graph_id'];
|
|
$data_template_rrd_id = $data_source['id'];
|
|
|
|
if (thold_create_from_template($local_data_id, $local_graph_id, $data_template_rrd_id, $template, $message)) {
|
|
$created++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strlen($message)) {
|
|
thold_raise_message(__('Created %s thresholds', $created) . '<br>' . $message, MESSAGE_LEVEL_INFO);
|
|
} else {
|
|
thold_raise_message(__('No Threshold(s) Created. Either they already exist, or no suitable matches found.', 'thold'), MESSAGE_LEVEL_INFO);
|
|
}
|
|
} else {
|
|
thold_raise_message(__('No Threshold(s) Created. Threshold(s) Template not found.', 'thold'), MESSAGE_LEVEL_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
/**
|
|
* Renders the confirmation page for the "Create Threshold from
|
|
* Template" bulk graph action, listing which selected graphs have at
|
|
* least one applicable threshold template (and offering a template
|
|
* selection), while filtering out any that don't. Registered as the
|
|
* 'graphs_action_prepare' Cacti hook; a no-op passthrough for any
|
|
* other action.
|
|
*
|
|
* @param array $save The bulk-action form submission data, including
|
|
* 'drp_action'.
|
|
*
|
|
* @return array|void The unmodified $save array (for hook chaining)
|
|
* when the action isn't 'plugin_thold_create';
|
|
* otherwise this renders the confirmation UI
|
|
* directly and returns nothing.
|
|
*
|
|
* @global array $config Cacti global configuration array (declared but
|
|
* not used directly here).
|
|
*/
|
|
function thold_graphs_action_prepare($save) {
|
|
global $config;
|
|
|
|
if ($save['drp_action'] == 'plugin_thold_create') {
|
|
/* get the valid thold templates
|
|
* remove those hosts that do not have any valid templates
|
|
*/
|
|
$found_list = '';
|
|
$not_found = '';
|
|
$template_ids = [];
|
|
|
|
if (cacti_sizeof($save['graph_array'])) {
|
|
foreach ($save['graph_array'] as $item) {
|
|
$item_found = false;
|
|
$data_template_ids = db_fetch_assoc_prepared('SELECT DISTINCT dtr.data_template_id
|
|
FROM data_template_rrd AS dtr
|
|
LEFT JOIN graph_templates_item AS gti
|
|
ON gti.task_item_id=dtr.id
|
|
LEFT JOIN graph_local AS gl
|
|
ON gl.id=gti.local_graph_id
|
|
WHERE gl.id = ?',
|
|
[$item]);
|
|
|
|
if (cacti_sizeof($data_template_ids)) {
|
|
foreach ($data_template_ids as $i => $data_template_rec) {
|
|
$data_template_id = $data_template_rec['data_template_id'];
|
|
|
|
$templates = db_fetch_assoc_prepared('SELECT id
|
|
FROM thold_template
|
|
WHERE data_template_id = ?',
|
|
[$data_template_id]);
|
|
|
|
if (cacti_sizeof($templates)) {
|
|
$item_found = true;
|
|
$template_ids[] = $data_template_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$item_found) {
|
|
$not_found .= '<li>' . html_escape(get_graph_title($item)) . '</li>';
|
|
} else {
|
|
$found_list .= '<li>' . html_escape(get_graph_title($item)) . '</li>';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cacti_sizeof($template_ids)) {
|
|
$sql = 'SELECT id, name FROM thold_template WHERE data_template_id IN (' . implode(', ', $template_ids) . ') ORDER BY name';
|
|
} else {
|
|
$sql = 'SELECT id, name FROM thold_template ORDER BY name';
|
|
}
|
|
|
|
print "<tr><td colspan='2' class='textArea'>";
|
|
|
|
if (strlen($found_list)) {
|
|
if (strlen($not_found)) {
|
|
print '<p>' . __('The following Graphs have no Threshold Templates associated with them', 'thold') . '</p>';
|
|
print '<div class="itemlist"><ul>' . $not_found . '</ul></div>';
|
|
}
|
|
|
|
print '<p>' . __('Press \'Continue\' if you wish to create Threshold(s) for these Graph(s)', 'thold') . '</p>
|
|
<div class="itemlist"><ul>' . $found_list . "</ul></div>
|
|
</td>
|
|
</tr></table><table class='cactiTable'><tr><td>";
|
|
|
|
$form_array = [
|
|
'general_header' => [
|
|
'friendly_name' => __('Available Threshold Templates', 'thold'),
|
|
'method' => 'spacer',
|
|
],
|
|
'thold_template_id' => [
|
|
'method' => 'drop_sql',
|
|
'friendly_name' => __('Select a Threshold Template', 'thold'),
|
|
'description' => '',
|
|
'value' => __('None', 'thold'),
|
|
'sql' => $sql
|
|
]
|
|
];
|
|
|
|
draw_edit_form(
|
|
[
|
|
'config' => ['no_form_tag' => true],
|
|
'fields' => $form_array
|
|
]
|
|
);
|
|
} else {
|
|
if (strlen($not_found)) {
|
|
print '<p>' . __('There are no Threshold Templates associated with the following Graphs', 'thold') . '</p>';
|
|
print '<div class="itemlist"><ul>' . $not_found . '</ul></div>';
|
|
}
|
|
}
|
|
|
|
print '</td></tr></table><table class="cactiTable"><tr><td class="saveRow">';
|
|
} else {
|
|
return $save;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds the "Create Threshold from Template" entry to the graph
|
|
* management bulk-actions dropdown. Registered as the
|
|
* 'graphs_action_array' Cacti hook.
|
|
*
|
|
* @param array $action The existing bulk-action label map, keyed by
|
|
* action id.
|
|
*
|
|
* @return array The action map with this plugin's entry added.
|
|
*/
|
|
function thold_graphs_action_array($action) {
|
|
$action['plugin_thold_create'] = __('Create Threshold from Template', 'thold');
|
|
|
|
return $action;
|
|
}
|
|
|
|
/**
|
|
* Emits the JavaScript that shows/hides the notification-list dropdown
|
|
* on the device edit form based on the selected "Threshold Up/Down
|
|
* Email Notification" mode. Registered as the 'host_edit_bottom' and
|
|
* 'device_change_javascript' Cacti hooks.
|
|
*
|
|
* @return void
|
|
*/
|
|
function thold_host_edit_bottom() {
|
|
// This hook can fire on core pages that have not loaded includes/functions.php, where plugin_thold_csp_nonce() lives.
|
|
require_once(__DIR__ . '/includes/functions.php');
|
|
|
|
?>
|
|
<script type='text/javascript' <?php print plugin_thold_csp_nonce(); ?>>
|
|
|
|
changeNotify();
|
|
function changeNotify() {
|
|
if ($('#thold_send_email').val() < 2) {
|
|
$('#row_thold_host_email').hide();
|
|
} else {
|
|
$('#row_thold_host_email').show();
|
|
}
|
|
}
|
|
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Installs this plugin's CACTI-THOLD-MIB definitions into Cacti's SNMP
|
|
* agent cache (SNMP Notification Receiver / SNMP Agent support),
|
|
* when the SNMP agent's MibCache class is available. Registered as the
|
|
* 'snmpagent_cache_install' Cacti hook, and invoked during plugin
|
|
* installation.
|
|
*
|
|
* @return void
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the MIB definition file.
|
|
*/
|
|
function thold_snmpagent_cache_install() {
|
|
global $config;
|
|
|
|
if (class_exists('MibCache')) {
|
|
$mc = new MibCache('CACTI-THOLD-MIB');
|
|
$mc->install($config['base_path'] . '/plugins/thold/CACTI-THOLD-MIB', true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes this plugin's CACTI-THOLD-MIB definitions from Cacti's SNMP
|
|
* agent cache, when the SNMP agent's MibCache class is available.
|
|
* Invoked during plugin uninstallation.
|
|
*
|
|
* @return void
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the MIB definition file.
|
|
*/
|
|
function thold_snmpagent_cache_uninstall() {
|
|
global $config;
|
|
|
|
if (class_exists('MibCache')) {
|
|
$mc = new MibCache('CACTI-THOLD-MIB');
|
|
$mc->uninstall();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Emits this plugin's theme-specific stylesheet link (when one exists)
|
|
* and a JavaScript handler that rebinds the threshold-VRULE toggle
|
|
* link's click behavior to an AJAX partial-page refresh after each AJAX
|
|
* completion. Registered as the 'page_head' Cacti hook.
|
|
*
|
|
* @return void
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* check for a theme-specific stylesheet and build
|
|
* asset URLs.
|
|
*/
|
|
function thold_page_head() {
|
|
global $config;
|
|
|
|
// This hook can fire on core pages that have not loaded includes/functions.php, where plugin_thold_csp_nonce() lives.
|
|
require_once(__DIR__ . '/includes/functions.php');
|
|
|
|
if (file_exists($config['base_path'] . '/plugins/thold/css/' . get_selected_theme() . '.css')) {
|
|
print get_md5_include_css('plugins/thold/css/' . get_selected_theme() . '.css');
|
|
}
|
|
|
|
print get_md5_include_css('plugins/thold/css/pills.css');
|
|
|
|
?>
|
|
<script type='text/javascript' <?php print plugin_thold_csp_nonce(); ?>>
|
|
$(function() {
|
|
$(document).ajaxComplete(function() {
|
|
$('.tholdVRule').unbind().click(function(event) {
|
|
event.preventDefault();
|
|
|
|
href = $(this).attr('href');
|
|
href += '&header=false';
|
|
|
|
$.get(href, function(data) {
|
|
$('#main').empty().hide();
|
|
$('div[class^="ui-"]').remove();
|
|
$('#main').html(data);
|
|
applySkin();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Renders the "Associated Threshold Templates" box on the device edit
|
|
* page, listing threshold templates already associated with the
|
|
* device (with their per-device threshold existence status and a
|
|
* remove link) and offering a dropdown to associate an additional
|
|
* template. Registered as the 'device_edit_pre_bottom' Cacti hook.
|
|
*
|
|
* @return void
|
|
*/
|
|
function thold_device_edit_pre_bottom() {
|
|
// This hook can fire on core pages that have not loaded includes/functions.php, where plugin_thold_csp_nonce() lives.
|
|
require_once(__DIR__ . '/includes/functions.php');
|
|
|
|
html_start_box(__('Associated Threshold Templates', 'thold'), '100%', false, '3', 'center', '');
|
|
|
|
$host_id = get_request_var('id');
|
|
|
|
$host_template_id = db_fetch_cell_prepared('SELECT host_template_id
|
|
FROM host
|
|
WHERE id = ?',
|
|
[$host_id]);
|
|
|
|
$threshold_templates = db_fetch_assoc_prepared('SELECT DISTINCT pth.thold_template_id, tt.name
|
|
FROM plugin_thold_host AS pth
|
|
INNER JOIN thold_template AS tt
|
|
ON tt.id = pth.thold_template_id
|
|
WHERE pth.host_id = ?
|
|
ORDER BY name',
|
|
[$host_id]);
|
|
|
|
html_header([__('Name', 'thold'), __('Status', 'thold')], 2);
|
|
|
|
$i = 1;
|
|
|
|
if (cacti_sizeof($threshold_templates)) {
|
|
foreach ($threshold_templates as $item) {
|
|
$exists = db_fetch_cell_prepared('SELECT id
|
|
FROM thold_data
|
|
WHERE host_id = ?
|
|
AND thold_template_id = ?',
|
|
[get_request_var('id'), $item['thold_template_id']]);
|
|
|
|
if ($exists) {
|
|
$exists = __('Threshold Exists', 'thold');
|
|
} else {
|
|
$exists = __('Threshold Does Not Exist', 'thold');
|
|
}
|
|
|
|
form_alternate_row("tt$i", true);
|
|
?>
|
|
<td class='left'>
|
|
<strong><?php print $i; ?>)</strong> <?php print html_escape($item['name']); ?>
|
|
</td>
|
|
<td>
|
|
<?php print $exists; ?>
|
|
</td>
|
|
<td class='nowrap right'>
|
|
<a class='delete deleteMarker fa fa-times' title='<?php print __esc('Delete Threshold Template Association', 'thold'); ?>' href='<?php print html_escape('host.php?action=item_remove_tt_confirm&id=' . $item['thold_template_id'] . '&host_id=' . get_request_var('id')); ?>'></a>
|
|
</td>
|
|
<?php
|
|
form_end_row();
|
|
|
|
$i++;
|
|
}
|
|
} else {
|
|
print '<tr><td class="templateAdd" colspan="2"><em>' . __('No Associated Threshold Templates.', 'thold') . '</em></td></tr>';
|
|
}
|
|
|
|
$available_thold_templates = db_fetch_assoc_prepared('SELECT DISTINCT tt.id, tt.name
|
|
FROM thold_template AS tt
|
|
LEFT JOIN plugin_thold_host AS pth
|
|
ON pth.thold_template_id = tt.id
|
|
AND pth.host_id = ?
|
|
WHERE tt.id NOT IN (SELECT thold_template_id FROM plugin_thold_host WHERE host_id = ?)',
|
|
[$host_id, $host_id]);
|
|
|
|
if (cacti_sizeof($available_thold_templates)) {
|
|
?>
|
|
<tr class='odd'>
|
|
<td class='saveRow' colspan='3'>
|
|
<table>
|
|
<tr style='line-height:10px;'>
|
|
<td class='nowrap templateAdd' style='padding-right:15px;'>
|
|
<?php print __('Add Threshold Template'); ?>
|
|
</td>
|
|
<td class='noHide'>
|
|
<?php form_dropdown('thold_template_id', $available_thold_templates, 'name', 'id', '', '', ''); ?>
|
|
</td>
|
|
<td class='noHide'>
|
|
<input id='add_tt' type='button' class='ui-button ui-corner-all ui-widget' value='<?php print __esc('Add'); ?>' title='<?php print __esc('Add Threshold Template to Device'); ?>' onClick='addThresholdTemplate()'>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<script type='text/javascript' <?php print plugin_thold_csp_nonce(); ?>>
|
|
function addThresholdTemplate() {
|
|
scrollTop = $(window).scrollTop();
|
|
$.post(urlPath+'host.php?header=false&action=item_add_tt', {
|
|
host_id: $('#id').val(),
|
|
thold_template_id: $('#thold_template_id').val(),
|
|
__csrf_magic: csrfMagicToken})
|
|
.done(function() {
|
|
loadPageNoHeader(urlPath+'host.php?header=false&action=edit&id='+$('#id').val());
|
|
});
|
|
}
|
|
</script>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
}
|
|
|
|
html_end_box();
|
|
}
|
|
|
|
/**
|
|
* Renders the confirmation page for removing a threshold template's
|
|
* association with a device (action=item_remove_tt_confirm), or
|
|
* otherwise defers to the standard device edit page (this hook is
|
|
* registered on host.php, whose own action-dispatch relies on this
|
|
* function to intercept this plugin-specific action). Registered as
|
|
* the 'device_top' Cacti hook.
|
|
*
|
|
* @return void
|
|
*/
|
|
function thold_device_top() {
|
|
if (get_request_var('action') == 'item_remove_tt_confirm') {
|
|
// ================= input validation =================
|
|
get_filter_request_var('id');
|
|
get_filter_request_var('host_id');
|
|
// ====================================================
|
|
|
|
form_start('host.php?action=edit&id' . get_request_var('host_id'));
|
|
|
|
html_start_box('', '100%', false, '3', 'center', '');
|
|
|
|
$template = db_fetch_row_prepared('SELECT *
|
|
FROM thold_template
|
|
WHERE id = ?',
|
|
[get_request_var('id')]);
|
|
|
|
?>
|
|
<tr>
|
|
<td class='topBoxAlt'>
|
|
<p><?php print __('Click \'Continue\' to Delete the following Threshold Template will be disassociated from the Device.', 'thold'); ?></p>
|
|
<p><?php print __esc('Threshold Template Name: %s', $template['name'], 'thold'); ?>'<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td align='right'>
|
|
<input id='cancel' type='button' value='<?php print __esc('Cancel', 'thold'); ?>' onClick='$("#cdialog").dialog("close")' name='cancel'>
|
|
<input id='continue' type='button' value='<?php print __esc('Continue', 'thold'); ?>' name='continue' title='<?php print __esc('Remove Threshold Template', 'thold'); ?>'>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
|
|
html_end_box();
|
|
|
|
form_end();
|
|
|
|
?>
|
|
<script type='text/javascript'>
|
|
$(function() {
|
|
$('#cdialog').dialog();
|
|
});
|
|
|
|
$('#continue').click(function(data) {
|
|
$.post('host.php?action=item_remove_tt', {
|
|
__csrf_magic: csrfMagicToken,
|
|
host_id: <?php print get_request_var('host_id'); ?>,
|
|
id: <?php print get_request_var('id'); ?>
|
|
}).done(function(data) {
|
|
$('#cdialog').dialog('close');
|
|
loadPageNoHeader('host.php?action=edit&header=false&id=<?php print get_request_var('host_id'); ?>');
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
|
|
exit;
|
|
}
|
|
|
|
if (get_request_var('action') == 'item_remove_tt') {
|
|
// ================= input validation =================
|
|
get_filter_request_var('id');
|
|
get_filter_request_var('host_id');
|
|
// ====================================================
|
|
|
|
db_execute_prepared('DELETE
|
|
FROM plugin_thold_host
|
|
WHERE thold_template_id = ?
|
|
AND host_id = ?',
|
|
[get_request_var('id'), get_request_var('host_id')]);
|
|
|
|
header('Location: host.php?header=false&action=edit&id=' . get_request_var('host_id'));
|
|
|
|
exit;
|
|
}
|
|
|
|
if (get_request_var('action') == 'item_add_tt') {
|
|
// ================= input validation =================
|
|
get_filter_request_var('host_id');
|
|
get_filter_request_var('thold_template_id');
|
|
// ====================================================
|
|
|
|
db_execute_prepared('REPLACE INTO plugin_thold_host
|
|
(host_id, thold_template_id) VALUES (?, ?)',
|
|
[get_request_var('host_id'), get_request_var('thold_template_id')]);
|
|
|
|
header('Location: host.php?header=false&action=edit&id=' . get_request_var('host_id'));
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders the "Associated Threshold Templates" box on the device
|
|
* template edit page, listing threshold templates already associated
|
|
* with the device template (with a remove link) and offering a
|
|
* dropdown to associate an additional template. Registered as the
|
|
* 'device_template_edit' Cacti hook.
|
|
*
|
|
* @return void
|
|
*/
|
|
function thold_device_template_edit() {
|
|
// This hook can fire on core pages that have not loaded includes/functions.php, where plugin_thold_csp_nonce() lives.
|
|
require_once(__DIR__ . '/includes/functions.php');
|
|
|
|
html_start_box(__('Associated Threshold Templates', 'thold'), '100%', false, '3', 'center', '');
|
|
|
|
$threshold_templates = db_fetch_assoc_prepared('SELECT ptdt.thold_template_id, tt.name
|
|
FROM plugin_thold_host_template AS ptdt
|
|
INNER JOIN thold_template AS tt
|
|
ON tt.id=ptdt.thold_template_id
|
|
WHERE ptdt.host_template_id = ?
|
|
ORDER BY name',
|
|
[get_request_var('id')]);
|
|
|
|
$i = 0;
|
|
|
|
if (cacti_sizeof($threshold_templates)) {
|
|
foreach ($threshold_templates as $item) {
|
|
form_alternate_row("tt$i", true);
|
|
?>
|
|
<td class='left'>
|
|
<strong><?php print $i; ?>)</strong> <?php print html_escape($item['name']); ?>
|
|
</td>
|
|
<td class='right'>
|
|
<a class='delete deleteMarker fa fa-times' title='<?php print __esc('Delete', 'thold'); ?>' href='<?php print html_escape('host_templates.php?action=item_remove_tt_confirm&id=' . $item['thold_template_id'] . '&host_template_id=' . get_request_var('id')); ?>'></a>
|
|
</td>
|
|
<?php
|
|
form_end_row();
|
|
|
|
$i++;
|
|
}
|
|
} else {
|
|
print '<tr><td class="templateAdd" colspan="2"><em>' . __('No Associated Threshold Templates.', 'thold') . '</em></td></tr>';
|
|
}
|
|
|
|
$unmapped = db_fetch_assoc_prepared('SELECT DISTINCT tt.id, tt.name
|
|
FROM thold_template AS tt
|
|
LEFT JOIN plugin_thold_host_template AS ptdt
|
|
ON tt.id = ptdt.thold_template_id
|
|
AND ptdt.host_template_id = ?
|
|
WHERE ptdt.host_template_id IS NULL OR ptdt.host_template_id != ?
|
|
ORDER BY tt.name',
|
|
[get_request_var('id'), get_request_var('id')]);
|
|
|
|
if (cacti_sizeof($unmapped)) {
|
|
?>
|
|
<tr class='odd'>
|
|
<td colspan='2'>
|
|
<table>
|
|
<tr style='line-height:10px;'>
|
|
<td style='padding-right: 15px;'>
|
|
<?php print __('Add Threshold Template', 'thold'); ?>
|
|
</td>
|
|
<td>
|
|
<?php form_dropdown('thold_template_id', $unmapped, 'name', 'id', '', '', ''); ?>
|
|
</td>
|
|
<td>
|
|
<input type='button' value='<?php print __esc('Add', 'thold'); ?>' id='add_tt' title='<?php print __esc('Add Threshold Template to Device Template', 'thold'); ?>' onClick='addThresholdTemplate()'>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<script type='text/javascript' <?php print plugin_thold_csp_nonce(); ?>>
|
|
function addThresholdTemplate() {
|
|
$('#add_tt').click(function() {
|
|
scrollTop = $(window).scrollTop();
|
|
$.post('host_templates.php?header=false&action=item_add_tt', {
|
|
host_template_id: $('#id').val(),
|
|
thold_template_id: $('#thold_template_id').val(),
|
|
__csrf_magic: csrfMagicToken})
|
|
.done(function(data) {
|
|
loadPageNoHeader(urlPath+'host_templates.php?header=false&action=edit&id='+$('#id').val());
|
|
});
|
|
});
|
|
}
|
|
</script>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
}
|
|
|
|
html_end_box();
|
|
}
|
|
|
|
/**
|
|
* Renders the confirmation page for removing a threshold template's
|
|
* association with a device template (action=item_remove_tt_confirm).
|
|
* Registered as the 'device_template_top' Cacti hook.
|
|
*
|
|
* @return void
|
|
*/
|
|
function thold_device_template_top() {
|
|
if (get_request_var('action') == 'item_remove_tt_confirm') {
|
|
// ================= input validation =================
|
|
get_filter_request_var('id');
|
|
get_filter_request_var('host_template_id');
|
|
// ====================================================
|
|
|
|
form_start('host_templates.php?action=edit&id' . get_request_var('host_template_id'));
|
|
|
|
html_start_box('', '100%', false, '3', 'center', '');
|
|
|
|
$template = db_fetch_row_prepared('SELECT *
|
|
FROM thold_template
|
|
WHERE id = ?',
|
|
[get_request_var('id')]);
|
|
|
|
?>
|
|
<tr>
|
|
<td class='topBoxAlt'>
|
|
<p><?php print __('Click \'Continue\' to Delete the following Threshold Template will be disassociated from the Device Template.', 'thold'); ?></p>
|
|
<p><?php print __esc('Threshold Template Name: %s', $template['name'], 'thold'); ?>'<br>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td align='right'>
|
|
<input id='cancel' type='button' value='<?php print __esc('Cancel', 'thold'); ?>' onClick='$("#cdialog").dialog("close")' name='cancel'>
|
|
<input id='continue' type='button' value='<?php print __esc('Continue', 'thold'); ?>' name='continue' title='<?php print __esc('Remove Threshold Template', 'thold'); ?>'>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
|
|
html_end_box();
|
|
|
|
form_end();
|
|
|
|
?>
|
|
<script type='text/javascript'>
|
|
$(function() {
|
|
$('#cdialog').dialog();
|
|
});
|
|
|
|
$('#continue').click(function(data) {
|
|
$.post('host_templates.php?action=item_remove_tt', {
|
|
__csrf_magic: csrfMagicToken,
|
|
host_template_id: <?php print get_request_var('host_template_id'); ?>,
|
|
id: <?php print get_request_var('id'); ?>
|
|
}).done(function(data) {
|
|
$('#cdialog').dialog('close');
|
|
loadPageNoHeader('host_templates.php?action=edit&header=false&id=<?php print get_request_var('host_template_id'); ?>');
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
|
|
exit;
|
|
}
|
|
|
|
if (get_request_var('action') == 'item_remove_tt') {
|
|
// ================= input validation =================
|
|
get_filter_request_var('id');
|
|
get_filter_request_var('host_template_id');
|
|
// ====================================================
|
|
|
|
db_execute_prepared('DELETE
|
|
FROM plugin_thold_host_template
|
|
WHERE thold_template_id = ?
|
|
AND host_template_id = ?',
|
|
[get_request_var('id'), get_request_var('host_template_id')]);
|
|
|
|
header('Location: host_templates.php?header=false&action=edit&id=' . get_request_var('host_template_id'));
|
|
|
|
exit;
|
|
}
|
|
|
|
if (get_request_var('action') == 'item_add_tt') {
|
|
// ================= input validation =================
|
|
get_filter_request_var('host_template_id');
|
|
get_filter_request_var('thold_template_id');
|
|
// ====================================================
|
|
|
|
db_execute_prepared('REPLACE INTO plugin_thold_host_template
|
|
(host_template_id, thold_template_id) VALUES (?, ?)',
|
|
[get_request_var('host_template_id'), get_request_var('thold_template_id')]);
|
|
|
|
header('Location: host_templates.php?header=false&action=edit&id=' . get_request_var('host_template_id'));
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Propagates a device template's associated threshold templates to a
|
|
* device when its device template is changed, associating the device
|
|
* with every threshold template linked to the newly selected device
|
|
* template. Registered as the 'device_template_change' Cacti hook.
|
|
*
|
|
* @param array $data The device template change payload, including
|
|
* 'device_id' and 'device_template_id'.
|
|
*
|
|
* @return array The unmodified $data array, for hook chaining.
|
|
*/
|
|
function thold_device_template_change($data) {
|
|
$device_id = $data['device_id'];
|
|
$device_template_id = $data['device_template_id'];
|
|
|
|
$thold_templates = db_fetch_assoc_prepared('SELECT *
|
|
FROM plugin_thold_host_template
|
|
WHERE host_template_id = ?',
|
|
[$device_template_id]);
|
|
|
|
if (cacti_sizeof($thold_templates)) {
|
|
foreach ($thold_templates as $tt) {
|
|
db_execute_prepared('REPLACE INTO plugin_thold_host (host_id, thold_template_id) VALUES (?, ?)',
|
|
[$device_id, $tt['thold_template_id']]);
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Auto-creates thresholds for a device using its device template's
|
|
* associated threshold templates. Registered as the
|
|
* 'device_threshold_autocreate' Cacti hook.
|
|
*
|
|
* @param int $host_id The device id to auto-create thresholds for.
|
|
*
|
|
* @return int The unmodified $host_id, for hook chaining.
|
|
*/
|
|
function thold_device_autocreate($host_id) {
|
|
autocreate($host_id);
|
|
|
|
return $host_id;
|
|
}
|
|
|
|
/**
|
|
* Auto-creates a threshold for a newly created graph using its host's
|
|
* associated threshold templates, when the global thold_autocreate
|
|
* setting is enabled. Registered as the
|
|
* 'create_complete_graph_from_template' Cacti hook.
|
|
*
|
|
* @param array $save The newly created graph's saved field data,
|
|
* including its 'id'.
|
|
*
|
|
* @return array The unmodified $save array, for hook chaining.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include.
|
|
*/
|
|
function thold_create_graph_thold($save) {
|
|
global $config;
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
if (read_config_option('thold_autocreate') == 'on') {
|
|
$graph = db_fetch_row_prepared('SELECT *
|
|
FROM graph_local
|
|
WHERE id = ?',
|
|
[$save['id']]);
|
|
|
|
if (cacti_sizeof($graph)) {
|
|
autocreate($graph['host_id'], [$graph['id']]);
|
|
}
|
|
}
|
|
|
|
return $save;
|
|
}
|
|
|
|
/**
|
|
* Deletes any thresholds associated with data sources that are being
|
|
* removed, logging each removal for audit purposes. Registered as the
|
|
* 'data_source_remove' Cacti hook.
|
|
*
|
|
* @param array $data_ids The local_data_id values of the data sources
|
|
* being removed.
|
|
*
|
|
* @return array The unmodified $data_ids array, for hook chaining.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include.
|
|
*/
|
|
function thold_data_source_remove($data_ids) {
|
|
global $config;
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
$tholds = array_rekey(
|
|
db_fetch_assoc('SELECT id
|
|
FROM thold_data
|
|
WHERE local_data_id IN (' . implode(', ', $data_ids) . ')'),
|
|
'id', 'id'
|
|
);
|
|
|
|
if (cacti_sizeof($tholds)) {
|
|
foreach ($tholds as $thold) {
|
|
plugin_thold_log_changes($thold, 'deleted', ['message' => 'Deleted due to Data Source removal']);
|
|
thold_api_thold_remove($thold);
|
|
}
|
|
}
|
|
|
|
return $data_ids;
|
|
}
|
|
|
|
/**
|
|
* Registers a "TH[id, id, ...]" pattern with Cacti's log viewer (clog)
|
|
* regex hyperlinking system, so that threshold id references embedded
|
|
* in log messages are rendered as links via
|
|
* thold_clog_regex_threshold(). Registered as the 'clog_regex_array'
|
|
* Cacti hook.
|
|
*
|
|
* @param array $regex_array The existing list of clog hyperlink regex
|
|
* definitions.
|
|
*
|
|
* @return array The regex definitions list with this plugin's pattern
|
|
* added.
|
|
*/
|
|
function thold_clog_regex_array($regex_array) {
|
|
$regex_array[] = ['name' => 'TH', 'regex' => '( TH\[)([, \d]+)(\])', 'func' => 'thold_clog_regex_threshold'];
|
|
|
|
return $regex_array;
|
|
}
|
|
|
|
/**
|
|
* Replaces a "TH[id, id, ...]" log-message match with linked threshold
|
|
* descriptions (falling back to the raw threshold id when no matching
|
|
* threshold is found), used as the clog regex callback registered by
|
|
* thold_clog_regex_array().
|
|
*
|
|
* @param array $matches The regex match groups: [0] the full match,
|
|
* [1] the opening " TH[" text, [2] the
|
|
* comma-separated threshold ids, [3] the closing
|
|
* "]" text.
|
|
*
|
|
* @return string The rendered HTML with each threshold id replaced by a
|
|
* link to its edit page.
|
|
*
|
|
* @global array $config Cacti global configuration array; used to
|
|
* locate the library file to include and build
|
|
* edit link URLs.
|
|
*/
|
|
function thold_clog_regex_threshold($matches) {
|
|
global $config;
|
|
|
|
include_once($config['base_path'] . '/plugins/thold/includes/functions.php');
|
|
|
|
$result = $matches[0];
|
|
|
|
$threshold_ids = explode(',', str_replace(' ', '', $matches[2]));
|
|
|
|
if (cacti_sizeof($threshold_ids)) {
|
|
$result = '';
|
|
$thresholds = db_fetch_assoc('SELECT id, name, name_cache, local_data_id
|
|
FROM thold_data
|
|
WHERE id IN (' . implode(',',$threshold_ids) . ')');
|
|
|
|
$thresholdDescriptions = [];
|
|
|
|
if (cacti_sizeof($thresholds)) {
|
|
foreach ($thresholds as $threshold) {
|
|
$thresholdDescriptions[$threshold['id']] = html_escape(
|
|
!empty($threshold['name_cache']) ?
|
|
$threshold['name_cache'] :
|
|
thold_substitute_data_source_description($threshold['name'], $threshold['local_data_id'])
|
|
);
|
|
}
|
|
}
|
|
|
|
foreach ($threshold_ids as $threshold_id) {
|
|
$result .= $matches[1] . '<a href=\'' . $config['url_path'] .
|
|
'plugins/thold/thold.php?action=edit&id=' . $threshold_id . '\'>' .
|
|
html_escape(isset($thresholdDescriptions[$threshold_id]) ? $thresholdDescriptions[$threshold_id] : $threshold_id) .
|
|
'</a>' . $matches[3];
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Emits the JavaScript that initializes the multiselect widget for the
|
|
* "Pause Notifications" settings field on the Console settings page.
|
|
* Registered as the 'settings_bottom' Cacti hook.
|
|
*
|
|
* @return void
|
|
*/
|
|
function thold_settings_bottom() {
|
|
// This hook can fire on core pages that have not loaded includes/functions.php, where plugin_thold_csp_nonce() lives.
|
|
require_once(__DIR__ . '/includes/functions.php');
|
|
|
|
?>
|
|
<script type='text/javascript' <?php print plugin_thold_csp_nonce(); ?>>
|
|
$(function() {
|
|
$('#alert_notification_pause').multiselect();
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Removes files and directories that a previous version of this plugin
|
|
* shipped but that have since moved or been deleted, using the tombstone
|
|
* and whitelist lists in manifest.json. Whitelisted (user-data) paths and
|
|
* any VCS metadata (.git*) are never touched; the dev-only tests/ tree is
|
|
* removed. Any path that resolves outside the plugin directory (a tampered
|
|
* manifest.json) is refused, and any file/directory that cannot be removed
|
|
* (e.g. read-only) is reported to the Cacti log. Any top-level entry that is
|
|
* neither expected nor a tombstone nor whitelisted is logged to the Cacti
|
|
* log and left in place. Called on a plugin version change.
|
|
*
|
|
* @return void
|
|
*
|
|
* @global array $config Cacti global configuration array; used to resolve
|
|
* the plugin directory.
|
|
*/
|
|
function thold_prune_files(): void {
|
|
global $config;
|
|
|
|
$plugin_dir = $config['base_path'] . '/plugins/thold';
|
|
$manifest_path = $plugin_dir . '/manifest.json';
|
|
|
|
if (!is_readable($manifest_path)) {
|
|
return;
|
|
}
|
|
|
|
$manifest = json_decode((string) file_get_contents($manifest_path), true);
|
|
|
|
if (!is_array($manifest)) {
|
|
cacti_log('WARNING: thold manifest.json could not be parsed; skipping file prune', false, 'THOLD');
|
|
|
|
return;
|
|
}
|
|
|
|
$tombstones = isset($manifest['tombstones']) && is_array($manifest['tombstones']) ? $manifest['tombstones'] : [];
|
|
$expected = isset($manifest['expected']) && is_array($manifest['expected']) ? $manifest['expected'] : [];
|
|
$whitelist = isset($manifest['whitelist']) && is_array($manifest['whitelist']) ? $manifest['whitelist'] : [];
|
|
|
|
$protected = function (string $rel) use ($whitelist): bool {
|
|
if (strncmp($rel, '.git', 4) === 0 || strncmp($rel, '.md', 3) === 0) {
|
|
return true;
|
|
}
|
|
|
|
foreach ($whitelist as $entry) {
|
|
$entry = trim((string) $entry, '/');
|
|
|
|
if ($entry !== '' && ($rel === $entry
|
|
|| strncmp($rel, $entry . '/', strlen($entry) + 1) === 0
|
|
|| strncmp($entry, $rel . '/', strlen($rel) + 1) === 0)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
// Security: resolve the plugin directory so a tampered manifest.json
|
|
// cannot steer the prune outside of it.
|
|
$plugin_real = realpath($plugin_dir);
|
|
|
|
// Remove tombstoned (moved/deleted) paths plus the dev-only tests/
|
|
// tree and the phpunit.xml test configuration.
|
|
$remove = $tombstones;
|
|
$remove[] = 'tests/';
|
|
$remove[] = 'phpunit.xml';
|
|
|
|
foreach ($remove as $rel) {
|
|
$rel = trim((string) $rel, '/');
|
|
|
|
if ($rel === '' || $protected($rel)) {
|
|
continue;
|
|
}
|
|
|
|
// A tombstone must never contain '.'/'..' segments; a tampered manifest
|
|
// could use them to escape the plugin directory or target its root.
|
|
$segments = explode('/', $rel);
|
|
|
|
if (in_array('.', $segments, true) || in_array('..', $segments, true)) {
|
|
cacti_log(sprintf('WARNING: thold prune refused to remove %s: path contains a traversal segment (tampered manifest.json?)', $rel), false, 'THOLD');
|
|
|
|
continue;
|
|
}
|
|
|
|
$path = $plugin_dir . '/' . $rel;
|
|
|
|
if (!is_link($path) && !file_exists($path)) {
|
|
continue;
|
|
}
|
|
|
|
// Refuse any path that, after resolving symlinks and ../ segments,
|
|
// escapes the plugin directory (protects user data from a tampered
|
|
// manifest.json).
|
|
$anchor = is_link($path) ? dirname($path) : $path;
|
|
$real = realpath($anchor);
|
|
|
|
if ($real === false || ($real !== $plugin_real && strncmp($real, $plugin_real . DIRECTORY_SEPARATOR, strlen((string) $plugin_real) + 1) !== 0)) {
|
|
cacti_log(sprintf('WARNING: thold prune refused to remove %s: path resolves outside the plugin directory (tampered manifest.json?)', $rel), false, 'THOLD');
|
|
|
|
continue;
|
|
}
|
|
|
|
if (is_dir($path) && !is_link($path)) {
|
|
$removed = thold_rmtree($path);
|
|
} else {
|
|
$removed = @unlink($path);
|
|
}
|
|
|
|
if (!$removed) {
|
|
cacti_log(sprintf('WARNING: thold upgrade could not remove %s (check file/directory permissions)', $rel), false, 'THOLD');
|
|
}
|
|
}
|
|
|
|
// Surface any top-level entry the manifest does not account for.
|
|
$known = [];
|
|
|
|
foreach (array_merge($expected, $tombstones) as $entry) {
|
|
$top = explode('/', trim((string) $entry, '/'))[0];
|
|
|
|
if ($top !== '') {
|
|
$known[$top] = true;
|
|
}
|
|
}
|
|
|
|
$entries = scandir($plugin_dir);
|
|
|
|
foreach (($entries !== false ? $entries : []) as $entry) {
|
|
if ($entry === '.' || $entry === '..' || $entry === 'tests' || $entry === 'phpunit.xml' || $protected($entry) || isset($known[$entry])) {
|
|
continue;
|
|
}
|
|
|
|
cacti_log(sprintf('WARNING: thold upgrade found a file/directory not described in manifest.json: %s (left in place)', $entry), false, 'THOLD');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively deletes a directory and its contents. Symlinks are removed
|
|
* without being followed. Helper for thold_prune_files().
|
|
*
|
|
* @param string $dir Absolute path to the directory to remove.
|
|
*
|
|
* @return bool True if the directory and everything under it was removed;
|
|
* false if any entry could not be deleted.
|
|
*/
|
|
function thold_rmtree(string $dir): bool {
|
|
$entries = scandir($dir);
|
|
$ok = true;
|
|
|
|
foreach (($entries !== false ? $entries : []) as $entry) {
|
|
if ($entry === '.' || $entry === '..') {
|
|
continue;
|
|
}
|
|
|
|
$path = $dir . '/' . $entry;
|
|
|
|
if (is_dir($path) && !is_link($path)) {
|
|
if (!thold_rmtree($path)) {
|
|
$ok = false;
|
|
}
|
|
} elseif (!@unlink($path)) {
|
|
$ok = false;
|
|
}
|
|
}
|
|
|
|
if (!@rmdir($dir)) {
|
|
$ok = false;
|
|
}
|
|
|
|
return $ok;
|
|
}
|