'; } // 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 '
'; } } } /** * 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:|` / `|thold:low:|` * 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 "
"; print_r($g);print "
"; 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:\'' . __('Threshold Alert/Warning Values', 'thold') . '' . '\\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 "

" . __('Click \'Continue\' to apply all appropriate Thresholds to these Device(s).', 'thold') . "

    " . $save['host_list'] . '
'; 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'] = "" . ((empty($ds['data_template_name'])) ? '' . __('None', 'thold') . '' : html_escape($ds['data_template_name'])) . ''; } 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'] = "" . ((empty($ds['data_template_name'])) ? '' . __('None', 'thold') . '' : html_escape($ds['data_template_name'])) . ''; } } } 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 '*' . __('Auto-create Thresholds', 'thold') . '
'; } /** * 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) . '
' . $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 .= '
  • ' . html_escape(get_data_source_title($item)) . '
  • '; if (strlen($templates)) { $templates .= ", $data_template_id"; } else { $templates = "$data_template_id"; } } else { $not_found .= '
  • ' . html_escape(get_data_source_title($item)) . '
  • '; } } else { $not_found .= '
  • ' . html_escape(get_data_source_title($item)) . '
  • '; } } } 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 ""; if (strlen($found_list)) { if (strlen($not_found)) { print '

    ' . __('The following Data Sources have no Threshold Templates associated with them', 'thold') . '

    '; print '
      ' . $not_found . '
    '; } print '

    ' . __('Click \'Continue\' to create Thresholds for these Data Sources?', 'thold') . '

      ' . $found_list . "
    "; $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 '

    ' . __('There are no Threshold Templates associated with the following Data Sources', 'thold') . '

    '; print '
      ' . $not_found . '
    '; } } print '
    '; } 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) . '
    ' . $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 .= '
  • ' . html_escape(get_graph_title($item)) . '
  • '; } else { $found_list .= '
  • ' . html_escape(get_graph_title($item)) . '
  • '; } } } 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 "
    "; if (strlen($found_list)) { if (strlen($not_found)) { print '

    ' . __('The following Graphs have no Threshold Templates associated with them', 'thold') . '

    '; print '
      ' . $not_found . '
    '; } print '

    ' . __('Press \'Continue\' if you wish to create Threshold(s) for these Graph(s)', 'thold') . '

      ' . $found_list . "
    "; $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 '

    ' . __('There are no Threshold Templates associated with the following Graphs', 'thold') . '

    '; print '
      ' . $not_found . '
    '; } } print '
    '; } $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)) { ?> '; } $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)) { ?> '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] . '' . html_escape(isset($thresholdDescriptions[$threshold_id]) ? $thresholdDescriptions[$threshold_id] : $threshold_id) . '' . $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'); ?>
    '; } 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'); ?> 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'); ?> ) ' href=''> ' . __('No Associated Threshold Templates.', 'thold') . '
    ' title='' onClick='addThresholdTemplate()'>

    '

    ' onClick='$("#cdialog").dialog("close")' name='cancel'> ' name='continue' title=''>
    ) ' href=''> ' . __('No Associated Threshold Templates.', 'thold') . '
    ' id='add_tt' title='' onClick='addThresholdTemplate()'>

    '

    ' onClick='$("#cdialog").dialog("close")' name='cancel'> ' name='continue' title=''>