mirror of
https://github.com/Cacti/plugin_monitor.git
synced 2026-10-23 03:26:42 +09:00
* Relocate schema to includes/database.php via plugin table API + db_update_table upgrade * CI: allowlist unmeasured schema bootstrap and web entry points * CI: keep include_once for the settings-page-only lib/reports.php load (uncoverable) * Add manifest.json + upgrade-time file pruning; consolidate themes/ into css/ * database: keep user-owned dashboards table across uninstall api_plugin_db_table_create() records plugin_monitor_dashboards in plugin_db_changes, so api_plugin_db_changes_remove() (run by api_plugin_uninstall() after plugin_monitor_uninstall()) would drop it even though monitor_drop_tables() intentionally leaves it in place. Remove the 'create' ownership record after provisioning (install and upgrade paths) so user dashboards survive an uninstall. Adds a regression test. * Prune phpunit.xml on upgrade and ignore markdown-lint configs phpunit.xml ships in the repo for CI but is a dev-only test artifact, so it is removed from manifest.json 'expected', pruned from installs on upgrade (like tests/), and excluded from the manifest drift check. The retired markdown-lint configs (.mdlrc, .md_style.rb) are deleted, and .md* is now ignored like .git*: protected from pruning and excluded from drift if it reappears. * Move libraries into includes/, flatten css, drop sonar config db_functions.php, monitor_render.php and monitor_controller.php move into includes/ (the latter two lose the monitor_ prefix, becoming render.php and controller.php); monitor.php and the tests load them from there. Each per-theme stylesheet css/<theme>/monitor.css becomes css/<theme>.css so users can drop their own css/<theme>.css into the css base, and plugin_monitor_page_head() looks there, falling back to css/monitor.css. The unreferenced sonar-project.properties is removed. Old paths are tombstoned so existing installs drop the stale copies on upgrade. * Rename includes/db_functions.php to includes/functions.php Follows the fleet convention for the plugin's shared library filename; monitor.php, the measured-source list and the unit test load it from the new name. * Regenerate cacti.pot for the includes/ file relocations Moving the plugin's library files into includes/ changed the source-reference lines in the translation template; regenerate it so the 'Verify translation template' CI check passes. * Harden prune against tampered-manifest path traversal The upgrade-time prune now rejects any tombstone containing '.'/'..' segments (which could escape the plugin directory or resolve to its root) and treats ancestors of whitelist entries as protected, so a tombstone on a parent directory can no longer delete a whitelisted file beneath it. * docs: update and align the Project Structure block Point the structure tree at the relocated files (libraries now under includes/, data files under docs/, stylesheets under css/) and align the trailing '# ...' comments to a single column so they no longer drift right. * docs/tests: standardize test header and correct manifest-contract notes - Use the full license header (from the plugin's own setup.php) in tests/Unit/PruneFilesTest.php instead of the abbreviated copyright banner. - Document that the manifest drift check and the upgrade-time prune also handle phpunit.xml and .md* files, matching the implemented behavior. * refactor: rename prune helpers to the plugin-slug function prefix The repository naming contract reserves the plugin_<name>_ prefix for plugin lifecycle / hook-registration functions; all other functions use the plain <name>_ prefix. Rename the internal upgrade helpers accordingly: plugin_<name>_prune_files() -> <name>_prune_files() plugin_<name>_rmtree() -> <name>_rmtree() The call site, unit tests, and the copilot-instructions.md references are updated to match. No behavioral change. * chore: set the Cacti compatibility floor to 1.2.29 Align the compat metadata (and the contributor-guide references) with the team decision to standardize the minimum supported Cacti version at 1.2.29; the previously proposed 1.2.32 floor was not adopted. * Bump plugin version to 3.0 so the upgrade-time cleanup migration runs Add a 3.0 CHANGELOG section for this cycle's schema/manifest work and set INFO version = 3.0 so existing 2.9 installs detect the version change and run the schema refresh and manifest-based file prune. --------- Co-authored-by: Copilot <copilot@users.noreply.github.com>
1215 lines
39 KiB
PHP
1215 lines
39 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/ |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
/**
|
|
* Load saved dashboard URL parameters into request scope.
|
|
*
|
|
* @return void
|
|
*/
|
|
function loadDashboardSettings(): void {
|
|
$dashboard = get_filter_request_var('dashboard');
|
|
|
|
if ($dashboard > 0) {
|
|
$db_settings = db_fetch_cell_prepared(
|
|
'SELECT url
|
|
FROM plugin_monitor_dashboards
|
|
WHERE id = ?',
|
|
[$dashboard]
|
|
);
|
|
|
|
if ($db_settings != '') {
|
|
$db_settings = str_replace('monitor.php?', '', $db_settings);
|
|
$settings = explode('&', $db_settings);
|
|
|
|
if (cacti_sizeof($settings)) {
|
|
foreach ($settings as $setting) {
|
|
[$name, $value] = explode('=', $setting);
|
|
|
|
set_request_var($name, $value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render monitor page including filters, host layout, legend, and audio.
|
|
*
|
|
* @return void
|
|
*/
|
|
function drawPage(): void {
|
|
global $config, $iclasses, $icolorsdisplay, $mon_zoom_state, $dozoomrefresh, $dozoombgndcolor, $font_sizes;
|
|
global $new_form, $new_title;
|
|
|
|
$errored_list = getHostsDownOrTriggeredByPermission(true);
|
|
|
|
if (cacti_sizeof($errored_list) && read_user_setting('monitor_error_zoom') == 'on') {
|
|
if ($_SESSION['monitor_zoom_state'] == 0) {
|
|
$mon_zoom_state = $_SESSION['monitor_zoom_state'] = 1;
|
|
$_SESSION['mon_zoom_hist_status'] = get_nfilter_request_var('status');
|
|
$_SESSION['mon_zoom_hist_size'] = get_nfilter_request_var('size');
|
|
$dozoomrefresh = true;
|
|
$dozoombgndcolor = true;
|
|
}
|
|
} elseif (isset($_SESSION['monitor_zoom_state']) && $_SESSION['monitor_zoom_state'] == 1) {
|
|
$_SESSION['monitor_zoom_state'] = 0;
|
|
$dozoomrefresh = true;
|
|
$dozoombgndcolor = false;
|
|
}
|
|
|
|
$name = db_fetch_cell_prepared(
|
|
'SELECT name
|
|
FROM plugin_monitor_dashboards
|
|
WHERE id = ?',
|
|
[get_request_var('dashboard')]
|
|
);
|
|
|
|
if ($name == '') {
|
|
$name = __('New Dashboard', 'monitor');
|
|
}
|
|
|
|
$new_form = "<div id='newdialog'> <form id='new_dashboard'> <table class='monitorTableHeader'> <tr> <td colspan='2'>" . __('Enter the Dashboard Name and then press \'Save\' to continue, else press \'Cancel\'', 'monitor') . '</td> </tr> <tr> <td>' . __('Dashboard', 'monitor') . "</td> <td><input id='name' class='ui-state-default ui-corner-all' type='text' size='30' value='" . html_escape($name) . "'></td> </tr> </table> </form> </div>";
|
|
|
|
$new_title = __('Create New Dashboard', 'monitor');
|
|
|
|
findDownHosts();
|
|
|
|
general_header();
|
|
|
|
drawFilterAndStatus();
|
|
|
|
print '<tr><td>';
|
|
|
|
// Default with permissions = default_by_permission
|
|
// Tree = group_by_tree
|
|
$function = 'render' . ucfirst(get_request_var('grouping'));
|
|
|
|
if (function_exists($function) && get_request_var('view') != 'list') {
|
|
if (get_request_var('grouping') == 'default' || get_request_var('grouping') == 'site') {
|
|
html_start_box(__('Monitored Devices', 'monitor'), '100%', true, 3, 'center', '');
|
|
} else {
|
|
html_start_box('', '100%', true, 3, 'center', '');
|
|
}
|
|
print $function();
|
|
} else {
|
|
print renderDefault();
|
|
}
|
|
|
|
print '</td></tr>';
|
|
|
|
html_end_box();
|
|
|
|
if (read_user_setting('monitor_legend', read_config_option('monitor_legend'))) {
|
|
print "<div class='center monitor_legend'>";
|
|
|
|
foreach ($iclasses as $index => $class) {
|
|
print "<div class='monitor_legend_cell center $class" . "Full'>" . $icolorsdisplay[$index] . '</div>';
|
|
}
|
|
|
|
print '</div>';
|
|
}
|
|
|
|
// If the host is down, we need to insert the embedded wav file
|
|
$monitor_sound = getMonitorSound();
|
|
|
|
if (isMonitorAudible()) {
|
|
if (read_user_setting('monitor_sound_loop', read_config_option('monitor_sound_loop'))) {
|
|
print "<audio id='audio' playsinline='' loop src='" . html_escape($config['url_path'] . 'plugins/monitor/sounds/' . $monitor_sound) . "'></audio>";
|
|
} else {
|
|
print "<audio id='audio' playsinline='' src='" . html_escape($config['url_path'] . 'plugins/monitor/sounds/' . $monitor_sound) . "'></audio>";
|
|
}
|
|
}
|
|
|
|
print '<div class="center monitorFooter">' . getFilterText() . '</div>';
|
|
|
|
bottom_footer();
|
|
}
|
|
|
|
/**
|
|
* Determine if alert audio is available and should be considered playable.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isMonitorAudible(): bool {
|
|
return getMonitorSound() != '';
|
|
}
|
|
|
|
/**
|
|
* Resolve configured monitor sound file if it exists on disk.
|
|
*
|
|
* @return string
|
|
*/
|
|
function getMonitorSound(): string {
|
|
$sound = (string) read_user_setting('monitor_sound', read_config_option('monitor_sound'));
|
|
clearstatcache();
|
|
$file = __DIR__ . '/sounds/' . $sound;
|
|
$exists = file_exists($file);
|
|
|
|
return $exists ? $sound : '';
|
|
}
|
|
|
|
/**
|
|
* Update down-host request flags and mute state based on current host status.
|
|
*
|
|
* @return void
|
|
*/
|
|
function findDownHosts(): void {
|
|
$dhosts = getHostsDownOrTriggeredByPermission(false);
|
|
|
|
if (cacti_sizeof($dhosts)) {
|
|
set_request_var('downhosts', 'true');
|
|
|
|
if (isset($_SESSION['monitor_muted_hosts'])) {
|
|
unmuteUpNonTriggeredHosts($dhosts);
|
|
|
|
$unmuted_hosts = array_diff($dhosts, $_SESSION['monitor_muted_hosts']);
|
|
|
|
if (cacti_sizeof($unmuted_hosts)) {
|
|
unmuteUser();
|
|
}
|
|
} else {
|
|
set_request_var('mute', 'false');
|
|
}
|
|
} else {
|
|
unmuteAllHosts();
|
|
set_request_var('downhosts', 'false');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove recovered hosts from muted-host session state.
|
|
*
|
|
* @param array $dhosts Current down/triggered host id list.
|
|
*
|
|
* @return void
|
|
*/
|
|
function unmuteUpNonTriggeredHosts(array $dhosts): void {
|
|
if (isset($_SESSION['monitor_muted_hosts'])) {
|
|
foreach ($_SESSION['monitor_muted_hosts'] as $index => $host_id) {
|
|
if (array_search($host_id, $dhosts, true) === false) {
|
|
unset($_SESSION['monitor_muted_hosts'][$index]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mute all currently down/triggered hosts for the active user session.
|
|
*
|
|
* @return void
|
|
*/
|
|
function muteAllHosts(): void {
|
|
$_SESSION['monitor_muted_hosts'] = getHostsDownOrTriggeredByPermission(false);
|
|
muteUser();
|
|
}
|
|
|
|
/**
|
|
* Clear muted-host list and unmute monitor notifications for this user.
|
|
*
|
|
* @return void
|
|
*/
|
|
function unmuteAllHosts(): void {
|
|
$_SESSION['monitor_muted_hosts'] = [];
|
|
unmuteUser();
|
|
}
|
|
|
|
/**
|
|
* Persist user mute state as enabled.
|
|
*
|
|
* @return void
|
|
*/
|
|
function muteUser(): void {
|
|
set_request_var('mute', 'true');
|
|
set_user_setting('monitor_mute', 'true');
|
|
}
|
|
|
|
/**
|
|
* Persist user mute state as disabled.
|
|
*
|
|
* @return void
|
|
*/
|
|
function unmuteUser(): void {
|
|
set_request_var('mute', 'false');
|
|
set_user_setting('monitor_mute', 'false');
|
|
}
|
|
|
|
/**
|
|
* Build footer text describing currently active monitor filters.
|
|
*
|
|
* @return string
|
|
*/
|
|
function getFilterText(): string {
|
|
$filter = '<div class="center monitorFooterText">';
|
|
|
|
switch (get_request_var('status')) {
|
|
case '-4':
|
|
$filter .= __('Devices without Thresholds', 'monitor');
|
|
|
|
break;
|
|
case '-3':
|
|
$filter .= __('Not Monitored Devices', 'monitor');
|
|
|
|
break;
|
|
case '-2':
|
|
$filter .= __('All Devices', 'monitor');
|
|
|
|
break;
|
|
case '-1':
|
|
$filter .= __('All Monitored Devices', 'monitor');
|
|
|
|
break;
|
|
case '0':
|
|
$filter .= __('Monitored Devices either Down or Recovering', 'monitor');
|
|
|
|
break;
|
|
case '1':
|
|
$filter .= __('Monitored Devices either Down, Recovering, or with Triggered Thresholds', 'monitor');
|
|
|
|
break;
|
|
case '2':
|
|
$filter .= __('Monitored Devices either Down, Recovering, or with Breached or Triggered Thresholds', 'monitor');
|
|
|
|
break;
|
|
default:
|
|
$filter .= __('Unknown monitoring status (%s)', get_request_var('status'), 'monitor');
|
|
}
|
|
|
|
switch (get_request_var('crit')) {
|
|
case '0':
|
|
$filter .= __(', and All Criticalities', 'monitor');
|
|
|
|
break;
|
|
case '1':
|
|
$filter .= __(', and of Low Criticality or Higher', 'monitor');
|
|
|
|
break;
|
|
case '2':
|
|
$filter .= __(', and of Medium Criticality or Higher', 'monitor');
|
|
|
|
break;
|
|
case '3':
|
|
$filter .= __(', and of High Criticality or Higher', 'monitor');
|
|
|
|
break;
|
|
case '4':
|
|
$filter .= __(', and of Mission Critical Status', 'monitor');
|
|
|
|
break;
|
|
}
|
|
|
|
$filter .= __('</div><div class="center monitorFooterTextBold">Remember to first select eligible Devices to be Monitored from the Devices page!</div></div></div>', 'monitor');
|
|
|
|
return $filter;
|
|
}
|
|
|
|
/**
|
|
* Render one filter dropdown (or hidden fallback input) for the monitor form.
|
|
*
|
|
* @param string $id Filter field id/name.
|
|
* @param string $title Filter display title.
|
|
* @param array $settings Option map of value => label.
|
|
* @param string|int $value Selected value override.
|
|
*
|
|
* @return void
|
|
*/
|
|
function drawFilterDropdown(string $id, string $title, array $settings = [], mixed $value = null): void {
|
|
if ($value == null) {
|
|
$value = get_nfilter_request_var($id);
|
|
}
|
|
|
|
if (cacti_sizeof($settings)) {
|
|
print '<td>' . html_escape($title) . '</td>';
|
|
print '<td><select id="' . $id . '" title="' . html_escape($title) . '">' . PHP_EOL;
|
|
|
|
foreach ($settings as $setting_value => $setting_name) {
|
|
if ($value == null || $value == '') {
|
|
$value = $setting_value;
|
|
}
|
|
|
|
$setting_selected = ($value == $setting_value) ? ' selected' : '';
|
|
|
|
print '<option value="' . $setting_value . '"' . $setting_selected . '>' . html_escape($setting_name) . '</option>' . PHP_EOL;
|
|
}
|
|
|
|
print '</select></td>' . PHP_EOL;
|
|
} else {
|
|
print "<td style='display:none;'><input type='hidden' id='$id' value='" . html_escape($value) . "'></td>" . PHP_EOL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build dashboard dropdown option map for current user context.
|
|
*
|
|
* @return array
|
|
*/
|
|
function monitorGetDashboardOptions(): array {
|
|
$dashboards = [0 => __('Unsaved', 'monitor')];
|
|
$dashboards += array_rekey(
|
|
db_fetch_assoc_prepared(
|
|
'SELECT id, name
|
|
FROM plugin_monitor_dashboards
|
|
WHERE user_id = 0 OR user_id = ?
|
|
ORDER BY name',
|
|
[$_SESSION['sess_user_id']]
|
|
),
|
|
'id',
|
|
'name'
|
|
);
|
|
|
|
return $dashboards;
|
|
}
|
|
|
|
/**
|
|
* Resolve zoom override dropdown state from session values.
|
|
*
|
|
* @param bool $dozoombgndcolor Zoom background flag, updated in place.
|
|
*
|
|
* @return array{int|null, string|null}
|
|
*/
|
|
function monitorGetZoomDropdownState(bool &$dozoombgndcolor): array {
|
|
$mon_zoom_status = null;
|
|
$mon_zoom_size = null;
|
|
|
|
if (isset($_SESSION['monitor_zoom_state'])) {
|
|
if ($_SESSION['monitor_zoom_state'] == 1) {
|
|
$mon_zoom_status = 2;
|
|
$mon_zoom_size = 'monitor_errorzoom';
|
|
$dozoombgndcolor = true;
|
|
} else {
|
|
if (isset($_SESSION['mon_zoom_hist_status'])) {
|
|
$mon_zoom_status = $_SESSION['mon_zoom_hist_status'];
|
|
}
|
|
|
|
if (isset($_SESSION['mon_zoom_hist_size'])) {
|
|
$currentddsize = get_nfilter_request_var('size');
|
|
|
|
if ($currentddsize != $_SESSION['mon_zoom_hist_size'] && $currentddsize != 'monitor_errorzoom') {
|
|
$_SESSION['mon_zoom_hist_size'] = $currentddsize;
|
|
}
|
|
|
|
$mon_zoom_size = $_SESSION['mon_zoom_hist_size'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return [$mon_zoom_status, $mon_zoom_size];
|
|
}
|
|
|
|
/**
|
|
* Render the primary filter row (layout/status/view/grouping/actions).
|
|
*
|
|
* @param array $dashboards Dashboard option map.
|
|
* @param array $monitor_status Status filter options.
|
|
* @param array $monitor_view_type View mode options.
|
|
* @param array $monitor_grouping Grouping mode options.
|
|
* @param array $item_rows Device row count options.
|
|
* @param int|null $mon_zoom_status Zoom-driven status override.
|
|
*
|
|
* @return void
|
|
*/
|
|
function monitorRenderPrimaryFilterRow(array $dashboards, array $monitor_status, array $monitor_view_type, array $monitor_grouping, array $item_rows, mixed $mon_zoom_status): void {
|
|
drawFilterDropdown('dashboard', __('Layout', 'monitor'), $dashboards);
|
|
drawFilterDropdown('status', __('Status', 'monitor'), $monitor_status, $mon_zoom_status);
|
|
drawFilterDropdown('view', __('View', 'monitor'), $monitor_view_type);
|
|
drawFilterDropdown('grouping', __('Grouping', 'monitor'), $monitor_grouping);
|
|
drawFilterDropdown('rows', __('Devices', 'monitor'), $item_rows);
|
|
|
|
print '<td><span>' . PHP_EOL;
|
|
print '<input type="submit" value="' . __esc('Refresh', 'monitor') . '" id="go" title="' . __esc('Refresh the Device List', 'monitor') . '">' . PHP_EOL;
|
|
print '<input type="button" value="' . __esc('Clear', 'monitor') . '" id="clear" title="' . __esc('Clear the set filter', 'monitor') . '">' . PHP_EOL;
|
|
print '<input type="button" value="' . __esc('Save', 'monitor') . '" id="save" title="' . __esc('Save Filter Settings', 'monitor') . '">' . PHP_EOL;
|
|
print '<input type="button" value="' . __esc('New', 'monitor') . '" id="new" title="' . __esc('Save New Dashboard', 'monitor') . '">' . PHP_EOL;
|
|
|
|
if (get_request_var('dashboard') > 0) {
|
|
print '<input type="button" value="' . __esc('Rename', 'monitor') . '" id="rename" title="' . __esc('Rename Dashboard', 'monitor') . '">' . PHP_EOL;
|
|
print '<input type="button" value="' . __esc('Delete', 'monitor') . '" id="delete" title="' . __esc('Delete Dashboard', 'monitor') . '">' . PHP_EOL;
|
|
}
|
|
|
|
print '<input type="button" value="' . (get_request_var('mute') == 'false' ? getMuteText() : getUnmuteText()) . '" id="sound" title="' . (get_request_var('mute') == 'false' ? __('%s Alert for downed Devices', getMuteText(), 'monitor') : __('%s Alerts for downed Devices', getUnmuteText(), 'monitor')) . '">' . PHP_EOL;
|
|
print '<input id="downhosts" type="hidden" value="' . html_escape(get_request_var('downhosts')) . '"><input id="mute" type="hidden" value="' . html_escape(get_request_var('mute')) . '">' . PHP_EOL;
|
|
print '</span></td>';
|
|
}
|
|
|
|
/**
|
|
* Render secondary grouping/filter controls for monitor page.
|
|
*
|
|
* @param array $classes Size class options.
|
|
* @param array $criticalities Criticality options.
|
|
* @param array $monitor_trim Trim options.
|
|
* @param array $page_refresh_interval Refresh options.
|
|
* @param string|null $mon_zoom_size Zoom-driven size override.
|
|
*
|
|
* @return void
|
|
*/
|
|
function monitorRenderGroupingDropdowns(array $classes, array $criticalities, array $monitor_trim, array $page_refresh_interval, mixed $mon_zoom_size): void {
|
|
drawFilterDropdown('crit', __('Criticality', 'monitor'), $criticalities);
|
|
|
|
if (get_request_var('view') != 'list') {
|
|
drawFilterDropdown('size', __('Size', 'monitor'), $classes, $mon_zoom_size);
|
|
}
|
|
|
|
if (get_request_var('view') == 'default' || get_request_var('view') == 'names') {
|
|
drawFilterDropdown('trim', __('Trim', 'monitor'), $monitor_trim);
|
|
}
|
|
|
|
if (get_nfilter_request_var('grouping') == 'tree') {
|
|
$trees = [];
|
|
|
|
if (get_request_var('grouping') == 'tree') {
|
|
$trees_allowed = array_rekey(get_allowed_trees(), 'id', 'name');
|
|
|
|
if (cacti_sizeof($trees_allowed)) {
|
|
$trees_prefix = [-1 => __('All Trees', 'monitor')];
|
|
$trees_suffix = [-2 => __('Non-Tree Devices', 'monitor')];
|
|
$trees = $trees_prefix + $trees_allowed + $trees_suffix;
|
|
}
|
|
}
|
|
|
|
drawFilterDropdown('tree', __('Tree', 'monitor'), $trees);
|
|
}
|
|
|
|
if (get_nfilter_request_var('grouping') == 'site') {
|
|
$sites = [];
|
|
|
|
if (get_request_var('grouping') == 'site') {
|
|
$sites = array_rekey(
|
|
db_fetch_assoc('SELECT id, name
|
|
FROM sites
|
|
ORDER BY name'),
|
|
'id',
|
|
'name'
|
|
);
|
|
|
|
if (cacti_sizeof($sites)) {
|
|
$sites_prefix = [-1 => __('All Sites', 'monitor')];
|
|
$sites_suffix = [-2 => __('Non-Site Devices', 'monitor')];
|
|
$sites = $sites_prefix + $sites + $sites_suffix;
|
|
}
|
|
}
|
|
|
|
drawFilterDropdown('site', __('Sites', 'monitor'), $sites);
|
|
}
|
|
|
|
if (get_request_var('grouping') == 'template') {
|
|
$templates = [];
|
|
$templates_allowed = array_rekey(
|
|
db_fetch_assoc('SELECT ht.id, ht.name, COUNT(gl.id) AS graphs
|
|
FROM host_template AS ht
|
|
INNER JOIN host AS h
|
|
ON h.host_template_id = ht.id
|
|
INNER JOIN graph_local AS gl
|
|
ON h.id = gl.host_id
|
|
GROUP BY ht.id
|
|
HAVING graphs > 0'),
|
|
'id',
|
|
'name'
|
|
);
|
|
|
|
if (cacti_sizeof($templates_allowed)) {
|
|
$templates_prefix = [-1 => __('All Templates', 'monitor')];
|
|
$templates_suffix = [-2 => __('Non-Templated Devices', 'monitor')];
|
|
$templates = $templates_prefix + $templates_allowed + $templates_suffix;
|
|
}
|
|
|
|
drawFilterDropdown('template', __('Template', 'monitor'), $templates);
|
|
}
|
|
|
|
drawFilterDropdown('refresh', __('Refresh', 'monitor'), $page_refresh_interval);
|
|
}
|
|
|
|
/**
|
|
* Render hidden fallback input fields for inactive filters.
|
|
*
|
|
* @return void
|
|
*/
|
|
function monitorRenderHiddenFilterInputs(): void {
|
|
if (get_request_var('grouping') != 'tree') {
|
|
print '<td><input type="hidden" id="tree" value="' . html_escape(get_request_var('tree')) . '"></td>' . PHP_EOL;
|
|
}
|
|
|
|
if (get_request_var('grouping') != 'site') {
|
|
print '<td><input type="hidden" id="site" value="' . html_escape(get_request_var('site')) . '"></td>' . PHP_EOL;
|
|
}
|
|
|
|
if (get_request_var('grouping') != 'template') {
|
|
print '<td><input type="hidden" id="template" value="' . html_escape(get_request_var('template')) . '"></td>' . PHP_EOL;
|
|
}
|
|
|
|
if (get_request_var('view') == 'list') {
|
|
print '<td><input type="hidden" id="size" value="' . html_escape(get_request_var('size')) . '"></td>' . PHP_EOL;
|
|
}
|
|
|
|
if (get_request_var('view') != 'default') {
|
|
print '<td><input type="hidden" id="trim" value="' . html_escape(get_request_var('trim')) . '"></td>' . PHP_EOL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resolve zoom background color/font style for page rendering.
|
|
*
|
|
* @param bool $dozoombgndcolor Whether zoom background styling is enabled.
|
|
*
|
|
* @return array{string, string}
|
|
*/
|
|
function monitorGetZoomBackgroundStyle(bool $dozoombgndcolor): array {
|
|
if ($dozoombgndcolor) {
|
|
$mbcolora = db_fetch_row_prepared(
|
|
'SELECT *
|
|
FROM colors
|
|
WHERE id = ?',
|
|
[read_user_setting('monitor_error_background')]
|
|
);
|
|
|
|
$monitor_error_fontsize = read_user_setting('monitor_error_fontsize') . 'px';
|
|
$mbcolor = cacti_sizeof($mbcolora) ? '#' . $mbcolora['hex'] : 'snow';
|
|
} else {
|
|
$mbcolor = '';
|
|
$monitor_error_fontsize = '10px';
|
|
}
|
|
|
|
return [$mbcolor, $monitor_error_fontsize];
|
|
}
|
|
|
|
/**
|
|
* Emit JavaScript bootstrap config and monitor JS include tag.
|
|
*
|
|
* @param array $config Global Cacti config.
|
|
* @param string $mbcolor Monitor background color.
|
|
* @param string $monitor_error_fontsize Zoom mode font size.
|
|
* @param bool $dozoomrefresh Auto-refresh flag for zoom mode.
|
|
* @param string $new_form New dashboard dialog markup.
|
|
* @param string $new_title New dashboard dialog title.
|
|
*
|
|
* @return void
|
|
*/
|
|
function monitorPrintJsBootstrap(array $config, string $mbcolor, string $monitor_error_fontsize, bool $dozoomrefresh, string $new_form, string $new_title): void {
|
|
$monitor_js_config = [
|
|
'mbColor' => $mbcolor,
|
|
'monitorFont' => $monitor_error_fontsize,
|
|
'doZoomRefresh' => $dozoomrefresh,
|
|
'newForm' => $new_form,
|
|
'newTitle' => $new_title,
|
|
'messages' => [
|
|
'filterSaved' => __(' [ Filter Settings Saved ]', 'monitor'),
|
|
'cancel' => __('Cancel', 'monitor'),
|
|
'save' => __('Save', 'monitor')
|
|
]
|
|
];
|
|
|
|
print '<script type="text/javascript" ' . plugin_monitor_csp_nonce() . '>window.monitorPageConfig = ' .
|
|
json_encode($monitor_js_config, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) .
|
|
';</script>';
|
|
print get_md5_include_js('plugins/monitor/js/monitor.js');
|
|
}
|
|
|
|
/**
|
|
* Render monitor filter area and inject page JS bootstrap config.
|
|
*
|
|
* @return void
|
|
*/
|
|
function drawFilterAndStatus(): void {
|
|
global $config, $criticalities, $page_refresh_interval, $classes, $monitor_grouping;
|
|
global $monitor_view_type, $monitor_status, $monitor_trim;
|
|
global $dozoombgndcolor, $dozoomrefresh, $zoom_hist_status, $zoom_hist_size, $mon_zoom_state;
|
|
global $new_form, $new_title, $item_rows;
|
|
|
|
$header = __('Monitor Filter [ Last Refresh: %s ]', date('g:i:s a', time()), 'monitor') . (get_request_var('refresh') < 99999 ? __(' [ Refresh Again in <i style="padding:0px !important;margin:0px;" id="timer">%d</i> Seconds ]', get_request_var('refresh'), 'monitor') : '') . (get_request_var('view') == 'list' ? __('[ Showing only first 30 Devices ]', 'monitor') : '') . '<span id="text" style="vertical-align:baseline;padding:0px !important;display:none"></span>';
|
|
|
|
html_start_box($header, '100%', false, 3, 'center', '');
|
|
|
|
print '<tr class="even"><td>' . PHP_EOL;
|
|
print '<form class="monitorFilterForm">' . PHP_EOL;
|
|
|
|
print '<table class="filterTable">' . PHP_EOL;
|
|
print '<tr class="even">' . PHP_EOL;
|
|
[$mon_zoom_status, $mon_zoom_size] = monitorGetZoomDropdownState($dozoombgndcolor);
|
|
monitorRenderPrimaryFilterRow(
|
|
monitorGetDashboardOptions(),
|
|
$monitor_status,
|
|
$monitor_view_type,
|
|
$monitor_grouping,
|
|
$item_rows,
|
|
$mon_zoom_status
|
|
);
|
|
print '</tr>';
|
|
print '</table>';
|
|
|
|
// Second line of filter
|
|
print '<table class="filterTable">' . PHP_EOL;
|
|
print '<tr>' . PHP_EOL;
|
|
print '<td>' . __('Search', 'monitor') . '</td>';
|
|
print '<td><input type="text" size="30" id="rfilter" value="' . html_escape_request_var('rfilter') . '"></input></td>';
|
|
monitorRenderGroupingDropdowns($classes, $criticalities, $monitor_trim, $page_refresh_interval, $mon_zoom_size);
|
|
monitorRenderHiddenFilterInputs();
|
|
|
|
print '</tr>';
|
|
print '</table>';
|
|
print '</form></td></tr>' . PHP_EOL;
|
|
|
|
html_end_box();
|
|
|
|
[$mbcolor, $monitor_error_fontsize] = monitorGetZoomBackgroundStyle($dozoombgndcolor);
|
|
monitorPrintJsBootstrap($config, $mbcolor, $monitor_error_fontsize, $dozoomrefresh, $new_form, $new_title);
|
|
}
|
|
|
|
/**
|
|
* Get action label for mute button based on current audio capability.
|
|
*
|
|
* @return string
|
|
*/
|
|
function getMuteText(): string {
|
|
if (isMonitorAudible()) {
|
|
return __('Mute', 'monitor');
|
|
} else {
|
|
return __('Acknowledge', 'monitor');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get action label for unmute/reset button based on audio capability.
|
|
*
|
|
* @return string
|
|
*/
|
|
function getUnmuteText(): string {
|
|
if (isMonitorAudible()) {
|
|
return __('Un-Mute', 'monitor');
|
|
} else {
|
|
return __('Reset', 'monitor');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete selected dashboard when owned by current user.
|
|
*
|
|
* @return void
|
|
*/
|
|
function removeDashboard(): void {
|
|
$dashboard = get_filter_request_var('dashboard');
|
|
|
|
$name = db_fetch_cell_prepared(
|
|
'SELECT name
|
|
FROM plugin_monitor_dashboards
|
|
WHERE id = ?
|
|
AND user_id = ?',
|
|
[$dashboard, $_SESSION['sess_user_id']]
|
|
);
|
|
|
|
if ($name != '') {
|
|
db_execute_prepared(
|
|
'DELETE FROM plugin_monitor_dashboards
|
|
WHERE id = ?',
|
|
[$dashboard]
|
|
);
|
|
|
|
raise_message('removed', __('Dashboard \'%s\' Removed.', $name, 'monitor'), MESSAGE_LEVEL_INFO);
|
|
} else {
|
|
$name = db_fetch_cell_prepared(
|
|
'SELECT name
|
|
FROM plugin_monitor_dashboards
|
|
WHERE id = ?',
|
|
[$dashboard]
|
|
);
|
|
|
|
raise_message('notremoved', __('Dashboard \'%s\' is not owned by you.', $name, 'monitor'), MESSAGE_LEVEL_ERROR);
|
|
}
|
|
|
|
set_request_var('dashboard', '0');
|
|
}
|
|
|
|
/**
|
|
* Save monitor filter settings to user prefs or selected dashboard record.
|
|
*
|
|
* @return void
|
|
*/
|
|
function saveSettings(): void {
|
|
if (isset_request_var('dashboard') && get_filter_request_var('dashboard') != 0) {
|
|
$save_db = true;
|
|
} else {
|
|
$save_db = false;
|
|
}
|
|
|
|
validateRequestVars();
|
|
|
|
if (!$save_db) {
|
|
if (cacti_sizeof($_REQUEST)) {
|
|
foreach ($_REQUEST as $var => $value) {
|
|
switch ($var) {
|
|
case 'dashboard':
|
|
set_user_setting('monitor_rfilter', get_request_var('dashboard'));
|
|
|
|
break;
|
|
case 'rfilter':
|
|
set_user_setting('monitor_rfilter', get_request_var('rfilter'));
|
|
|
|
break;
|
|
case 'refresh':
|
|
set_user_setting('monitor_refresh', get_request_var('refresh'));
|
|
|
|
break;
|
|
case 'grouping':
|
|
set_user_setting('monitor_grouping', get_request_var('grouping'));
|
|
|
|
break;
|
|
case 'view':
|
|
set_user_setting('monitor_view', get_request_var('view'));
|
|
|
|
break;
|
|
case 'rows':
|
|
set_user_setting('monitor_rows', get_request_var('rows'));
|
|
|
|
break;
|
|
case 'crit':
|
|
set_user_setting('monitor_crit', get_request_var('crit'));
|
|
|
|
break;
|
|
case 'mute':
|
|
set_user_setting('monitor_mute', get_request_var('mute'));
|
|
|
|
break;
|
|
case 'size':
|
|
set_user_setting('monitor_size', get_request_var('size'));
|
|
|
|
break;
|
|
case 'trim':
|
|
set_user_setting('monitor_trim', get_request_var('trim'));
|
|
|
|
break;
|
|
case 'status':
|
|
set_user_setting('monitor_status', get_request_var('status'));
|
|
|
|
break;
|
|
case 'tree':
|
|
set_user_setting('monitor_tree', get_request_var('tree'));
|
|
|
|
break;
|
|
case 'mute':
|
|
set_user_setting('monitor_mute', get_request_var('mute'));
|
|
|
|
break;
|
|
case 'site':
|
|
set_user_setting('monitor_site', get_request_var('site'));
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$url = 'monitor.php' .
|
|
'?refresh=' . get_request_var('refresh') .
|
|
'&grouping=' . get_request_var('grouping') .
|
|
'&view=' . get_request_var('view') .
|
|
'&rows=' . get_request_var('rows') .
|
|
'&crit=' . get_request_var('crit') .
|
|
'&size=' . get_request_var('size') .
|
|
'&trim=' . get_request_var('trim') .
|
|
'&status=' . get_request_var('status') .
|
|
'&tree=' . get_request_var('tree') .
|
|
'&site=' . get_request_var('site');
|
|
|
|
if (!isset_request_var('user')) {
|
|
$user = $_SESSION['sess_user_id'];
|
|
} else {
|
|
$user = get_request_var('user');
|
|
}
|
|
|
|
$id = get_request_var('dashboard');
|
|
$name = get_nfilter_request_var('name');
|
|
|
|
$save = [];
|
|
$save['id'] = $id;
|
|
$save['name'] = $name;
|
|
$save['user_id'] = $user;
|
|
$save['url'] = $url;
|
|
|
|
$id = sql_save($save, 'plugin_monitor_dashboards');
|
|
|
|
if (!empty($id)) {
|
|
raise_message('monitorsaved', __('Dashboard \'%s\' has been Saved!', $name, 'monitor'), MESSAGE_LEVEL_INFO);
|
|
set_request_var('dashboard', $id);
|
|
} else {
|
|
raise_message('monitornotsaved', __('Dashboard \'%s\' could not be Saved!', $name, 'monitor'), MESSAGE_LEVEL_INFO);
|
|
set_request_var('dashboard', '0');
|
|
}
|
|
}
|
|
|
|
validateRequestVars(true);
|
|
}
|
|
|
|
/**
|
|
* Validate and persist monitor request/session filter variables.
|
|
*
|
|
* @param bool $force Force reload from saved defaults.
|
|
*
|
|
* @return void
|
|
*/
|
|
function validateRequestVars(bool $force = false): void {
|
|
// ================= input validation and session storage =================
|
|
$filters = [
|
|
'refresh' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => read_user_setting('monitor_refresh', read_config_option('monitor_refresh'), $force)
|
|
],
|
|
'dashboard' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_dashboard', '0', $force)
|
|
],
|
|
'rfilter' => [
|
|
'filter' => FILTER_VALIDATE_IS_REGEX,
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_rfilter', '', $force)
|
|
],
|
|
'name' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'options' => ['options' => 'sanitize_search_string'],
|
|
'default' => ''
|
|
],
|
|
'mute' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'options' => ['options' => 'sanitize_search_string'],
|
|
'default' => read_user_setting('monitor_mute', 'false', $force)
|
|
],
|
|
'grouping' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'options' => ['options' => 'sanitize_search_string'],
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_grouping', read_config_option('monitor_grouping'), $force)
|
|
],
|
|
'view' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'options' => ['options' => 'sanitize_search_string'],
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_view', read_config_option('monitor_view'), $force)
|
|
],
|
|
'rows' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'options' => ['options' => 'sanitize_search_string'],
|
|
'default' => read_user_setting('monitor_rows', read_config_option('num_rows_table'), $force)
|
|
],
|
|
'size' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'options' => ['options' => 'sanitize_search_string'],
|
|
'default' => read_user_setting('monitor_size', 'monitor_medium', $force)
|
|
],
|
|
'trim' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => read_user_setting('monitor_trim', read_config_option('monitor_trim'), $force)
|
|
],
|
|
'crit' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_crit', '-1', $force)
|
|
],
|
|
'status' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_status', '-1', $force)
|
|
],
|
|
'tree' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_tree', '-1', $force)
|
|
],
|
|
'site' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_site', '-1', $force)
|
|
],
|
|
'template' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'pageset' => true,
|
|
'default' => read_user_setting('monitor_template', '-1', $force)
|
|
],
|
|
'id' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '-1'
|
|
],
|
|
'page' => [
|
|
'filter' => FILTER_VALIDATE_INT,
|
|
'default' => '1'
|
|
],
|
|
'sort_column' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'default' => 'status',
|
|
'options' => ['options' => 'sanitize_search_string']
|
|
],
|
|
'sort_direction' => [
|
|
'filter' => FILTER_CALLBACK,
|
|
'default' => 'ASC',
|
|
'options' => ['options' => 'sanitize_search_string']
|
|
]
|
|
];
|
|
|
|
validate_store_request_vars($filters, 'sess_monitor');
|
|
// ================= input validation =================
|
|
}
|
|
|
|
/**
|
|
* Load host row and normalize status fields for AJAX tooltip rendering.
|
|
*
|
|
* @param int $id|string Host id.
|
|
* @param array $thold_hosts Threshold host map.
|
|
* @param array $config Global Cacti config.
|
|
*
|
|
* @return array
|
|
*/
|
|
function monitorLoadAjaxStatusHost(mixed $id, array $thold_hosts, array $config): array {
|
|
$host = db_fetch_row_prepared(
|
|
'SELECT *
|
|
FROM host
|
|
WHERE id = ?',
|
|
[$id]
|
|
);
|
|
|
|
if (!cacti_sizeof($host)) {
|
|
return [];
|
|
}
|
|
|
|
$host['anchor'] = $config['url_path'] . 'graph_view.php?action=preview&reset=1&host_id=' . $host['id'];
|
|
|
|
if ($host['status'] == 3 && array_key_exists($host['id'], $thold_hosts)) {
|
|
$host['status'] = 4;
|
|
$host['anchor'] = $config['url_path'] . 'plugins/thold/thold_graph.php?action=thold&reset=true&status=1&host_id=' . $host['id'];
|
|
}
|
|
|
|
if ($host['availability_method'] == 0) {
|
|
$host['status'] = 6;
|
|
}
|
|
|
|
$host['real_status'] = getHostStatus($host, true);
|
|
$host['status'] = getHostStatus($host);
|
|
|
|
return $host;
|
|
}
|
|
|
|
/**
|
|
* Build quick-action link markup for AJAX tooltip panel.
|
|
*
|
|
* @param array $host Host row.
|
|
* @param array $config Global Cacti config.
|
|
*
|
|
* @return string
|
|
*/
|
|
function monitorGetAjaxStatusLinks(array $host, array $config): string {
|
|
$links = '';
|
|
|
|
if (api_plugin_user_realm_auth('host.php')) {
|
|
$host_link = html_escape($config['url_path'] . 'host.php?action=edit&id=' . $host['id']);
|
|
$links .= '<div><a title="' . __('Edit Device', 'monitor') . '" class="pic hyperLink monitorLink" href="' . $host_link . '"><i class="fas fa-pen-square deviceUp monitorLinkIcon"></i></a></div>';
|
|
}
|
|
|
|
$graphs = db_fetch_cell_prepared(
|
|
'SELECT COUNT(*)
|
|
FROM graph_local
|
|
WHERE host_id = ?',
|
|
[$host['id']]
|
|
);
|
|
|
|
if ($graphs > 0) {
|
|
$graph_link = html_escape($config['url_path'] . 'graph_view.php?action=preview&reset=1&host_id=' . $host['id']);
|
|
$links .= '<div><a title="' . __esc('View Graphs', 'monitor') . '" class="pic hyperLink monitorLink" href="' . $graph_link . '"><i class="fa fa-chart-line deviceUp monitorLinkIcon"></i></a></div>';
|
|
}
|
|
|
|
if (api_plugin_is_enabled('thold')) {
|
|
$tholds = db_fetch_cell_prepared(
|
|
'SELECT count(*)
|
|
FROM thold_data
|
|
WHERE host_id = ?',
|
|
[$host['id']]
|
|
);
|
|
|
|
if ($tholds) {
|
|
$thold_link = html_escape($config['url_path'] . 'plugins/thold/thold_graph.php?action=thold&reset=true&status=1&host_id=' . $host['id']);
|
|
$links .= '<div><a title="' . __esc('View Thresholds/Alerts', 'monitor') . '" class="pic hyperLink monitorLink" href="' . $thold_link . '"><i class="fas fa-tasks deviceRecovering monitorLinkIcon"></i></a></div>';
|
|
}
|
|
}
|
|
|
|
if (api_plugin_is_enabled('syslog') && api_plugin_user_realm_auth('syslog.php')) {
|
|
require($config['base_path'] . '/plugins/syslog/config.php');
|
|
require_once($config['base_path'] . '/plugins/syslog/functions.php');
|
|
|
|
$syslog_logs = syslog_db_fetch_cell_prepared(
|
|
'SELECT count(*)
|
|
FROM syslog_logs
|
|
WHERE host = ?',
|
|
[$host['hostname']]
|
|
);
|
|
|
|
$syslog_host = syslog_db_fetch_cell_prepared(
|
|
'SELECT host_id
|
|
FROM syslog_hosts
|
|
WHERE host = ?',
|
|
[$host['hostname']]
|
|
);
|
|
|
|
if ($syslog_logs && $syslog_host) {
|
|
$syslog_log_link = html_escape($config['url_path'] . 'plugins/syslog/syslog/syslog.php?reset=1&tab=alerts&host_id=' . $syslog_host);
|
|
$links .= '<div><a title="' . __esc('View Device Syslog Alerts', 'monitor') . '" class="pic hyperLink monitorLink" href="' . $syslog_log_link . '"><i class="fas fa-life-ring deviceDown monitorLinkIcon"></i></a></div>';
|
|
}
|
|
|
|
if ($syslog_host) {
|
|
$syslog_link = html_escape($config['url_path'] . 'plugins/syslog/syslog/syslog.php?reset=1&tab=syslog&host_id=' . $syslog_host);
|
|
$links .= '<div><a title="' . __esc('View Device Syslog Entries', 'monitor') . '" class="pic hyperLink monitorLink" href="' . $syslog_link . '"><i class="fas fa-life-ring deviceUp monitorLinkIcon"></i></a></div>';
|
|
}
|
|
}
|
|
|
|
return $links;
|
|
}
|
|
|
|
/**
|
|
* Render full hover tooltip table HTML for one host.
|
|
*
|
|
* @param array $host Host row.
|
|
* @param string $size Tooltip CSS size key.
|
|
* @param string $links Action links HTML.
|
|
* @param string $site Site display value.
|
|
* @param string $sdisplay Status display string.
|
|
* @param string $iclass Status CSS class.
|
|
* @param array $criticalities Criticality label map.
|
|
*
|
|
* @return string
|
|
*/
|
|
function monitorRenderAjaxStatusTooltip(array $host, string $size, string $links, string $site, string $sdisplay, string $iclass, array $criticalities): string {
|
|
return "<table class='monitorHover $size'>
|
|
<tr class='tableHeader'>
|
|
<th class='left' colspan='2'>" . __('Device Status Information', 'monitor') . '</th>
|
|
</tr>
|
|
<tr>
|
|
<td>' . __('Device:', 'monitor') . "</td>
|
|
<td><a class='pic hyperLink monitorLink' href='" . html_escape($host['anchor']) . "'>" . html_escape($host['description']) . '</a></td>
|
|
<tr>
|
|
<td>' . __('Site:', 'monitor') . '</td>
|
|
<td>' . html_escape($site) . '</td>
|
|
</tr>
|
|
<tr>
|
|
<td>' . __('Location:', 'monitor') . '</td>
|
|
<td>' . html_escape($host['location']) . '</td>
|
|
</tr>' . (isset($host['monitor_criticality']) && $host['monitor_criticality'] > 0 ? '
|
|
<tr>
|
|
<td>' . __('Criticality:', 'monitor') . '</td>
|
|
<td>' . html_escape($criticalities[$host['monitor_criticality']]) . '</td>
|
|
</tr>' : '') . '
|
|
<tr>
|
|
<td>' . __('Status:', 'monitor') . "</td>
|
|
<td class='$iclass'>$sdisplay</td>
|
|
</tr>" . ($host['status'] < 3 || $host['status'] == 5 ? '
|
|
<tr>
|
|
<td>' . __('Admin Note:', 'monitor') . "</td>
|
|
<td class='$iclass'>" . html_escape($host['monitor_text']) . '</td>
|
|
</tr>' : '') . ($host['availability_method'] > 0 ? '
|
|
<tr>
|
|
<td>' . __('IP/Hostname:', 'monitor') . '</td>
|
|
<td>' . html_escape($host['hostname']) . '</td>
|
|
</tr>' : '') . ($host['availability_method'] > 0 ? "
|
|
<tr>
|
|
<td class='nowrap'>" . __('Curr/Avg:', 'monitor') . '</td>
|
|
<td>' . __('%d ms', $host['cur_time'], 'monitor') . ' / ' . __('%d ms', $host['avg_time'], 'monitor') . '</td>
|
|
</tr>' : '') . (isset($host['monitor_warn']) && ($host['monitor_warn'] > 0 || $host['monitor_alert'] > 0) ? "
|
|
<tr>
|
|
<td class='nowrap'>" . __('Warn/Alert:', 'monitor') . '</td>
|
|
<td>' . __('%0.2d ms', $host['monitor_warn'], 'monitor') . ' / ' . __('%0.2d ms', $host['monitor_alert'], 'monitor') . '</td>
|
|
</tr>' : '') . '
|
|
<tr>
|
|
<td>' . __('Last Fail:', 'monitor') . '</td>
|
|
<td>' . html_escape($host['status_fail_date']) . '</td>
|
|
</tr>
|
|
<tr>
|
|
<td>' . __('Time In State:', 'monitor') . '</td>
|
|
<td>' . get_timeinstate($host) . '</td>
|
|
</tr>
|
|
<tr>
|
|
<td>' . __('Availability:', 'monitor') . '</td>
|
|
<td>' . round((float) $host['availability'], 2) . ' %</td>
|
|
</tr>' . ($host['snmp_version'] > 0 && ($host['status'] == 3 || $host['status'] == 2) ? '
|
|
<tr>
|
|
<td>' . __('Agent Uptime:', 'monitor') . '</td>
|
|
<td>' . ($host['status'] == 3 || $host['status'] == 5 ? monitorPrintHostTime($host['snmp_sysUpTimeInstance']) : __('N/A', 'monitor')) . "</td>
|
|
</tr>
|
|
<tr>
|
|
<td class='nowrap'>" . __('Sys Description:', 'monitor') . '</td>
|
|
<td>' . html_escape(monitorTrim($host['snmp_sysDescr'])) . '</td>
|
|
</tr>
|
|
<tr>
|
|
<td>' . __('Location:', 'monitor') . '</td>
|
|
<td>' . html_escape(monitorTrim($host['snmp_sysLocation'])) . '</td>
|
|
</tr>
|
|
<tr>
|
|
<td>' . __('Contact:', 'monitor') . '</td>
|
|
<td>' . html_escape(monitorTrim($host['snmp_sysContact'])) . '</td>
|
|
</tr>' : '') . ($host['notes'] != '' ? '
|
|
<tr>
|
|
<td>' . __('Notes:', 'monitor') . '</td>
|
|
<td>' . html_escape($host['notes']) . '</td>
|
|
</tr>' : '') . "
|
|
<tr><td colspan='2' style='width:100%'><hr></td></tr>
|
|
<tr><td colspan='2' style='width:100%'><div style='display:flex;justify-content:space-around;'>$links</div></td></tr>
|
|
</table>";
|
|
}
|
|
|
|
/**
|
|
* Handle AJAX monitor status tooltip request and print response HTML.
|
|
*
|
|
* @return bool|null
|
|
*/
|
|
function ajaxStatus(): void {
|
|
global $thold_hosts, $config, $iclasses, $criticalities;
|
|
|
|
validateRequestVars();
|
|
|
|
if (!isset_request_var('id') || !get_filter_request_var('id')) {
|
|
return;
|
|
}
|
|
|
|
$id = get_request_var('id');
|
|
$size = get_request_var('size');
|
|
$host = monitorLoadAjaxStatusHost($id, $thold_hosts, $config);
|
|
|
|
if (!cacti_sizeof($host)) {
|
|
cacti_log('Attempted to retrieve status for missing Device ' . $id, false, 'MONITOR', POLLER_VERBOSITY_HIGH);
|
|
|
|
return;
|
|
}
|
|
|
|
$links = monitorGetAjaxStatusLinks($host, $config);
|
|
|
|
if (strtotime($host['status_fail_date']) < 86400) {
|
|
$host['status_fail_date'] = __('Never', 'monitor');
|
|
}
|
|
|
|
if ($host['location'] == '') {
|
|
$host['location'] = __('Unspecified', 'monitor');
|
|
}
|
|
|
|
$iclass = $iclasses[$host['status']];
|
|
$sdisplay = getHostStatusDescription($host['real_status']);
|
|
$site = db_fetch_cell_prepared('SELECT name FROM sites WHERE id = ?', [$host['site_id']]);
|
|
|
|
if ($site == '') {
|
|
$site = __('None', 'monitor');
|
|
}
|
|
|
|
print monitorRenderAjaxStatusTooltip($host, $size, $links, $site, $sdisplay, $iclass, $criticalities);
|
|
}
|