From 4134160b97807452f2a82da45bd9bb47af9dc63d Mon Sep 17 00:00:00 2001 From: MK13 Date: Sat, 13 Aug 2022 15:09:05 +0200 Subject: [PATCH] #11 Metrics widget --- .../de/nbscloud/dashboard/DashboardApp.java | 14 +++- .../dashboard/config/DashboardConfig.java | 49 ++++++++++++ .../controller/DashboardWidgetController.java | 34 ++++++++ .../widget/MachineMetricsWidget.java | 11 +++ .../updates/scraper/UpdateScraper.java | 9 +++ .../updates/scraper/UpdateScrapers.java | 25 ++++++ .../scraper/impl/ArchAuracleScraperImpl.java | 36 +++++++++ .../impl/ArchCheckupdatesScraperImpl.java | 36 +++++++++ .../widget/service/MetricService.java | 78 +++++++++++++++++++ .../config/dashboard-application.properties | 7 ++ .../i18n/dashboard_messages.properties | 6 +- .../i18n/dashboard_messages_de_DE.properties | 6 +- .../resources/static/css/dashboard_main.css | 5 ++ .../dashboard/widgets/machineMetrics.html | 19 +++++ ...roller.java => FilesWidgetController.java} | 4 +- 15 files changed, 334 insertions(+), 5 deletions(-) create mode 100644 dashboard/src/main/java/de/nbscloud/dashboard/config/DashboardConfig.java create mode 100644 dashboard/src/main/java/de/nbscloud/dashboard/controller/DashboardWidgetController.java create mode 100644 dashboard/src/main/java/de/nbscloud/dashboard/widget/MachineMetricsWidget.java create mode 100644 dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/UpdateScraper.java create mode 100644 dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/UpdateScrapers.java create mode 100644 dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/impl/ArchAuracleScraperImpl.java create mode 100644 dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/impl/ArchCheckupdatesScraperImpl.java create mode 100644 dashboard/src/main/java/de/nbscloud/dashboard/widget/service/MetricService.java create mode 100644 dashboard/src/main/resources/config/dashboard-application.properties create mode 100644 dashboard/src/main/resources/templates/dashboard/widgets/machineMetrics.html rename files/src/main/java/de/nbscloud/files/controller/{WidgetController.java => FilesWidgetController.java} (96%) diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/DashboardApp.java b/dashboard/src/main/java/de/nbscloud/dashboard/DashboardApp.java index 2c44431..975ba8f 100644 --- a/dashboard/src/main/java/de/nbscloud/dashboard/DashboardApp.java +++ b/dashboard/src/main/java/de/nbscloud/dashboard/DashboardApp.java @@ -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 getWidgets() { + return List.of(new MachineMetricsWidget()); + } + @Override public void afterPropertiesSet() throws Exception { this.appRegistry.registerApp(this); diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/config/DashboardConfig.java b/dashboard/src/main/java/de/nbscloud/dashboard/config/DashboardConfig.java new file mode 100644 index 0000000..209bea4 --- /dev/null +++ b/dashboard/src/main/java/de/nbscloud/dashboard/config/DashboardConfig.java @@ -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 scrapers; + + public List getScrapers() { + return scrapers; + } + + public void setScrapers(List 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; + } +} diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/controller/DashboardWidgetController.java b/dashboard/src/main/java/de/nbscloud/dashboard/controller/DashboardWidgetController.java new file mode 100644 index 0000000..7e92181 --- /dev/null +++ b/dashboard/src/main/java/de/nbscloud/dashboard/controller/DashboardWidgetController.java @@ -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"; + } +} diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/widget/MachineMetricsWidget.java b/dashboard/src/main/java/de/nbscloud/dashboard/widget/MachineMetricsWidget.java new file mode 100644 index 0000000..5d2a4ff --- /dev/null +++ b/dashboard/src/main/java/de/nbscloud/dashboard/widget/MachineMetricsWidget.java @@ -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"; + } +} diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/UpdateScraper.java b/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/UpdateScraper.java new file mode 100644 index 0000000..9950cc8 --- /dev/null +++ b/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/UpdateScraper.java @@ -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(); +} diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/UpdateScrapers.java b/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/UpdateScrapers.java new file mode 100644 index 0000000..9c5995a --- /dev/null +++ b/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/UpdateScrapers.java @@ -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 scraperClass; + + UpdateScrapers(Class scraperClass) { + this.scraperClass = scraperClass; + } + + public UpdateScraper getUpdateScraper() { + try { + return this.scraperClass.getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/impl/ArchAuracleScraperImpl.java b/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/impl/ArchAuracleScraperImpl.java new file mode 100644 index 0000000..584d7b8 --- /dev/null +++ b/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/impl/ArchAuracleScraperImpl.java @@ -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"); + } +} diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/impl/ArchCheckupdatesScraperImpl.java b/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/impl/ArchCheckupdatesScraperImpl.java new file mode 100644 index 0000000..cf825ac --- /dev/null +++ b/dashboard/src/main/java/de/nbscloud/dashboard/widget/metrics/updates/scraper/impl/ArchCheckupdatesScraperImpl.java @@ -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"); + } +} diff --git a/dashboard/src/main/java/de/nbscloud/dashboard/widget/service/MetricService.java b/dashboard/src/main/java/de/nbscloud/dashboard/widget/service/MetricService.java new file mode 100644 index 0000000..113e4c7 --- /dev/null +++ b/dashboard/src/main/java/de/nbscloud/dashboard/widget/service/MetricService.java @@ -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 getInterfaces() { + try { + final Iterator networkInterfaceIterator = NetworkInterface.getNetworkInterfaces() + .asIterator(); + final List 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 getUpdates() { + return this.dashboardConfig.getMetrics() + .getUpdates() + .getScrapers() + .stream() + .map(UpdateScrapers::getUpdateScraper) + .map(UpdateScraper::scrape) + .sorted(Comparator.comparing(UpdateContainer::key)) + .collect(Collectors.toList()); + } +} diff --git a/dashboard/src/main/resources/config/dashboard-application.properties b/dashboard/src/main/resources/config/dashboard-application.properties new file mode 100644 index 0000000..05a5bd8 --- /dev/null +++ b/dashboard/src/main/resources/config/dashboard-application.properties @@ -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 \ No newline at end of file diff --git a/dashboard/src/main/resources/i18n/dashboard_messages.properties b/dashboard/src/main/resources/i18n/dashboard_messages.properties index 0e41302..761c5ae 100644 --- a/dashboard/src/main/resources/i18n/dashboard_messages.properties +++ b/dashboard/src/main/resources/i18n/dashboard_messages.properties @@ -1,3 +1,7 @@ nbscloud.dashboard.greeting=Welcome to nbscloud -nbscloud.dashboard.index.title=nbscloud\: dashboard \ No newline at end of file +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\: \ No newline at end of file diff --git a/dashboard/src/main/resources/i18n/dashboard_messages_de_DE.properties b/dashboard/src/main/resources/i18n/dashboard_messages_de_DE.properties index 46cc008..1c84a51 100644 --- a/dashboard/src/main/resources/i18n/dashboard_messages_de_DE.properties +++ b/dashboard/src/main/resources/i18n/dashboard_messages_de_DE.properties @@ -1,3 +1,7 @@ nbscloud.dashboard.greeting=Willkommen bei nbscloud -nbscloud.dashboard.index.title=nbscloud\: \u00DCbersicht \ No newline at end of file +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\: \ No newline at end of file diff --git a/dashboard/src/main/resources/static/css/dashboard_main.css b/dashboard/src/main/resources/static/css/dashboard_main.css index 1670d0d..31707ae 100644 --- a/dashboard/src/main/resources/static/css/dashboard_main.css +++ b/dashboard/src/main/resources/static/css/dashboard_main.css @@ -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; } \ No newline at end of file diff --git a/dashboard/src/main/resources/templates/dashboard/widgets/machineMetrics.html b/dashboard/src/main/resources/templates/dashboard/widgets/machineMetrics.html new file mode 100644 index 0000000..b1a15b6 --- /dev/null +++ b/dashboard/src/main/resources/templates/dashboard/widgets/machineMetrics.html @@ -0,0 +1,19 @@ +
+

+ + + + + + + +
+ + +
+ + +
+

\ No newline at end of file diff --git a/files/src/main/java/de/nbscloud/files/controller/WidgetController.java b/files/src/main/java/de/nbscloud/files/controller/FilesWidgetController.java similarity index 96% rename from files/src/main/java/de/nbscloud/files/controller/WidgetController.java rename to files/src/main/java/de/nbscloud/files/controller/FilesWidgetController.java index f2a02d1..e816fa7 100644 --- a/files/src/main/java/de/nbscloud/files/controller/WidgetController.java +++ b/files/src/main/java/de/nbscloud/files/controller/FilesWidgetController.java @@ -17,8 +17,8 @@ import javax.servlet.http.HttpServletResponse; import java.nio.file.Paths; @Controller -public class WidgetController { - private static final Logger logger = LoggerFactory.getLogger(WidgetController.class); +public class FilesWidgetController { + private static final Logger logger = LoggerFactory.getLogger(FilesWidgetController.class); @Autowired private FilesConfig filesConfig;