From 7c73b68981cd14e11484fdf41ffd31cec07427d1 Mon Sep 17 00:00:00 2001 From: John Haverlack Date: Sun, 8 Feb 2026 09:52:39 -0900 Subject: [PATCH] Refactor of telemetry format + hw detection --- docs/DESIGN.md | 2 +- sdl-mgr/app/modules/sdl-mgr/index.js | 3 +- sdl-mgr/conf/modules/sdl-mgr.json | 4 +- sdl-wkr/app/modules/sdl-wkr/index.js | 98 +++++++++++++++++++++++++++- 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 5265d63..8d81594 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -57,7 +57,7 @@ MVP - Minimum Viable Product Task List - [ ] Web UI - [ ] Progresive Web App - [ ] Editable Dashboard - - [ ] Panel Icon + - [x] Panel Icon **Resource Management** diff --git a/sdl-mgr/app/modules/sdl-mgr/index.js b/sdl-mgr/app/modules/sdl-mgr/index.js index b2e9338..9664a5a 100644 --- a/sdl-mgr/app/modules/sdl-mgr/index.js +++ b/sdl-mgr/app/modules/sdl-mgr/index.js @@ -1,6 +1,6 @@ const module = 'sdl-mgr'; // Module Name -import { load_config, log, ipToInt, intToIp, computeBroadcast, getProcessStartTs, getUptimeDHMS } from '../nwa-lib/index.js'; +import { load_config, log, ipToInt, intToIp, computeBroadcast, getProcessStartTs, getUptimeDHMS, getUptimeSec } from '../nwa-lib/index.js'; import mqtt from 'mqtt'; import os from 'os'; import dgram from 'dgram'; @@ -385,6 +385,7 @@ function publishStatus(client, topic) { msg: { sdl: { version: config.package.version, + uptime_secs: getUptimeSec(), uptime: getUptimeDHMS(), update_cmd: `curl -s http://${ip_addr}:${config.modules.web.port}/dist/install-sdl-wkr.sh | bash -s ${ip_addr}:${web_port}`, }, diff --git a/sdl-mgr/conf/modules/sdl-mgr.json b/sdl-mgr/conf/modules/sdl-mgr.json index d4f7d6d..f8b93bf 100644 --- a/sdl-mgr/conf/modules/sdl-mgr.json +++ b/sdl-mgr/conf/modules/sdl-mgr.json @@ -5,8 +5,8 @@ "update_interval": { "udp_beacon": 2000, "cluster_status": 10000, - "cluster_workers": 10000, - "cluster_telemetry": 3000 + "cluster_workers": 2000, + "cluster_telemetry": 1000 }, "expiration_timeout": { "cluster_status": 60000, diff --git a/sdl-wkr/app/modules/sdl-wkr/index.js b/sdl-wkr/app/modules/sdl-wkr/index.js index 1640e2b..e64af4f 100644 --- a/sdl-wkr/app/modules/sdl-wkr/index.js +++ b/sdl-wkr/app/modules/sdl-wkr/index.js @@ -2,8 +2,10 @@ const module = 'sdl-wkr'; // Module Name import { load_config, log, getUptimeDHMS, getOSUptimeDHMS } from '../nwa-lib/index.js'; import dgram from 'dgram'; import mqtt from 'mqtt'; +import fs from 'fs'; import os, { platform } from 'os'; import { exec, execSync } from 'child_process'; +import { get } from 'http'; const config = load_config(); @@ -237,6 +239,8 @@ function handleClusterStatus(payload) { const clusterVersion = payload.msg?.sdl?.version; const updateCmd = payload.msg?.sdl?.update_cmd; const localVersion = config.package.version; + const uptime = payload.msg?.sdl?.uptime_secs; + if (!clusterVersion) return; @@ -245,6 +249,11 @@ function handleClusterStatus(payload) { `${module}: version mismatch detected (cluster=${clusterVersion}, local=${localVersion})` ); triggerWorkerUpdate(clusterVersion, updateCmd); + } else if (uptime < 5) { // sld-mgr restarts for Development Testing + log( + `${module}: version mismatch detected (cluster=${clusterVersion}, local=${localVersion})` + ); + triggerWorkerUpdate(clusterVersion, updateCmd); } } @@ -394,7 +403,8 @@ function publishTelemetry(topic) { arch: config.host.cpu.arch, distro: config.host.os.pretty_name, distro_name: config.host.os.name, - distro_version: config.host.os.version + distro_version: config.host.os.version, + hardware: getSystemHardware() }, uptime: { process: Math.floor(process.uptime()), @@ -497,6 +507,92 @@ function getGPUUsage() { +function getSystemHardware() { + const hardware = { + type: 'unknown', + manufacturer: 'Unknown', + model: 'Unknown' + }; + + // Only run Linux-specific detection on Linux + if (config.host.os.platform !== 'linux') { + return hardware; + } + + // === Linux-specific detection below === + + // Check for Raspberry Pi FIRST + try { + const piModel = fs.readFileSync('/proc/device-tree/model', 'utf8') + .replace(/\0/g, '') + .trim(); + + if (piModel) { + hardware.type = 'hw'; // ✅ Changed from 'physical' + hardware.manufacturer = 'Raspberry Pi Foundation'; + hardware.model = piModel; + return hardware; + } + } catch (err) { + // Not a Pi, continue + } + + // Check for hypervisor flag + try { + const cpuinfo = fs.readFileSync('/proc/cpuinfo', 'utf8'); + if (cpuinfo.includes('hypervisor')) { + hardware.type = 'vm'; + + // Detect hypervisor type from DMI + try { + const manufacturer = fs.readFileSync('/sys/class/dmi/id/sys_vendor', 'utf8').trim(); + const product = fs.readFileSync('/sys/class/dmi/id/product_name', 'utf8').trim(); + + hardware.manufacturer = manufacturer; + + // Set model to hypervisor type + if (manufacturer.includes('QEMU') || product.includes('KVM')) { + hardware.model = 'KVM'; + } else if (manufacturer.includes('VMware')) { + hardware.model = 'VMware'; + } else if (manufacturer.includes('innotek')) { + hardware.model = 'VirtualBox'; + } else if (manufacturer.includes('Microsoft')) { + hardware.model = 'Hyper-V'; + } else if (manufacturer.includes('Xen')) { + hardware.model = 'Xen'; + } else { + hardware.model = 'Unknown VM'; + } + + return hardware; + } catch (err) { + hardware.manufacturer = 'Unknown'; + hardware.model = 'Unknown VM'; + return hardware; + } + } + } catch (err) { + // Can't read cpuinfo + } + + // No hypervisor flag - likely physical, get DMI info + try { + const manufacturer = fs.readFileSync('/sys/class/dmi/id/sys_vendor', 'utf8').trim(); + const product = fs.readFileSync('/sys/class/dmi/id/product_name', 'utf8').trim(); + + hardware.type = 'hw'; // ✅ Changed from 'physical' + hardware.manufacturer = manufacturer; + hardware.model = product; + + return hardware; + } catch (err) { + // DMI not available + } + + return hardware; +} + function stopTelemetry() { if (telemetryTimer) { clearInterval(telemetryTimer);