This is an automated email from the ASF dual-hosted git repository.
DomGarguilo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/main by this push:
new 68a1e53cdf Add overview matrix to monitor home page (#6361)
68a1e53cdf is described below
commit 68a1e53cdf8693e847c1e95ca1b0594970e2b5d4
Author: Dom G. <[email protected]>
AuthorDate: Fri May 8 15:23:22 2026 -0400
Add overview matrix to monitor home page (#6361)
* Add overview matrix to monitor home page
* Error on all servers dead
* Remove colors from total cells
* Remove title from deployment table
* Remove regex
---
.../accumulo/monitor/next/SystemInformation.java | 12 +-
.../accumulo/monitor/resources/css/screen.css | 79 ++++++
.../apache/accumulo/monitor/resources/js/navbar.js | 61 ++++-
.../accumulo/monitor/resources/js/overview.js | 279 +++++++++++----------
.../apache/accumulo/monitor/templates/navbar.ftl | 10 +-
.../apache/accumulo/monitor/templates/overview.ftl | 40 +--
6 files changed, 291 insertions(+), 190 deletions(-)
diff --git
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/SystemInformation.java
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/SystemInformation.java
index 92eed0e75e..b45bad0f3c 100644
---
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/SystemInformation.java
+++
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/SystemInformation.java
@@ -853,8 +853,16 @@ public class SystemInformation {
int missingMetricCount = (int) servers.stream()
.filter(serverId ->
!TableDataFactory.hasMetricData(allMetrics.getIfPresent(serverId)))
.count();
- return Status.buildStatus(servers.size(), problemHostCount,
missingMetricCount,
- type == ServerId.Type.TABLET_SERVER);
+ return Status.buildStatus(servers.size(), problemHostCount,
missingMetricCount, switch (type) {
+ case MANAGER:
+ case GARBAGE_COLLECTOR:
+ case COMPACTOR:
+ case SCAN_SERVER:
+ case TABLET_SERVER:
+ yield true;
+ case MONITOR:
+ yield false;
+ });
}
private String computeManagerGoalState() {
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/css/screen.css
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/css/screen.css
index 8a804cb566..129f103f79 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/css/screen.css
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/css/screen.css
@@ -252,6 +252,85 @@ pre.logevent {
color: #5cb85c;
}
+.server-count {
+ color: #6c757d;
+ font-size: 0.875em;
+ font-variant-numeric: tabular-nums;
+}
+
+.deployment-overview-content {
+ width: fit-content;
+ max-width: 100%;
+ margin: 0 auto;
+}
+
+.deployment-matrix-table {
+ margin: 0 auto;
+ table-layout: auto;
+ width: auto;
+}
+
+.deployment-matrix-table th,
+.deployment-matrix-table td {
+ text-align: center;
+ vertical-align: middle;
+}
+
+.deployment-matrix-group {
+ min-width: 10rem;
+ text-align: left !important;
+ white-space: nowrap;
+ font-weight: 400;
+}
+
+.deployment-matrix-cell {
+ min-width: 5.25rem;
+}
+
+.deployment-matrix-total {
+ font-weight: 600;
+}
+
+.deployment-matrix-header {
+ font-weight: 600;
+ white-space: normal;
+ line-height: 1.15;
+ background-color: #f8f9fa;
+}
+
+.deployment-matrix-table tfoot th,
+.deployment-matrix-table tfoot td {
+ font-weight: 600;
+}
+
+.deployment-count-badge {
+ min-width: 2.35rem;
+ padding: 0.3em 0.55em;
+ color: #333333;
+ font-variant-numeric: tabular-nums;
+}
+
+.deployment-count-total {
+ background-color: #f5f5f5;
+ border: 1px solid #d9d9d9;
+}
+
+.deployment-count-ok {
+ background-color: #5cb85c;
+}
+
+.deployment-count-warn {
+ background-color: #f0ad4e;
+}
+
+.deployment-count-error {
+ background-color: #d9534f;
+}
+
+.deployment-count-empty {
+ background-color: #f5f5f5;
+}
+
.highlight {
background-color: #cef4b5;
}
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/navbar.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/navbar.js
index 0ce1125bea..7b6dfce77f 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/navbar.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/navbar.js
@@ -36,6 +36,28 @@ const CLASS = {
ERROR: 'error'
};
+const NAVBAR_COMPONENTS = [{
+ statusKey: 'MANAGER',
+ indicatorId: 'managerStatusNotification',
+ countId: 'managerStatusCount'
+}, {
+ statusKey: 'TABLET_SERVER',
+ indicatorId: 'serverStatusNotification',
+ countId: 'serverStatusCount'
+}, {
+ statusKey: 'GARBAGE_COLLECTOR',
+ indicatorId: 'gcStatusNotification',
+ countId: 'gcStatusCount'
+}, {
+ statusKey: 'SCAN_SERVER',
+ indicatorId: 'sserverStatusNotification',
+ countId: 'sserverStatusCount'
+}, {
+ statusKey: 'COMPACTOR',
+ indicatorId: 'compactorStatusNotification',
+ countId: 'compactorStatusCount'
+}];
+
/**
* Remove other bootstrap color classes and add the given class to the given
element
* @param {string} elementId the element id to update
@@ -59,6 +81,21 @@ function updateElementStatus(elementId, status) {
}
}
+function updateServerCount(elementId, status) {
+ const $element = $(`#${elementId}`);
+
+ if (!status || Number(status.serverCount) <= 0) {
+ $element.text('0/0');
+ return;
+ }
+
+ const total = Number(status.serverCount);
+ const problem = Number(status.problemServerCount || 0);
+ const responding = Math.max(0, total - problem);
+
+ $element.text(`${responding}/${total}`);
+}
+
/**
* Updates the notifications of the servers dropdown notification as well as
the individual server notifications.
* @param {JSON} statusData object containing the status info for the servers
@@ -67,14 +104,13 @@ function updateServerNotifications(statusData) {
const managerGoalState = statusData.managerGoalState;
const isSafeMode = managerGoalState === 'SAFE_MODE';
const isCleanStop = managerGoalState === 'CLEAN_STOP';
- const componentStatuses = [
- getComponentStatus(statusData, 'MANAGER'),
- getComponentStatus(statusData, 'TABLET_SERVER'),
- getComponentStatus(statusData, 'GARBAGE_COLLECTOR'),
- getComponentStatus(statusData, 'SCAN_SERVER'),
- getComponentStatus(statusData, 'COMPACTOR')
- ];
+ const componentStatuses = NAVBAR_COMPONENTS.map(function (component) {
+ return getComponentStatus(statusData, component.statusKey);
+ });
const managerStatus = componentStatuses[0];
+ const componentData = NAVBAR_COMPONENTS.map(function (component) {
+ return statusData.componentStatuses?.[component.statusKey] || null;
+ });
// setting manager status notification
if (managerStatus === STATUS.ERROR || isCleanStop) {
@@ -88,10 +124,13 @@ function updateServerNotifications(statusData) {
'. Could not properly set manager status notification.');
}
- updateElementStatus('serverStatusNotification', componentStatuses[1]);
- updateElementStatus('gcStatusNotification', componentStatuses[2]);
- updateElementStatus('sserverStatusNotification', componentStatuses[3]);
- updateElementStatus('compactorStatusNotification', componentStatuses[4]);
+ NAVBAR_COMPONENTS.forEach(function (component, index) {
+ updateServerCount(component.countId, componentData[index]);
+ if (index === 0) {
+ return;
+ }
+ updateElementStatus(component.indicatorId, componentStatuses[index]);
+ });
// Setting overall servers status notification
if (!isSafeMode && !isCleanStop && componentStatuses.every(status => status
=== STATUS.OK)) {
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/overview.js
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/overview.js
index c35a963502..27bf0a2ebb 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/overview.js
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/overview.js
@@ -18,18 +18,7 @@
*/
"use strict";
-var deploymentSummaryTable;
-var deploymentBreakdownTable;
-const SERVER_TYPE_LINKS = new Map([
- ['Manager', 'manager'],
- ['Garbage Collector', 'gc'],
- ['Compactor', 'compactors'],
- ['Scan Server', 'sservers'],
- ['Tablet Server', 'tservers']
-]);
-const SUMMARY_SERVER_TYPES = ['Manager', 'Garbage Collector', 'Compactor',
'Scan Server',
- 'Tablet Server'
-];
+var deploymentBreakdown = [];
/**
* Creates overview initial table
@@ -37,104 +26,6 @@ const SUMMARY_SERVER_TYPES = ['Manager', 'Garbage
Collector', 'Compactor', 'Scan
$(function () {
// display datatables errors in the console instead of in alerts
$.fn.dataTable.ext.errMode = 'throw';
-
- deploymentSummaryTable = $('#deploymentSummaryTable').DataTable({
- "autoWidth": false,
- "paging": false,
- "searching": false,
- "info": false,
- "ordering": false,
- "dom": 't',
- "data": [],
- "columnDefs": [{
- "targets": 0,
- "width": "60%"
- },
- {
- "targets": 1,
- "width": "40%"
- }
- ],
- "columns": [{
- "data": "serverType",
- "render": function (data, type) {
- if (type !== 'display') {
- return data;
- }
-
- var link = SERVER_TYPE_LINKS.get(data);
- if (link === undefined) {
- return data;
- }
-
- return '<a class="link-body-emphasis" href="' + sanitize(link) +
'">' + sanitize(data) + '</a>';
- }
- },
- {
- "data": null,
- "render": function (data, type, row) {
- return row.responding + ' / ' + row.total;
- }
- }
- ]
- });
-
- deploymentBreakdownTable = $('#deploymentBreakdownTable').DataTable({
- "autoWidth": false,
- "paging": true,
- "pageLength": 10,
- "searching": true,
- "info": true,
- "order": [
- [0, 'asc'],
- [1, 'asc']
- ],
- "dom": 't<"row"<"col-sm-3"l><"col-sm-5 text-center"i><"col-sm-4
text-right"p>>',
- "data": [],
- "columnDefs": [{
- "targets": 0,
- "width": "30%"
- },
- {
- "targets": 1,
- "width": "40%"
- },
- {
- "targets": 2,
- "width": "30%"
- }
- ],
- "columns": [{
- "data": "resourceGroup",
- "name": "resourceGroup"
- },
- {
- "data": "serverType"
- },
- {
- "data": null,
- "render": function (data, type, row) {
- return row.responding + ' / ' + row.total;
- }
- }
- ]
- });
-
- $('#deployment-rg-filter').on('keyup', function () {
- var input = this.value;
- if (isValidRegex(input) || input === '') {
- $('#deployment-rg-feedback').hide();
- $(this).removeClass('is-invalid');
- deploymentBreakdownTable
- .column('resourceGroup:name')
- .search(input, true, false)
- .draw();
- } else {
- $('#deployment-rg-feedback').show();
- $(this).addClass('is-invalid');
- }
- });
-
refreshOverview();
});
@@ -159,7 +50,7 @@ function refreshDeploymentTables() {
getDeployment().then(function () {
var data = JSON.parse(sessionStorage.deployment);
var breakdown = Array.isArray(data.breakdown) ? data.breakdown : [];
- var summary = buildDeploymentSummary(breakdown);
+ deploymentBreakdown = breakdown;
if (breakdown.length === 0) {
$('#deploymentWarning').html('<div class="alert alert-warning"
role="alert">' +
@@ -168,39 +59,159 @@ function refreshDeploymentTables() {
$('#deploymentWarning').empty();
}
- deploymentSummaryTable.clear().rows.add(summary).draw();
- deploymentBreakdownTable.clear().rows.add(breakdown).draw();
+ renderDeploymentMatrix(breakdown);
});
}
-function buildDeploymentSummary(breakdown) {
+function renderDeploymentMatrix(breakdown) {
+ var matrixData = buildDeploymentMatrix(breakdown);
+ var $container = $('#deploymentBreakdownMatrix');
+
if (breakdown.length === 0) {
- return [];
+ $container.empty();
+ return;
}
- var totalsByType = new Map();
+ if (matrixData.filteredResourceGroups.length === 0) {
+ $container.html(buildDeploymentEmptyState(resourceGroupFilter));
+ return;
+ }
- SUMMARY_SERVER_TYPES.forEach(function (serverType) {
- totalsByType.set(serverType, {
- serverType: serverType,
- total: 0,
- responding: 0
- });
- });
+ $container.html(buildDeploymentMatrixTable(matrixData));
+}
+
+function buildDeploymentMatrix(breakdown) {
+ var serverTypes = [];
+ var serverTypeSet = new Set();
+ var resourceGroups = new Map();
breakdown.forEach(function (row) {
- var existing = totalsByType.get(row.serverType);
- if (existing === undefined) {
- totalsByType.set(row.serverType, {
- serverType: row.serverType,
- total: row.total,
- responding: row.responding
- });
- } else {
- existing.total += row.total;
- existing.responding += row.responding;
+ if (serverTypeSet.has(row.serverType) === false) {
+ serverTypeSet.add(row.serverType);
+ serverTypes.push(row.serverType);
}
+
+ if (resourceGroups.has(row.resourceGroup) === false) {
+ resourceGroups.set(row.resourceGroup, new Map());
+ }
+
+ resourceGroups.get(row.resourceGroup).set(row.serverType, {
+ responding: Number(row.responding),
+ total: Number(row.total)
+ });
+ });
+
+ var sortedResourceGroups = Array.from(resourceGroups.keys()).sort();
+
+ return {
+ serverTypes: serverTypes,
+ resourceGroups: resourceGroups,
+ filteredResourceGroups: sortedResourceGroups
+ };
+}
+
+function buildDeploymentMatrixTable(matrixData) {
+ var headerCells = ['<th class="deployment-matrix-group
deployment-matrix-header">Resource Group</th>'];
+ var totalByServerType = new Map();
+ var grandTotals = {
+ responding: 0,
+ total: 0
+ };
+
+ matrixData.serverTypes.forEach(function (serverType) {
+ headerCells.push('<th class="deployment-matrix-cell
deployment-matrix-header">' +
+ sanitize(serverType) + '</th>');
+ totalByServerType.set(serverType, {
+ responding: 0,
+ total: 0
+ });
+ });
+ headerCells.push('<th class="deployment-matrix-cell
deployment-matrix-header">Total</th>');
+
+ var rowsHtml = matrixData.filteredResourceGroups.map(function
(resourceGroup) {
+ var cells = ['<th scope="row" class="deployment-matrix-group">' +
sanitize(resourceGroup) + '</th>'];
+ var rowTotals = {
+ responding: 0,
+ total: 0
+ };
+ var rowData = matrixData.resourceGroups.get(resourceGroup);
+
+ matrixData.serverTypes.forEach(function (serverType) {
+ var counts = rowData.get(serverType) || {
+ responding: 0,
+ total: 0
+ };
+ var totals = totalByServerType.get(serverType);
+
+ totals.responding += counts.responding;
+ totals.total += counts.total;
+ rowTotals.responding += counts.responding;
+ rowTotals.total += counts.total;
+
+ cells.push('<td class="deployment-matrix-cell">' +
buildDeploymentCell(counts) + '</td>');
+ });
+
+ grandTotals.responding += rowTotals.responding;
+ grandTotals.total += rowTotals.total;
+ cells.push('<td class="deployment-matrix-cell deployment-matrix-total">' +
+ buildDeploymentTotalCell(rowTotals) + '</td>');
+
+ return '<tr>' + cells.join('') + '</tr>';
+ }).join('');
+
+ var footerCells = ['<th scope="row" class="deployment-matrix-group
deployment-matrix-total">Total</th>'];
+
+ matrixData.serverTypes.forEach(function (serverType) {
+ footerCells.push('<td class="deployment-matrix-cell
deployment-matrix-total">' +
+ buildDeploymentTotalCell(totalByServerType.get(serverType)) + '</td>');
});
+ footerCells.push('<td class="deployment-matrix-cell
deployment-matrix-total">' +
+ buildDeploymentTotalCell(grandTotals) + '</td>');
+
+ return '<div class="table-responsive">' +
+ '<table class="table table-bordered table-sm align-middle
deployment-matrix-table mb-0">' +
+ '<thead><tr>' + headerCells.join('') +
+ '</tr></thead><tbody>' + rowsHtml + '</tbody><tfoot><tr>' +
footerCells.join('') +
+ '</tr></tfoot></table></div>';
+}
+
+/**
+ * Builds the HTML for a badge containing the counts of responding vs total
servers
+ */
+function buildDeploymentCell(counts, neutral) {
+ var badgeClass = getDeploymentBadgeClass(counts.responding, counts.total);
+ var label = counts.responding + '/' + counts.total;
+
+ return '<span class="badge rounded-pill deployment-count-badge ' +
badgeClass + '">' +
+ sanitize(label) + '</span>';
+}
+/**
+ * Builds the HTML for a badge containing the total counts of responding vs
total servers
+ */
+function buildDeploymentTotalCell(counts) {
+ var label = counts.responding + '/' + counts.total;
+
+ return '<span class="badge rounded-pill deployment-count-badge
deployment-count-total">' +
+ sanitize(label) + '</span>';
+}
+
+function getDeploymentBadgeClass(responding, total) {
+ if (total === 0) {
+ return 'deployment-count-empty';
+ }
+
+ if (responding === 0) {
+ return 'deployment-count-error';
+ }
+
+ if (responding === total) {
+ return 'deployment-count-ok';
+ }
+
+ return 'deployment-count-warn';
+}
- return Array.from(totalsByType.values());
+function buildDeploymentEmptyState() {
+ return '<div class="alert alert-secondary mb-0" role="alert">' +
+ 'No deployment breakdown data is currently available.</div>';
}
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl
index 6f07e4f8a4..d29518c380 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/navbar.ftl
@@ -39,11 +39,11 @@
<span id="statusNotification" class="icon-dot
normal"></span> Servers
</a>
<ul class="dropdown-menu">
- <li><a class="link-body-emphasis dropdown-item"
href="manager"><span id="managerStatusNotification" class="icon-dot
normal"></span> Manager Server </a></li>
- <li><a class="link-body-emphasis dropdown-item"
href="tservers"><span id="serverStatusNotification" class="icon-dot
normal"></span> Tablet Servers </a></li>
- <li><a class="link-body-emphasis dropdown-item"
href="sservers"><span id="sserverStatusNotification" class="icon-dot
normal"></span> Scan Servers </a></li>
- <li><a class="link-body-emphasis dropdown-item"
href="compactors"><span id="compactorStatusNotification" class="icon-dot
normal"></span> Compactors</a></li>
- <li><a class="link-body-emphasis dropdown-item"
href="gc"><span id="gcStatusNotification" class="icon-dot
normal"></span> Garbage collector </a></li>
+ <li><a class="link-body-emphasis dropdown-item d-flex
justify-content-between align-items-center gap-3" href="manager"><span><span
id="managerStatusNotification" class="icon-dot
normal"></span> Manager Server</span><span id="managerStatusCount"
class="server-count"></span></a></li>
+ <li><a class="link-body-emphasis dropdown-item d-flex
justify-content-between align-items-center gap-3" href="tservers"><span><span
id="serverStatusNotification" class="icon-dot
normal"></span> Tablet Servers</span><span id="serverStatusCount"
class="server-count"></span></a></li>
+ <li><a class="link-body-emphasis dropdown-item d-flex
justify-content-between align-items-center gap-3" href="sservers"><span><span
id="sserverStatusNotification" class="icon-dot
normal"></span> Scan Servers</span><span id="sserverStatusCount"
class="server-count"></span></a></li>
+ <li><a class="link-body-emphasis dropdown-item d-flex
justify-content-between align-items-center gap-3" href="compactors"><span><span
id="compactorStatusNotification" class="icon-dot
normal"></span> Compactors</span><span id="compactorStatusCount"
class="server-count"></span></a></li>
+ <li><a class="link-body-emphasis dropdown-item d-flex
justify-content-between align-items-center gap-3" href="gc"><span><span
id="gcStatusNotification" class="icon-dot
normal"></span> Garbage collector</span><span id="gcStatusCount"
class="server-count"></span></a></li>
</ul>
</li>
<li>
diff --git
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/overview.ftl
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/overview.ftl
index 72875d4952..42031ab289 100644
---
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/overview.ftl
+++
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/templates/overview.ftl
@@ -26,44 +26,8 @@
<div class="row d-flex justify-content-center">
<div class="col-xs-12" id="deploymentOverview">
<div id="deploymentWarning"></div>
- <div class="mb-4" style="max-width: 560px; margin: 0 auto;">
- <table id="deploymentSummaryTable"
- class="table table-bordered table-striped table-condensed"
style="width: 100%;">
- <thead>
- <tr>
- <th colspan="2" class="center">Server Type Summary</th>
- </tr>
- <tr>
- <th>Server Type</th>
- <th>Responding / Total</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
-
- <div style="max-width: 760px; margin: 0 auto;">
- <div class="mb-3">
- <label for="deployment-rg-filter" class="form-label">Resource
Group Filter</label>
- <input type="text" id="deployment-rg-filter" class="form-control"
- placeholder="Enter resource group regex">
- <small id="deployment-rg-feedback" class="form-text text-danger"
- style="display:none;">Invalid regex pattern</small>
- </div>
- <table id="deploymentBreakdownTable"
- class="table table-bordered table-striped table-condensed"
style="width: 100%;">
- <thead>
- <tr>
- <th colspan="3" class="center">Deployment Breakdown</th>
- </tr>
- <tr>
- <th>Resource Group</th>
- <th>Server Type</th>
- <th>Responding / Total</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
+ <div class="deployment-overview-content">
+ <div id="deploymentBreakdownMatrix"></div>
</div>
</div>
</div>