#11 Metrics widget
This commit is contained in:
@@ -1,19 +1,26 @@
|
|||||||
package de.nbscloud.dashboard;
|
package de.nbscloud.dashboard;
|
||||||
|
|
||||||
|
import de.nbscloud.dashboard.widget.MachineMetricsWidget;
|
||||||
import de.nbscloud.webcontainer.registry.App;
|
import de.nbscloud.webcontainer.registry.App;
|
||||||
import de.nbscloud.webcontainer.registry.AppRegistry;
|
import de.nbscloud.webcontainer.registry.AppRegistry;
|
||||||
|
import de.nbscloud.webcontainer.registry.Widget;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class DashboardApp implements App, InitializingBean {
|
public class DashboardApp implements App, InitializingBean {
|
||||||
|
public static final String ID = "dashboard";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AppRegistry appRegistry;
|
private AppRegistry appRegistry;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return "dashboard";
|
return ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -31,6 +38,11 @@ public class DashboardApp implements App, InitializingBean {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Widget> getWidgets() {
|
||||||
|
return List.of(new MachineMetricsWidget());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
this.appRegistry.registerApp(this);
|
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.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.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;
|
flex-grow: 1;
|
||||||
background-color: #1d2121;
|
background-color: #1d2121;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.widget-heading {
|
.widget-heading {
|
||||||
@@ -32,4 +33,8 @@
|
|||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
font-weight: bold;
|
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>
|
||||||
@@ -17,8 +17,8 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class WidgetController {
|
public class FilesWidgetController {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(WidgetController.class);
|
private static final Logger logger = LoggerFactory.getLogger(FilesWidgetController.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FilesConfig filesConfig;
|
private FilesConfig filesConfig;
|
||||||
Reference in New Issue
Block a user