Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ ChangeLog

--- develop ---
* feature: Add a Fleet (Summary) Dashboard reached by a dashboard glyph on the Summary tab's Device Type rows: a draggable, add/remove card grid scoped to all devices or one OS type, with min/avg/max bullet charts (CPU/memory/swap/processes/uptime), a device-status stacked bar, OS type distribution, storage utilization, fleet stat tiles, and top processes by CPU and by memory; scope, card order, expanded state and refresh interval persist per user
* issue: Make the per-device Dashboard device picker an AJAX callback (a Cacti drop_callback that searches Host MIB devices server-side via ?action=ajax_hosts) instead of rendering every host as an <option>, so installs with tens of thousands of devices no longer bloat the page
* issue: Stop the per-device Dashboard card header from resizing when hovering the drag handle or header tool buttons (reserve a transparent 1px border with box-sizing and give the header a min-height)
* issue: Fix 'Uncaught TypeError: runCollector(): Argument #2 ($lastrun) must be of type float, null given' in the poller when a collector's *_lastrun/*_freq setting has never been written (e.g. a task's first run); runCollector() now accepts null and normalizes it
* issue: Fix the per-device Dashboard Storage, Per-Processor Load, Hardware and Installed Software cards rendering empty because they filtered on 'present = 1' while the underlying Storage/Hardware/Inventory tabs never did, and add a Process Use History card
* feature: Add a per-device Dashboard that drills into a single host with a draggable, add/remove card grid (CPU/memory/swap gauges, per-processor load, storage, top processes, hardware, installed software, logins and system information); card order, expanded state, the selected device and the auto-refresh interval persist per user, and a dashboard glyph on the Devices tab opens a stateful Dashboard tab that remembers the last host opened
Expand Down
12 changes: 10 additions & 2 deletions css/hmib.css
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,9 @@
display: flex;
align-items: center;
gap: 8px;
box-sizing: border-box;
padding: 7px 10px;
min-height: 34px;
Comment thread
TheWitness marked this conversation as resolved.
border-bottom: 1px solid color-mix(in srgb, currentColor 18%, transparent);
background: color-mix(in srgb, currentColor 8%, transparent);
}
Expand All @@ -203,8 +205,10 @@

.hmibDashCardDrag {
flex: 0 0 auto;
box-sizing: border-box;
padding: 2px 5px;
border: 0;
/* reserve a transparent border so an inherited button:hover border cannot reflow the header */
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: color-mix(in srgb, currentColor 50%, transparent);
Expand All @@ -216,6 +220,7 @@
.hmibDashCardDrag:hover,
.hmibDashCardDrag:focus-visible {
color: currentColor;
border-color: transparent;
background: color-mix(in srgb, currentColor 12%, transparent);
outline: none;
}
Expand Down Expand Up @@ -244,8 +249,10 @@
}

