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 8f252bbdad Migrate Scan Server monitor page to use backend-derived DTO 
(#6165)
8f252bbdad is described below

commit 8f252bbdada829c6a486138ab59268ab17d2c7a6
Author: Dom G. <[email protected]>
AuthorDate: Wed Mar 4 12:24:53 2026 -0500

    Migrate Scan Server monitor page to use backend-derived DTO (#6165)
    
    * replaces the complex javascript parsing of raw metrics with a 
backend-built Data Transfer Object (DTO) which is consumed by the ScanServer 
monitor page UI.
---
 .../apache/accumulo/monitor/next/Endpoints.java    |  15 +-
 .../accumulo/monitor/next/SystemInformation.java   |  15 ++
 .../monitor/next/sservers/ScanServerView.java      | 183 ++++++++++++++++
 .../accumulo/monitor/resources/js/functions.js     |  17 +-
 .../apache/accumulo/monitor/resources/js/navbar.js |  31 +--
 .../accumulo/monitor/resources/js/sservers.js      | 236 ++++-----------------
 6 files changed, 264 insertions(+), 233 deletions(-)

diff --git 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/Endpoints.java 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/Endpoints.java
index 8eeecd71c3..4785dabcf0 100644
--- 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/Endpoints.java
+++ 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/Endpoints.java
@@ -60,6 +60,7 @@ import org.apache.accumulo.monitor.next.ec.CompactorsSummary;
 import org.apache.accumulo.monitor.next.ec.CoordinatorSummary;
 import org.apache.accumulo.monitor.next.ec.RunningCompactionDetails;
 import org.apache.accumulo.monitor.next.ec.RunningCompactionsSummary;
+import org.apache.accumulo.monitor.next.sservers.ScanServerView;
 
 import io.micrometer.core.instrument.Meter.Id;
 import io.micrometer.core.instrument.cumulative.CumulativeDistributionSummary;
@@ -238,7 +239,7 @@ public class Endpoints {
   @GET
   @Path("sservers/detail/{" + GROUP_PARAM_KEY + "}")
   @Produces(MediaType.APPLICATION_JSON)
-  @Description("Returns the metric responses for the ScanServers in the 
supplied resource group")
+  @Description("Returns raw metric responses for the ScanServers in the 
supplied resource group")
   public Collection<MetricResponse>
       getScanServers(@PathParam(GROUP_PARAM_KEY) String resourceGroup) {
     validateResourceGroup(resourceGroup);
@@ -253,7 +254,7 @@ public class Endpoints {
   @GET
   @Path("sservers/summary/{" + GROUP_PARAM_KEY + "}")
   @Produces(MediaType.APPLICATION_JSON)
-  @Description("Returns an aggregate view of the metric responses for the 
ScanServers in the supplied resource group")
+  @Description("Returns an aggregate raw metric summary for the ScanServers in 
the supplied resource group (diagnostic endpoint)")
   public Map<Id,CumulativeDistributionSummary>
       getScanServerResourceGroupMetricSummary(@PathParam(GROUP_PARAM_KEY) 
String resourceGroup) {
     validateResourceGroup(resourceGroup);
@@ -268,11 +269,19 @@ public class Endpoints {
   @GET
   @Path("sservers/summary")
   @Produces(MediaType.APPLICATION_JSON)
-  @Description("Returns an aggregate view of the metric responses for all 
ScanServers")
+  @Description("Returns an aggregate raw metric summary for all ScanServers 
(diagnostic endpoint)")
   public Map<Id,CumulativeDistributionSummary> getScanServerAllMetricSummary() 
{
     return 
monitor.getInformationFetcher().getSummaryForEndpoint().getSServerAllMetricSummary();
   }
 
+  @GET
+  @Path("sservers/view")
+  @Produces(MediaType.APPLICATION_JSON)
+  @Description("Returns a UI-ready view model for the Scan Server status page")
+  public ScanServerView getScanServerPageView() {
+    return 
monitor.getInformationFetcher().getSummaryForEndpoint().getScanServerView();
+  }
+
   @GET
   @Path("tservers/detail/{" + GROUP_PARAM_KEY + "}")
   @Produces(MediaType.APPLICATION_JSON)
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 89297e98bf..7e2037530d 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
@@ -23,6 +23,7 @@ import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -51,6 +52,7 @@ import org.apache.accumulo.core.metrics.flatbuffers.FMetric;
 import org.apache.accumulo.core.metrics.flatbuffers.FTag;
 import org.apache.accumulo.core.process.thrift.MetricResponse;
 import org.apache.accumulo.core.spi.balancer.TableLoadBalancer;
+import org.apache.accumulo.monitor.next.sservers.ScanServerView;
 import org.apache.accumulo.server.ServerContext;
 import org.apache.accumulo.server.conf.TableConfiguration;
 import org.apache.accumulo.server.metrics.MetricResponseWrapper;
@@ -348,6 +350,7 @@ public class SystemInformation {
   private final Set<String> suggestions = new ConcurrentSkipListSet<>();
 
   private long timestamp = 0;
+  private ScanServerView scanServerView;
 
   public SystemInformation(Cache<ServerId,MetricResponse> allMetrics, 
ServerContext ctx) {
     this.allMetrics = allMetrics;
@@ -371,6 +374,7 @@ public class SystemInformation {
     tablets.clear();
     deployment.clear();
     suggestions.clear();
+    scanServerView = null;
   }
 
   private void updateAggregates(final MetricResponse response,
@@ -510,7 +514,14 @@ public class SystemInformation {
             + " group " + balancerRG + ", but there are no TabletServers.");
       }
     }
+    Set<ServerId> scanServers = new HashSet<>();
+    sservers.values().forEach(scanServers::addAll);
+    int problemScanServerCount = (int) problemHosts.stream()
+        .filter(serverId -> serverId.getType() == 
ServerId.Type.SCAN_SERVER).count();
+    var responses = allMetrics.getAllPresent(scanServers).values();
     timestamp = System.currentTimeMillis();
+    scanServerView = ScanServerView.fromMetrics(responses, scanServers.size(),
+        problemScanServerCount, timestamp);
   }
 
   public Set<String> getResourceGroups() {
@@ -617,4 +628,8 @@ public class SystemInformation {
     return this.timestamp;
   }
 
+  public ScanServerView getScanServerView() {
+    return this.scanServerView;
+  }
+
 }
diff --git 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/next/sservers/ScanServerView.java
 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/sservers/ScanServerView.java
new file mode 100644
index 0000000000..a212424877
--- /dev/null
+++ 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/next/sservers/ScanServerView.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.accumulo.monitor.next.sservers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import org.apache.accumulo.core.metrics.Metric;
+import org.apache.accumulo.core.metrics.flatbuffers.FMetric;
+import org.apache.accumulo.core.metrics.flatbuffers.FTag;
+import org.apache.accumulo.core.process.thrift.MetricResponse;
+import org.apache.accumulo.server.metrics.MetricResponseWrapper;
+
+/**
+ * Data Transfer Object (DTO) for the Monitor Scan Servers page. It transforms 
backend metrics into
+ * a UI-ready JSON response consumed by the frontend; each record component is 
serialized as a JSON
+ * field.
+ */
+public record ScanServerView(long lastUpdate, List<Row> servers, Status 
status) {
+
+  /**
+   * all the data needed for a row in the table in monitor
+   */
+  public record Row(String host, String resourceGroup, long lastContact, 
boolean metricsAvailable,
+      Number openFiles, Number queries, Number scannedEntries, Number 
queryResults,
+      Number queryResultBytes, Number busyTimeouts, Number 
reservationConflicts,
+      Number zombieThreads, Number serverIdle, Number lowMemoryDetected,
+      Number scansPausedForMemory, Number scansReturnedEarlyForMemory) {
+  }
+
+  /**
+   * all the data needed for the ScanServer status indicator(s)
+   */
+  public record Status(boolean hasScanServers, boolean hasProblemScanServers,
+      boolean hasMissingMetrics, int scanServerCount, int 
problemScanServerCount,
+      long missingMetricServerCount, String level, String message) {
+  }
+
+  private static final String LEVEL_OK = "OK";
+  private static final String LEVEL_WARN = "WARN";
+
+  public static ScanServerView fromMetrics(Collection<MetricResponse> 
responses,
+      int scanServerCount, int problemScanServerCount, long snapshotTime) {
+    var rows = rows(responses, snapshotTime);
+    Status status = buildStatus(scanServerCount, problemScanServerCount, rows);
+    return new ScanServerView(snapshotTime, rows, status);
+  }
+
+  private static List<Row> rows(Collection<MetricResponse> responses, long 
nowMs) {
+    if (responses == null) {
+      return List.of();
+    }
+    return responses.stream().map(response -> toRow(response, nowMs))
+        
.sorted(Comparator.comparing(Row::resourceGroup).thenComparing(Row::host)).toList();
+  }
+
+  private static Status buildStatus(int scanServerCount, int 
problemScanServerCount,
+      List<Row> rows) {
+    long missingMetricServerCount = rows.stream().filter(row -> 
!row.metricsAvailable()).count();
+    boolean hasScanServers = scanServerCount > 0;
+    boolean hasProblemScanServers = problemScanServerCount > 0;
+    boolean hasMissingMetrics = missingMetricServerCount > 0;
+
+    List<String> warnings = new ArrayList<>(2);
+    if (hasProblemScanServers) {
+      warnings.add("one or more scan servers are unavailable");
+    }
+    if (hasMissingMetrics) {
+      warnings.add("ScanServer metrics are not present (are metrics 
enabled?)");
+    }
+
+    if (warnings.isEmpty()) {
+      // no warnings, set status to OK
+      return new Status(hasScanServers, false, false, scanServerCount, 0, 0, 
LEVEL_OK, null);
+    }
+
+    final String message = "WARN: " + String.join("; ", warnings) + ".";
+    return new Status(hasScanServers, hasProblemScanServers, 
hasMissingMetrics, scanServerCount,
+        problemScanServerCount, missingMetricServerCount, LEVEL_WARN, message);
+  }
+
+  private static Row toRow(MetricResponse response, long nowMs) {
+    if (response == null) {
+      return new Row(null, null, 0, false, null, null, null, null, null, null, 
null, null, null,
+          null, null, null);
+    }
+
+    var values = metricValuesByName(response);
+    var openFiles = values.get(Metric.SCAN_OPEN_FILES.getName());
+    var queries = values.get(Metric.SCAN_QUERIES.getName());
+    var scannedEntries = values.get(Metric.SCAN_SCANNED_ENTRIES.getName());
+    var queryResults = values.get(Metric.SCAN_QUERY_SCAN_RESULTS.getName());
+    var queryResultBytes = 
values.get(Metric.SCAN_QUERY_SCAN_RESULTS_BYTES.getName());
+    var busyTimeouts = values.get(Metric.SCAN_BUSY_TIMEOUT_COUNT.getName());
+    var reservationConflicts = 
values.get(Metric.SCAN_RESERVATION_CONFLICT_COUNTER.getName());
+    var zombieThreads = values.get(Metric.SCAN_ZOMBIE_THREADS.getName());
+    var serverIdle = values.get(Metric.SERVER_IDLE.getName());
+    var lowMemoryDetected = values.get(Metric.LOW_MEMORY.getName());
+    var scansPausedForMemory = 
values.get(Metric.SCAN_PAUSED_FOR_MEM.getName());
+    var scansReturnedEarlyForMemory = 
values.get(Metric.SCAN_RETURN_FOR_MEM.getName());
+
+    boolean allMetricsPresent =
+        Stream.of(openFiles, queries, scannedEntries, queryResults, 
queryResultBytes, busyTimeouts,
+            reservationConflicts, zombieThreads, serverIdle, lowMemoryDetected,
+            scansPausedForMemory, 
scansReturnedEarlyForMemory).allMatch(Objects::nonNull);
+
+    long lastContact = Math.max(0, nowMs - response.getTimestamp());
+
+    return new Row(response.getServer(), response.getResourceGroup(), 
lastContact,
+        allMetricsPresent, openFiles, queries, scannedEntries, queryResults, 
queryResultBytes,
+        busyTimeouts, reservationConflicts, zombieThreads, serverIdle, 
lowMemoryDetected,
+        scansPausedForMemory, scansReturnedEarlyForMemory);
+  }
+
+  private static Map<String,Number> metricValuesByName(MetricResponse 
response) {
+    var values = new HashMap<String,Number>();
+    if (response == null || response.getMetrics() == null || 
response.getMetrics().isEmpty()) {
+      return values;
+    }
+
+    for (var binary : response.getMetrics()) {
+      var metric = FMetric.getRootAsFMetric(binary);
+      var metricStatistic = extractStatistic(metric);
+      if (metricStatistic == null || metricStatistic.equals("value")
+          || metricStatistic.equals("count")) {
+        values.putIfAbsent(metric.name(), metricNumericValue(metric));
+      }
+    }
+    return values;
+  }
+
+  private static String extractStatistic(FMetric metric) {
+    for (int i = 0; i < metric.tagsLength(); i++) {
+      FTag tag = metric.tags(i);
+      if (MetricResponseWrapper.STATISTIC_TAG.equals(tag.key())) {
+        return normalizeStatistic(tag.value());
+      }
+    }
+    return null;
+  }
+
+  private static String normalizeStatistic(String statistic) {
+    if (statistic == null) {
+      return null;
+    }
+    return statistic.toLowerCase();
+  }
+
+  private static Number metricNumericValue(FMetric metric) {
+    if (metric.ivalue() != 0) {
+      return metric.ivalue();
+    }
+    if (metric.lvalue() != 0L) {
+      return metric.lvalue();
+    }
+    if (metric.dvalue() != 0.0d) {
+      return metric.dvalue();
+    }
+    return 0;
+  }
+}
diff --git 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js
 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js
index 44b8305719..2d7457f01e 100644
--- 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js
+++ 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/functions.js
@@ -586,14 +586,6 @@ function getCompactorsSummary(group) {
   return getJSONForTable(url, sessionDataVar);
 }
 
-/**
- * REST GET call for /sservers/summary,
- * stores it on a sessionStorage variable
- */
-function getSserversSummary() {
-  return getJSONForTable(REST_V2_PREFIX + '/sservers/summary', 
'sserversSummary');
-}
-
 /**
  * REST GET call for /tables/{name}/tablets,
  * stores it on a sessionStorage variable
@@ -657,14 +649,11 @@ function getDeployment() {
 }
 
 /**
- * REST GET call for /sservers/summary/{group},
+ * REST GET call for /sservers/view,
  * stores it on a sessionStorage variable
- * @param {string} group Group name
  */
-function getSserversSummaryGroup(group) {
-  const url = `${REST_V2_PREFIX}/sservers/summary/${group}`;
-  const sessionDataVar = `sserversSummary_${group}`;
-  return getJSONForTable(url, sessionDataVar);
+function getSserversView() {
+  return getJSONForTable(REST_V2_PREFIX + '/sservers/view', 'sserversView');
 }
 
 /**
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 23dc40e9b7..206302b4a6 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
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/*global REST_V2_PREFIX */
+/*global getSserversView */
 "use strict";
 
 /**
@@ -138,27 +138,18 @@ function updateServerNotifications(statusData) {
  * Updates the scan server notification based on REST v2 metrics status.
  */
 function refreshSserverStatus() {
-  return $.when(
-    $.getJSON(REST_V2_PREFIX + '/sservers/summary'),
-    $.getJSON(REST_V2_PREFIX + '/problems')
-  ).done(function (summaryResp, problemsResp) {
-    var summary = summaryResp && summaryResp[0] ? summaryResp[0] : {};
-    var problems = problemsResp && problemsResp[0] ? problemsResp[0] : [];
-
-    var hasSservers = summary && Object.keys(summary).length > 0;
-    var hasProblemSserver = problems.some(function (problem) {
-      return problem.type === 'SCAN_SERVER' || problem.serverType === 
'SCAN_SERVER';
-    });
-
-    // 1) Display WARN when problem entries show any scan servers
-    if (hasProblemSserver) {
+  return getSserversView().done(function () {
+    var view = sessionStorage.sserversView ? 
JSON.parse(sessionStorage.sserversView) : null;
+    var status = view && view.status ? view.status : null;
+    if (!status) {
+      sessionStorage.sServerStatus = STATUS.ERROR;
+      updateElementStatus('sserverStatusNotification', STATUS.ERROR);
+      return;
+    }
+
+    if (status.level === STATUS.WARN) {
       sessionStorage.sServerStatus = STATUS.WARN;
       updateElementStatus('sserverStatusNotification', STATUS.WARN);
-      // 2) Display OK when there are no scan servers reported
-    } else if (!hasSservers) {
-      sessionStorage.sServerStatus = STATUS.OK;
-      updateElementStatus('sserverStatusNotification', STATUS.OK);
-      // 3) Display OK when scan servers exist and no problems
     } else {
       sessionStorage.sServerStatus = STATUS.OK;
       updateElementStatus('sserverStatusNotification', STATUS.OK);
diff --git 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/sservers.js
 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/sservers.js
index 6b8ac2ae83..2ba580d472 100644
--- 
a/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/sservers.js
+++ 
b/server/monitor/src/main/resources/org/apache/accumulo/monitor/resources/js/sservers.js
@@ -19,227 +19,68 @@
 /* JSLint global definitions */
 /*global
     $, sessionStorage, timeDuration, bigNumberForQuantity, bigNumberForSize, 
ajaxReloadTable,
-    getGroups, getSserversDetail, REST_V2_PREFIX
+    getSserversView
 */
 "use strict";
 
 var sserversTable;
-var SSERVERS_SESSION_KEY = 'sservers';
+var SSERVERS_VIEW_SESSION_KEY = 'sserversView';
 
-var METRICS = {
-  OPEN_FILES: 'accumulo.scan.files.open',
-  QUERIES: 'accumulo.scan.queries',
-  SCANNED_ENTRIES: 'accumulo.scan.query.scanned.entries',
-  QUERY_RESULTS: 'accumulo.scan.query.results',
-  QUERY_RESULTS_BYTES: 'accumulo.scan.query.results.bytes',
-  BUSY_TIMEOUTS: 'accumulo.scan.busy.timeout.count',
-  RESERVATION_CONFLICTS: 'accumulo.scan.reservation.conflict.count',
-  ZOMBIE_THREADS: 'accumulo.scan.zombie.threads',
-  SERVER_IDLE: 'accumulo.server.idle',
-  LOW_MEMORY_DETECTED: 'accumulo.detected.low.memory',
-  SCANS_PAUSED_FOR_MEMORY: 'accumulo.scan.paused.for.memory',
-  SCANS_RETURNED_EARLY_FOR_MEMORY: 'accumulo.scan.return.early.for.memory'
-};
-
-function normalizeStatistic(value) {
-  if (value === null || value === undefined) {
-    return null;
-  }
-  return String(value).toLowerCase();
-}
-
-function extractStatistic(tags) {
-  if (!tags) {
-    return null;
-  }
-  for (var i = 0; i < tags.length; i++) {
-    var tag = tags[i];
-    for (var key in tag) {
-      if (Object.prototype.hasOwnProperty.call(tag, key) && key === 
'statistic') {
-        return normalizeStatistic(tag[key]);
-      }
-    }
-  }
-  return null;
-}
-
-function getMetricValue(metrics, name, statistic) {
-  if (!metrics) {
-    console.warn('Missing scan-server metrics array when reading metric: ' + 
name);
-    return 0;
-  }
-  var desiredStatistic = normalizeStatistic(statistic);
-  for (var i = 0; i < metrics.length; i++) {
-    var metric = metrics[i];
-    if (metric.name !== name) {
-      continue;
-    }
-    var metricStatistic = extractStatistic(metric.tags);
-    if (!desiredStatistic) {
-      if (metricStatistic === null || metricStatistic === 'value' || 
metricStatistic === 'count') {
-        return metric.value;
-      }
-    } else if (metricStatistic === desiredStatistic) {
-      return metric.value;
-    }
-  }
-  console.warn('Missing expected scan-server metric: ' + name);
-  return 0;
-}
-
-function buildScanServerRow(response) {
-  var metrics = response.metrics || [];
-  var hasMetrics = Array.isArray(metrics) && metrics.length > 0;
-  var row = {
-    host: response.host,
-    resourceGroup: response.resourceGroup,
-    lastContact: Date.now() - response.timestamp
-  };
-
-  if (!hasMetrics) {
-    row.openFiles = null;
-    row.queries = null;
-    row.scannedEntries = null;
-    row.queryResults = null;
-    row.queryResultBytes = null;
-    row.busyTimeouts = null;
-    row.reservationConflicts = null;
-    row.zombieThreads = null;
-    row.serverIdle = null;
-    row.lowMemoryDetected = null;
-    row.scansPausedForMemory = null;
-    row.scansReturnedEarlyForMemory = null;
-    return row;
+function getStoredView() {
+  if (!sessionStorage[SSERVERS_VIEW_SESSION_KEY]) {
+    return {};
   }
-
-  row.openFiles = getMetricValue(metrics, METRICS.OPEN_FILES);
-  row.queries = getMetricValue(metrics, METRICS.QUERIES);
-  row.scannedEntries = getMetricValue(metrics, METRICS.SCANNED_ENTRIES);
-  row.queryResults = getMetricValue(metrics, METRICS.QUERY_RESULTS);
-  row.queryResultBytes = getMetricValue(metrics, METRICS.QUERY_RESULTS_BYTES);
-  row.busyTimeouts = getMetricValue(metrics, METRICS.BUSY_TIMEOUTS);
-  row.reservationConflicts = getMetricValue(metrics, 
METRICS.RESERVATION_CONFLICTS);
-  row.zombieThreads = getMetricValue(metrics, METRICS.ZOMBIE_THREADS);
-  row.serverIdle = getMetricValue(metrics, METRICS.SERVER_IDLE);
-  row.lowMemoryDetected = getMetricValue(metrics, METRICS.LOW_MEMORY_DETECTED);
-  row.scansPausedForMemory = getMetricValue(metrics, 
METRICS.SCANS_PAUSED_FOR_MEMORY);
-  row.scansReturnedEarlyForMemory = getMetricValue(metrics, 
METRICS.SCANS_RETURNED_EARLY_FOR_MEMORY);
-  return row;
+  return JSON.parse(sessionStorage[SSERVERS_VIEW_SESSION_KEY]);
 }
 
 function getStoredRows() {
-  if (!sessionStorage[SSERVERS_SESSION_KEY]) {
+  var view = getStoredView();
+  if (!Array.isArray(view.servers)) {
     return [];
   }
-  return JSON.parse(sessionStorage[SSERVERS_SESSION_KEY]).servers;
+  return view.servers;
 }
 
-function setStoredRows(rows) {
-  sessionStorage[SSERVERS_SESSION_KEY] = JSON.stringify({
-    servers: rows
-  });
-}
-
-function buildRowsFromSessionStorage(groups) {
-  var rows = [];
-  var metricsUnavailable = false;
-
-  groups.forEach(function (group) {
-    var sessionKey = 'sserversDetail_' + group;
-    if (!sessionStorage[sessionKey]) {
-      return;
-    }
-    var responses = JSON.parse(sessionStorage[sessionKey]);
-    if (!Array.isArray(responses)) {
-      return;
-    }
-    for (var i = 0; i < responses.length; i++) {
-      if (!Array.isArray(responses[i].metrics) || responses[i].metrics.length 
=== 0) {
-        metricsUnavailable = true;
-        break;
-      }
-    }
-    rows = rows.concat(responses.map(buildScanServerRow));
-  });
-
-  return {
-    rows: rows,
-    metricsUnavailable: metricsUnavailable
-  };
+function getStoredStatus() {
+  var view = getStoredView();
+  return view.status || null;
 }
 
 function refreshScanServersTable() {
   ajaxReloadTable(sserversTable);
 }
 
-function refreshSserversBanner(metricsUnavailable) {
-  return $.when(
-    $.getJSON(REST_V2_PREFIX + '/sservers/summary'),
-    $.getJSON(REST_V2_PREFIX + '/problems')
-  ).done(function (summaryResp, problemsResp) {
-    var summary = summaryResp && summaryResp[0] ? summaryResp[0] : {};
-    var problems = problemsResp && problemsResp[0] ? problemsResp[0] : [];
-    var hasSservers = summary && Object.keys(summary).length > 0;
-    var hasProblemSserver = problems.some(function (problem) {
-      return problem.type === 'SCAN_SERVER' || problem.serverType === 
'SCAN_SERVER';
-    });
-
-    if (metricsUnavailable || hasProblemSserver) {
-      var warnings = [];
-      if (hasProblemSserver) {
-        warnings.push('one or more scan servers are unavailable');
-      }
-      if (metricsUnavailable) {
-        warnings.push('scan-server metrics are not present (are metrics 
enabled?)');
-      }
-      $('#sservers-banner-message')
-        .removeClass('alert-danger')
-        .addClass('alert-warning')
-        .text('WARN: ' + warnings.join('; ') + '.');
-      $('#sserversStatusBanner').show();
-    } else if (!hasSservers) {
-      $('#sserversStatusBanner').hide();
-    } else {
-      $('#sserversStatusBanner').hide();
-    }
-  }).fail(function () {
+function refreshSserversBanner(status) {
+  if (status && status.level === 'WARN') {
     $('#sservers-banner-message')
-      .removeClass('alert-warning')
-      .addClass('alert-danger')
-      .text('ERROR: unable to retrieve scan-server status.');
+      .removeClass('alert-danger')
+      .addClass('alert-warning')
+      .text(status.message || 'WARN: scan-server status warning.');
     $('#sserversStatusBanner').show();
-  });
+  } else {
+    $('#sserversStatusBanner').hide();
+  }
 }
 
-function refreshScanServers() {
-  getGroups().then(function () {
-    var groupList = JSON.parse(sessionStorage.groups || '[]');
-
-    if (!Array.isArray(groupList) || groupList.length === 0) {
-      setStoredRows([]);
-      refreshScanServersTable();
-      refreshSserversBanner(false);
-      return;
-    }
-
-    var requests = $.map(groupList, function (group) {
-      return getSserversDetail(group);
-    });
+function showSserversBannerError() {
+  $('#sservers-banner-message')
+    .removeClass('alert-warning')
+    .addClass('alert-danger')
+    .text('ERROR: unable to retrieve scan-server status.');
+  $('#sserversStatusBanner').show();
+}
 
-    $.when.apply($, requests).done(function () {
-      var detailData = buildRowsFromSessionStorage(groupList);
-      setStoredRows(detailData.rows);
-      refreshScanServersTable();
-      refreshSserversBanner(detailData.metricsUnavailable);
-    }).fail(function () {
-      setStoredRows([]);
-      refreshScanServersTable();
-      refreshSserversBanner(false);
-    });
+function refreshScanServers() {
+  getSserversView().then(function () {
+    refreshScanServersTable();
+    refreshSserversBanner(getStoredStatus());
   }).fail(function () {
-    setStoredRows([]);
+    sessionStorage[SSERVERS_VIEW_SESSION_KEY] = JSON.stringify({
+      servers: [],
+      status: null
+    });
     refreshScanServersTable();
-    refreshSserversBanner(false);
+    showSserversBannerError();
   });
 }
 
@@ -248,7 +89,10 @@ function refresh() {
 }
 
 $(function () {
-  setStoredRows([]);
+  sessionStorage[SSERVERS_VIEW_SESSION_KEY] = JSON.stringify({
+    servers: [],
+    status: null
+  });
 
   sserversTable = $('#sservers').DataTable({
     "autoWidth": false,

Reply via email to