diff --git a/misc/org-backend.env.example b/misc/org-backend.env.example index a6a30ae..709e9a6 100755 --- a/misc/org-backend.env.example +++ b/misc/org-backend.env.example @@ -17,15 +17,16 @@ ZONE_BUILD_DIR=/home/zaine/master-folder/org-platform/org_web ZONE_BUILD_LOG=/home/zaine/master-folder/org-platform/org_web/org-web-build.log ZONE_MANIFEST_PATH=/home/zaine/master-folder/org-platform/zone/data/manifest.json ZONE_AUTHORING_URL=http://127.0.0.1:8765 -ZONE_WATCHER_UNIT=watcher.service -ZONE_SCRIPTS_LOG_DIR=/home/zaine/logs - +ZONE_WATCHER_UNIT=watcher.service +ZONE_SCRIPTS_LOG_DIR=/home/zaine/logs +ZONE_RESOURCES_STORAGE_PATH=/ + EMACS_RUN_DIR=/home/zaine EMACS_RUN_LOG=/home/zaine/logs/emacs.log COMBINED_RUN_LOG=/home/zaine/logs/combined.log ADVENTURE_RESOURCES_FREE_DIR=/home/zaine ADVENTURE_RESOURCES_FREE_LOG=/home/zaine/logs/adventure-resources.log -ADVENTURE_RESOURCES_FREE_COMMAND="/usr/bin/docker system prune -af" +ADVENTURE_RESOURCES_FREE_COMMAND="sudo -n sh -c 'sync; echo 3 > /proc/sys/vm/drop_caches' && /usr/bin/docker system prune -af && /usr/bin/docker builder prune -af" GUACAMOLE_RUN_DIR=/home/zaine GUACAMOLE_CONTAINER_NAME=guacamole GUACAMOLE_START_LOG=/home/zaine/logs/guacamole-start.log diff --git a/src/main/java/org/zaine/app/controller/zone/BuildController.java b/src/main/java/org/zaine/app/controller/zone/BuildController.java index be4d40f..d4b460a 100755 --- a/src/main/java/org/zaine/app/controller/zone/BuildController.java +++ b/src/main/java/org/zaine/app/controller/zone/BuildController.java @@ -85,7 +85,7 @@ public class BuildController { @Value("${combined.run.log}") private String combinedRunLogFile; @Value("${adventure.resources.free.dir:/home/zaine}") private String resourceFreeDirectory; @Value("${adventure.resources.free.log:/home/zaine/logs/adventure-resources.log}") private String resourceFreeLogFile; - @Value("${adventure.resources.free.command:/usr/bin/docker system prune -af}") private String resourceFreeCommand; + @Value("${adventure.resources.free.command:sudo -n sh -c 'sync; echo 3 > /proc/sys/vm/drop_caches' && /usr/bin/docker system prune -af && /usr/bin/docker builder prune -af}") private String resourceFreeCommand; @Value("${guacamole.run.dir:/home/zaine}") private String guacamoleRunDirectory; @Value("${guacamole.container.name:guacamole}") private String guacamoleContainerName; @Value("${guacamole.start.log:/home/zaine/logs/guacamole-start.log}") private String guacamoleStartLogFile; diff --git a/src/main/java/org/zaine/app/service/ZoneStatusService.java b/src/main/java/org/zaine/app/service/ZoneStatusService.java index 8e77f17..79e57ef 100755 --- a/src/main/java/org/zaine/app/service/ZoneStatusService.java +++ b/src/main/java/org/zaine/app/service/ZoneStatusService.java @@ -6,11 +6,13 @@ import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; - -import java.nio.file.Files; - -import java.nio.file.Path; +import java.nio.charset.StandardCharsets; + +import java.nio.file.FileStore; + +import java.nio.file.Files; + +import java.nio.file.Path; import java.time.Duration; @@ -64,9 +66,11 @@ public class ZoneStatusService { private final Path buildLogPath; - private final Path scriptsLogDir; - - private final TimesheetService timesheetService; + private final Path scriptsLogDir; + + private final Path storagePath; + + private final TimesheetService timesheetService; private final BuildRunStateService buildRunState; @@ -78,13 +82,15 @@ public class ZoneStatusService { @Value("${zone.authoring.url}") String authoringUrl, - @Value("${zone.watcher.unit}") String watcherUnit, - - @Value("${zone.build.log}") String buildLogPath, - - @Value("${zone.scripts.log.dir}") String scriptsLogDir, - - TimesheetService timesheetService, + @Value("${zone.watcher.unit}") String watcherUnit, + + @Value("${zone.build.log}") String buildLogPath, + + @Value("${zone.scripts.log.dir}") String scriptsLogDir, + + @Value("${zone.resources.storage.path:/}") String storagePath, + + TimesheetService timesheetService, BuildRunStateService buildRunState) { @@ -96,9 +102,11 @@ public class ZoneStatusService { this.buildLogPath = Path.of(buildLogPath); - this.scriptsLogDir = Path.of(scriptsLogDir); - - this.timesheetService = timesheetService; + this.scriptsLogDir = Path.of(scriptsLogDir); + + this.storagePath = Path.of(storagePath); + + this.timesheetService = timesheetService; this.buildRunState = buildRunState; @@ -118,13 +126,86 @@ public class ZoneStatusService { status.put("scripts", fetchScriptLogs()); - status.put("lastRuns", buildRunState.lastRunsSnapshot()); - - status.put("timesheet", fetchTimesheetSummary()); + status.put("lastRuns", buildRunState.lastRunsSnapshot()); + + status.put("systemResources", fetchSystemResources()); + + status.put("timesheet", fetchTimesheetSummary()); return status; - } + } + + private Map fetchSystemResources() { + Map resources = new LinkedHashMap<>(); + resources.put("available", true); + resources.put("cpu", fetchCpu()); + resources.put("memory", fetchMemory()); + resources.put("storage", fetchStorage()); + return resources; + } + + private Map fetchCpu() { + Map cpu = new LinkedHashMap<>(); + java.lang.management.OperatingSystemMXBean os = + java.lang.management.ManagementFactory.getOperatingSystemMXBean(); + cpu.put("cores", os.getAvailableProcessors()); + cpu.put("loadAverage", os.getSystemLoadAverage()); + if (os instanceof com.sun.management.OperatingSystemMXBean sunOs) { + double load = sunOs.getCpuLoad(); + if (load >= 0) { + cpu.put("usagePct", Math.round(load * 1000.0) / 10.0); + } + double processLoad = sunOs.getProcessCpuLoad(); + if (processLoad >= 0) { + cpu.put("processUsagePct", Math.round(processLoad * 1000.0) / 10.0); + } + } + return cpu; + } + + private Map fetchMemory() { + Map memory = new LinkedHashMap<>(); + java.lang.management.OperatingSystemMXBean os = + java.lang.management.ManagementFactory.getOperatingSystemMXBean(); + if (os instanceof com.sun.management.OperatingSystemMXBean sunOs) { + long total = sunOs.getTotalPhysicalMemorySize(); + long free = sunOs.getFreePhysicalMemorySize(); + long used = Math.max(0, total - free); + memory.put("totalBytes", total); + memory.put("freeBytes", free); + memory.put("usedBytes", used); + memory.put("usedPct", pct(used, total)); + } else { + memory.put("available", false); + memory.put("message", "Physical memory stats unavailable"); + } + return memory; + } + + private Map fetchStorage() { + Map storage = new LinkedHashMap<>(); + storage.put("path", storagePath.toString()); + try { + FileStore store = Files.getFileStore(storagePath); + long total = store.getTotalSpace(); + long usable = store.getUsableSpace(); + long used = Math.max(0, total - usable); + storage.put("totalBytes", total); + storage.put("usableBytes", usable); + storage.put("usedBytes", used); + storage.put("usedPct", pct(used, total)); + } catch (IOException ex) { + storage.put("available", false); + storage.put("message", ex.getMessage()); + } + return storage; + } + + private static double pct(long value, long total) { + if (total <= 0) return 0.0; + return Math.round((value * 1000.0) / total) / 10.0; + }