.hmibDashCardTool {
box-sizing: border-box;
padding: 2px 5px;
border: 0;
/* reserve a transparent border so an inherited button:hover border cannot reflow the header */
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: color-mix(in srgb, currentColor 50%, transparent);
Expand All @@ -257,6 +264,7 @@
.hmibDashCardTool:hover,
.hmibDashCardTool:focus-visible {
color: currentColor;
border-color: transparent;
background: color-mix(in srgb, currentColor 12%, transparent);
outline: none;
}
Expand Down
50 changes: 35 additions & 15 deletions hmib.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
set_default_action('summary');

if (get_request_var('action') == 'ajax_hosts') {
get_allowed_ajax_hosts(true, false, 'h.id IN (SELECT host_id FROM plugin_hmib_hrSystem)');
// Serves the dashboard's device drop_callback; no 'Any'/'None' entries since a
// dashboard always targets one real Host MIB device.
get_allowed_ajax_hosts(false, false, 'h.id IN (SELECT host_id FROM plugin_hmib_hrSystem)');
exit;
}

Expand Down Expand Up @@ -4721,23 +4723,41 @@ function hmib_dashboard(): void {
$page_refresh_interval = [0 => __('No Refresh', 'hmib'), 30 => __('30 Seconds', 'hmib'), 60 => __('1 Minute', 'hmib'), 300 => __('5 Minutes', 'hmib')];
}

print '<div class="hmibDashToolbar">';
print '<label class="hmibDashToolbarLabel" for="hmib_dashboard_host">' . __esc('Device', 'hmib') . '</label>';
print '<select id="hmib_dashboard_host" class="hmibDashHost">';
// Device picker: a Cacti drop_callback (select2 AJAX) that searches Host MIB
// devices server-side via ?action=ajax_hosts, so installs with tens of
// thousands of hosts never materialize the full option list in the DOM.
// select2 ignores the 'sql' and invokes 'on_change' (data-callback) by name
// on selection; the 'sql' only feeds the plain-<select> fallback used when
// the admin has set autocomplete > 0, so it must list every Host MIB device
// there (that mode already renders full option lists site-wide, so this does
// not regress the AJAX scaling path).
form_start('hmib.php', 'hmib_dashboard_device_form');

html_start_box('', '100%', '', '3', 'center', '');

draw_edit_form([
'config' => ['no_form_tag' => true],
'fields' => [
'hmib_dashboard_host' => [
'method' => 'drop_callback',
'action' => 'ajax_hosts',
'id' => $host_id,
Comment thread
TheWitness marked this conversation as resolved.
'sql' => 'SELECT h.id, h.description AS name FROM host AS h WHERE h.id IN (SELECT host_id FROM plugin_hmib_hrSystem) ORDER BY h.description',
'on_change' => 'hmibDashboardNavigate()',
'friendly_name' => __('Device', 'hmib'),
'description' => __('Search Host MIB devices as you type to open that device\'s dashboard.', 'hmib'),
'value' => $host_id,
'size' => 40,
'max_length' => 64
]
]
]);

$hosts = db_fetch_assoc("SELECT host.id, host.description
FROM plugin_hmib_hrSystem AS hrs
INNER JOIN host ON host.id = hrs.host_id
WHERE host.description != ''
ORDER BY host.description");
html_end_box(false);

if (cacti_sizeof($hosts)) {
foreach ($hosts as $host) {
print '<option value="' . (int) $host['id'] . '"' . ((int) $host['id'] === $host_id ? ' selected' : '') . '>' . html_escape($host['description']) . '</option>';
}
}
form_end();

print '</select>';
print '<div class="hmibDashToolbar">';

print '<label class="hmibDashToolbarLabel" for="hmib_dashboard_add">' . __esc('Add card', 'hmib') . '</label>';
print '<select id="hmib_dashboard_add" class="hmibDashAdd"><option value="">' . __esc('Add a card…', 'hmib') . '</option>';
Expand Down
20 changes: 17 additions & 3 deletions js/hmib_dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@

var hmibDashDragging = null;

/* Named navigation callback for the device picker. The select2 drop_callback
* invokes this by name (via the field's on_change/data-callback) on selection
* instead of emitting a change event, so it reads the picker value itself and
* opens that device's dashboard. Must stay global for executeFunctionByName(). */
function hmibDashboardNavigate() {
var host = document.getElementById('hmib_dashboard_host');

if (host && host.value) {
window.location = 'hmib.php?action=dashboard&device=' + encodeURIComponent(host.value);
}
}

function initHmibDashboard() {
$(function() {
var grid = document.getElementById('hmib_dashboard');
Expand Down Expand Up @@ -87,10 +99,12 @@ function initHmibDashboard() {

var host = document.getElementById('hmib_dashboard_host');
if (host) {
// Native change covers the plain-<select> autocomplete fallback; the
// select2 AJAX control instead invokes the named hmibDashboardNavigate()
// callback (wired via the field's on_change/data-callback), which does
// not emit a change event on older Cacti, so both paths are handled.
$(host).off('change.hmibdash').on('change.hmibdash', function() {
if (this.value) {
window.location = 'hmib.php?action=dashboard&device=' + encodeURIComponent(this.value);
}
hmibDashboardNavigate();
});
}

Expand Down
4 changes: 4 additions & 0 deletions locales/po/cacti.pot
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,10 @@ msgstr ""
msgid "No Refresh"
msgstr ""

#: hmib.php
msgid "Search Host MIB devices as you type to open that device's dashboard."
msgstr ""

#: hmib.php
msgid "Add card"
msgstr ""
Expand Down
Loading