#11 Metrics widget
This commit is contained in:
@@ -1,19 +1,26 @@
|
||||
package de.nbscloud.dashboard;
|
||||
|
||||
import de.nbscloud.dashboard.widget.MachineMetricsWidget;
|
||||
import de.nbscloud.webcontainer.registry.App;
|
||||
import de.nbscloud.webcontainer.registry.AppRegistry;
|
||||
import de.nbscloud.webcontainer.registry.Widget;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class DashboardApp implements App, InitializingBean {
|
||||
public static final String ID = "dashboard";
|
||||
|
||||
@Autowired
|
||||
private AppRegistry appRegistry;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "dashboard";
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -31,6 +38,11 @@ public class DashboardApp implements App, InitializingBean {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Widget> getWidgets() {
|
||||
return List.of(new MachineMetricsWidget());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.appRegistry.registerApp(this);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package de.nbscloud.dashboard.config;
|
||||
|
||||
import de.nbscloud.dashboard.widget.metrics.updates.scraper.UpdateScrapers;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "nbs-cloud.dashboard")
|
||||
@PropertySource("classpath:/config/dashboard-application.properties")
|
||||
public class DashboardConfig {
|
||||
@Configuration
|
||||
public static class Metrics {
|
||||
@Configuration
|
||||
public static class Updates {
|
||||
private List<UpdateScrapers> scrapers;
|
||||
|
||||
public List<UpdateScrapers> getScrapers() {
|
||||
return scrapers;
|
||||
}
|
||||
|
||||
public void setScrapers(List<UpdateScrapers> scrapers) {
|
||||
this.scrapers = scrapers;
|
||||
}
|
||||
}
|
||||
|
||||
private Updates updates;
|
||||
|
||||
public Updates getUpdates() {
|
||||
return updates;
|
||||
}
|
||||
|
||||
public void setUpdates(Updates updates) {
|
||||
this.updates = updates;
|
||||
}
|
||||
}
|
||||
|
||||
private Metrics metrics;
|
||||
|
||||
public Metrics getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public void setMetrics(Metrics metrics) {
|
||||
this.metrics = metrics;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.nbscloud.dashboard.controller;
|
||||
|
||||
import de.nbscloud.dashboard.config.DashboardConfig;
|
||||
import de.nbscloud.dashboard.widget.service.MetricService;
|
||||
import de.nbscloud.webcontainer.shared.config.WebContainerSharedConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@Controller
|
||||
public class DashboardWidgetController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DashboardWidgetController.class);
|
||||
|
||||
@Autowired
|
||||
private WebContainerSharedConfig webContainerSharedConfig;
|
||||
@Autowired
|
||||
private MetricService metricService;
|
||||
@Autowired
|
||||
private DashboardConfig dashboardConfig;
|
||||
|
||||
@GetMapping("dashboard/widgets/machineMetrics")
|
||||
public String getDiskUsage(HttpServletRequest request, HttpServletResponse response, Model model) {
|
||||
model.addAttribute("interfaces", metricService.getInterfaces());
|
||||
model.addAttribute("updates", metricService.getUpdates());
|
||||
|
||||
return "dashboard/widgets/machineMetrics :: dashboard-machine-metrics";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.nbscloud.dashboard.widget;
|
||||
|
||||
import de.nbscloud.dashboard.DashboardApp;
|
||||
import de.nbscloud.webcontainer.registry.Widget;
|
||||
|
||||
public class MachineMetricsWidget implements Widget {
|
||||
@Override
|
||||
public String getPath() {
|
||||
return DashboardApp.ID + "/widgets/machineMetrics";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.nbscloud.dashboard.widget.metrics.updates.scraper;
|
||||
|
||||
import de.nbscloud.dashboard.widget.service.MetricService;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface UpdateScraper {
|
||||
public MetricService.UpdateContainer scrape();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package de.nbscloud.dashboard.widget.metrics.updates.scraper;
|
||||
|
||||
import de.nbscloud.dashboard.widget.metrics.updates.scraper.impl.ArchAuracleScraperImpl;
|
||||
import de.nbscloud.dashboard.widget.metrics.updates.scraper.impl.ArchCheckupdatesScraperImpl;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public enum UpdateScrapers {
|
||||
ARCH_CHECKUPDATES(ArchCheckupdatesScraperImpl.class),
|
||||
ARCH_AURACLE(ArchAuracleScraperImpl.class);
|
||||
|
||||
private Class<? extends UpdateScraper> scraperClass;
|
||||
|
||||
UpdateScrapers(Class<? extends UpdateScraper> scraperClass) {
|
||||
this.scraperClass = scraperClass;
|
||||
}
|
||||
|
||||
public UpdateScraper getUpdateScraper() {
|
||||
try {
|
||||
return this.scraperClass.getDeclaredConstructor().newInstance();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.nbscloud.dashboard.widget.metrics.updates.scraper.impl;
|
||||
|
||||
import de.nbscloud.dashboard.widget.metrics.updates.scraper.UpdateScraper;
|
||||
import de.nbscloud.dashboard.widget.service.MetricService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class ArchAuracleScraperImpl implements UpdateScraper {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ArchAuracleScraperImpl.class);
|
||||
private static final String KEY = "AUR";
|
||||
|
||||
@Override
|
||||
public MetricService.UpdateContainer scrape() {
|
||||
try {
|
||||
final Process process = Runtime.getRuntime()
|
||||
.exec(new String[]{"sh", "-c", "auracle outdated | wc -l"});
|
||||
final String updateCount = new BufferedReader(new InputStreamReader(process.getInputStream())).readLine();
|
||||
|
||||
process.destroy();
|
||||
|
||||
if(updateCount != null && !updateCount.isBlank()) {
|
||||
return new MetricService.UpdateContainer(KEY, updateCount);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not get AUR update count", e);
|
||||
|
||||
return new MetricService.UpdateContainer(KEY, "error");
|
||||
}
|
||||
|
||||
return new MetricService.UpdateContainer(KEY, "0");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.nbscloud.dashboard.widget.metrics.updates.scraper.impl;
|
||||
|
||||
import de.nbscloud.dashboard.widget.metrics.updates.scraper.UpdateScraper;
|
||||
import de.nbscloud.dashboard.widget.service.MetricService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class ArchCheckupdatesScraperImpl implements UpdateScraper {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ArchCheckupdatesScraperImpl.class);
|
||||
private static final String KEY = "pacman";
|
||||
|
||||
@Override
|
||||
public MetricService.UpdateContainer scrape() {
|
||||
try {
|
||||
final Process process = Runtime.getRuntime()
|
||||
.exec(new String[]{"sh", "-c", "checkupdates | wc -l"});
|
||||
final String updateCount = new BufferedReader(new InputStreamReader(process.getInputStream())).readLine();
|
||||
|
||||
process.destroy();
|
||||
|
||||
if(updateCount != null && !updateCount.isBlank()) {
|
||||
return new MetricService.UpdateContainer(KEY, updateCount);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not get pacman update count", e);
|
||||
|
||||
return new MetricService.UpdateContainer(KEY, "error");
|
||||
}
|
||||
|
||||
return new MetricService.UpdateContainer(KEY, "0");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package de.nbscloud.dashboard.widget.service;
|
||||
|
||||
import de.nbscloud.dashboard.config.DashboardConfig;
|
||||
import de.nbscloud.dashboard.widget.metrics.updates.scraper.UpdateScraper;
|
||||
import de.nbscloud.dashboard.widget.metrics.updates.scraper.UpdateScrapers;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class MetricService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MetricService.class);
|
||||
|
||||
public static record InterfaceContainer(String name, String ip) {
|
||||
}
|
||||
|
||||
public static record UpdateContainer(String key, String count) {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private DashboardConfig dashboardConfig;
|
||||
|
||||
public List<InterfaceContainer> getInterfaces() {
|
||||
try {
|
||||
final Iterator<NetworkInterface> networkInterfaceIterator = NetworkInterface.getNetworkInterfaces()
|
||||
.asIterator();
|
||||
final List<InterfaceContainer> retList = new ArrayList<>();
|
||||
|
||||
while (networkInterfaceIterator.hasNext()) {
|
||||
final NetworkInterface networkInterface = networkInterfaceIterator.next();
|
||||
|
||||
if (networkInterface.isLoopback() || !networkInterface.isUp()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
|
||||
retList.add(new InterfaceContainer(networkInterface.getDisplayName(), removeInterfaceName(removeSlash(interfaceAddress.getAddress()
|
||||
.toString()))));
|
||||
}
|
||||
}
|
||||
|
||||
retList.sort(Comparator.comparing(InterfaceContainer::name));
|
||||
|
||||
return retList;
|
||||
} catch (SocketException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String removeSlash(String address) {
|
||||
return address.startsWith("/") ? address.substring(1) : address;
|
||||
}
|
||||
|
||||
private static final String removeInterfaceName(String address) {
|
||||
// IPv6 addresses have the interface name in them like
|
||||
// abc:...:xyz%myInterface
|
||||
|
||||
return address.contains("%") ? address.substring(0, address.indexOf("%")) : address;
|
||||
}
|
||||
|
||||
public List<UpdateContainer> getUpdates() {
|
||||
return this.dashboardConfig.getMetrics()
|
||||
.getUpdates()
|
||||
.getScrapers()
|
||||
.stream()
|
||||
.map(UpdateScrapers::getUpdateScraper)
|
||||
.map(UpdateScraper::scrape)
|
||||
.sorted(Comparator.comparing(UpdateContainer::key))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
spring.profiles.active=@activeProfiles@
|
||||
|
||||
info.app.name=NoBullShit Cloud - Dashboard app
|
||||
info.app.description=A simple dashboard app with widget support
|
||||
|
||||
nbs-cloud.dashboard.metrics.updates.scrapers[0]=ARCH_CHECKUPDATES
|
||||
nbs-cloud.dashboard.metrics.updates.scrapers[1]=ARCH_AURACLE
|
||||
@@ -1,3 +1,7 @@
|
||||
nbscloud.dashboard.greeting=Welcome to nbscloud
|
||||
|
||||
nbscloud.dashboard.index.title=nbscloud\: dashboard
|
||||
nbscloud.dashboard.index.title=nbscloud\: dashboard
|
||||
|
||||
nbscloud.dashboard.dashboard-machine-metrics-widget.heading=dashboard machine metrics
|
||||
nbscloud.dashboard.dashboard-machine-metrics-widget-table.interfaces=Interfaces\:
|
||||
nbscloud.dashboard.dashboard-machine-metrics-widget-table.updates=Updates\:
|
||||
@@ -1,3 +1,7 @@
|
||||
nbscloud.dashboard.greeting=Willkommen bei nbscloud
|
||||
|
||||
nbscloud.dashboard.index.title=nbscloud\: \u00DCbersicht
|
||||
nbscloud.dashboard.index.title=nbscloud\: \u00DCbersicht
|
||||
|
||||
nbscloud.dashboard.dashboard-machine-metrics-widget.heading=dashboard Metriken
|
||||
nbscloud.dashboard.dashboard-machine-metrics-widget-table.interfaces=Schnittstellen\:
|
||||
nbscloud.dashboard.dashboard-machine-metrics-widget-table.updates=Updates\:
|
||||
@@ -18,6 +18,7 @@
|
||||
flex-grow: 1;
|
||||
background-color: #1d2121;
|
||||
padding: 1em;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.widget-heading {
|
||||
@@ -32,4 +33,8 @@
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.widget > table > tbody > tr > td {
|
||||
padding-inline-end: 1em;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<div id="dashboard-machine-metrics-widget" class="widget" th:fragment="dashboard-machine-metrics">
|
||||
<p class="widget-heading" th:text="#{nbscloud.dashboard.dashboard-machine-metrics-widget.heading}"/>
|
||||
<table id="dashboard-machine-metrics-widget-table">
|
||||
<tr th:each="interface, i : ${interfaces}">
|
||||
<td th:if="${i.count == 1}"
|
||||
th:text="#{nbscloud.dashboard.dashboard-machine-metrics-widget-table.interfaces}" />
|
||||
<td th:if="${i.count > 1}"></td>
|
||||
<td th:text="${interface.name}" />
|
||||
<td th:text="${interface.ip}" />
|
||||
</tr>
|
||||
<tr th:each="update, i : ${updates}">
|
||||
<td th:if="${i.count == 1}"
|
||||
th:text="#{nbscloud.dashboard.dashboard-machine-metrics-widget-table.updates}" />
|
||||
<td th:if="${i.count > 1}"></td>
|
||||
<td th:text="${update.key}" />
|
||||
<td th:text="${update.count}" />
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
Reference in New Issue
Block a user