diff --git a/README.md b/README.md
index b2f493a..439a3f7 100644
--- a/README.md
+++ b/README.md
@@ -132,7 +132,7 @@ git pull --rebase
source venv/bin/activate
pip install -r requirements.txt
deactivate
-sudo systemctl restart pve-ha-web
+systemctl restart pve-ha-web
```
---
@@ -140,10 +140,10 @@ sudo systemctl restart pve-ha-web
## Uninstall
```bash
-sudo systemctl disable --now pve-ha-web
-sudo rm -f /etc/systemd/system/pve-ha-web.service
-sudo systemctl daemon-reload
-sudo rm -rf /opt/pve-ha-web
+systemctl disable --now pve-ha-web
+rm -f /etc/systemd/system/pve-ha-web.service
+systemctl daemon-reload
+rm -rf /opt/pve-ha-web
```
---
diff --git a/static/main.js b/static/main.js
index 87b6d00..e6e3610 100644
--- a/static/main.js
+++ b/static/main.js
@@ -121,27 +121,6 @@ async function fetchNodeDetail(name) {
return await r.json();
}
-// ------ Services status ------
-function normSvcState(s) {
- const vals = [
- s.active, s['active-state'], s.ActiveState, s.activestate,
- s.SubState, s.substate,
- s.state, s.State,
- s.loadstate, s.LoadState,
- s.result, s.Result, s['exit-code']
- ]
- .filter(v => v !== undefined && v !== null && v !== '')
- .map(v => String(v).toLowerCase());
-
- const joined = ' ' + vals.join(' ') + ' ';
- if (/\b(failed|error|dead|auto-restart\b.*fail)\b/.test(joined)) return 'failed';
- if (/\b(active|running|up)\b/.test(joined)) return 'active';
- if (/\b(activating|starting|start-pre|reload)\b/.test(joined)) return 'activating';
- if (/\b(deactivating|stopping|stop-post)\b/.test(joined)) return 'deactivating';
- if (vals.length) return 'inactive';
- return 'inactive';
-}
-
// ------ VM detail card ------
function renderVmDetailCard(d) {
const meta = d.meta || {};
@@ -266,8 +245,7 @@ function renderVmDetailCard(d) {
Memory (used/free/total)
${(used != null && maxmem != null)
? `${humanBytes(used)} / ${humanBytes(free)} / ${humanBytes(maxmem)}`
- : '—'
- }
+ : '—'}
Ballooning: ${balloonEnabled ? badge('enabled', 'ok') : badge('disabled', 'err')}
${binfo ? `Balloon actual: ${humanBytes(binfo.actual)} | guest free: ${humanBytes(binfo.free_mem)} | guest total: ${humanBytes(binfo.total_mem)}
` : ''}
@@ -316,7 +294,6 @@ function renderNodeDetailCard(d) {
const st = d.status || {};
const ver = d.version || {};
const tm = d.time || {};
- const svcs = ensureArr(d.services);
const netcfg = ensureArr(d.network_cfg);
const disks = ensureArr(d.disks);
const sub = d.subscription || {};
@@ -334,6 +311,40 @@ function renderNodeDetailCard(d) {
const root = st.rootfs || {};
const load = st.loadavg || '';
+ // ---- SYSTEM DETAILS (zamiast "HA services")
+ const hw = d.hw || d.hardware || d.cpuinfo || st.cpuinfo || {};
+ const cpuModel = pick(
+ hw.model, hw['model-name'], hw.model_name,
+ st['cpu-model'], ver['cpu-model'], ver.cpu, st.cpu_model
+ );
+ const arch = pick(hw.arch, ver.arch, st.architecture, st.arch);
+ const sockets = pick(hw.sockets, st.sockets);
+ const coresPerSocket = pick(hw.cores, st.cores_per_socket, st.cores);
+ const threadsPerCore = pick(hw.threads, st.threads_per_core, st.threads);
+ const totalCPUs = pick(st.maxcpu, hw.total_cpus, hw.cpus);
+ const microcode = pick(hw.microcode, st.microcode);
+ const bios = pick(hw.bios, st.bios_version, ver.bios);
+ const virt = pick(hw.virtualization, ver.virtualization, st.virtualization);
+ const machine = pick(ver.machine, st.machine);
+ const kvm = pick(ver.kvm, st.kvm);
+
+ const tech = {
+ 'Kernel': pick(ver.kernel, ver.release),
+ 'QEMU': pick(ver.qemu, ver['running-qemu']),
+ 'KVM': kvm,
+ 'PVE Manager': pick(ver.pvemanager, ver['pve-manager']),
+ 'Architecture': arch,
+ 'CPU model': cpuModel,
+ 'Sockets': sockets,
+ 'Cores/socket': coresPerSocket,
+ 'Threads/core': threadsPerCore,
+ 'Total CPUs': totalCPUs,
+ 'Microcode': microcode,
+ 'Virtualization': virt,
+ 'Machine': machine,
+ 'BIOS': bios
+ };
+
const top = `
${safe(d.node)}
@@ -349,17 +360,15 @@ function renderNodeDetailCard(d) {
Memory
-
${(mem.used != null && mem.total != null) ? `${humanBytes(mem.used)} / ${humanBytes(mem.total)} (${pct(mem.used / mem.total)})` : '—'
- }
+
${(mem.used != null && mem.total != null) ? `${humanBytes(mem.used)} / ${humanBytes(mem.total)} (${pct(mem.used / mem.total)})` : '—'}
RootFS
-
${(root.used != null && root.total != null) ? `${humanBytes(root.used)} / ${humanBytes(root.total)} (${pct(root.used / root.total)})` : '—'
- }
+
${(root.used != null && root.total != null) ? `${humanBytes(root.used)} / ${humanBytes(root.total)} (${pct(root.used / root.total)})` : '—'}
Kernel / QEMU
-
${safe(ver.kernel || (ver['release'] || ''))} / ${safe(ver.qemu || ver['running-qemu'] || '')}
+
${safe(tech['Kernel'])} / ${safe(tech['QEMU'])}
Time
@@ -367,33 +376,13 @@ function renderNodeDetailCard(d) {
`;
- // --- NORMALIZE service names + badge
- const svcMap = {};
- svcs.forEach(s => {
- const raw = (s && s.name) ? s.name : '';
- const key = raw.replace(/\.service$/, ''); // normalize
- svcMap[key] = normSvcState(s);
+ // --- SYSTEM DETAILS GRID
+ const sysDetails = kvGrid(tech, Object.keys(tech), {
+ 'QEMU': 'QEMU version', 'KVM': 'KVM',
+ 'CPU model': 'CPU model', 'Architecture': 'Arch',
+ 'Cores/socket': 'Cores per socket', 'Threads/core': 'Threads per core',
+ 'Total CPUs': 'Total logical CPUs'
});
- function svcBadge(name) {
- const st = String(svcMap[name] || '').toLowerCase();
- if (st === 'active') return badge('active', 'ok');
- if (st === 'activating' || st === 'deactivating') return badge(st, 'warn');
- if (st === 'failed') return badge('failed', 'err');
- return badge('inactive', 'err');
- }
- const svcTable = `
-
-
- Service | State | Service | State | Service | State |
-
-
- watchdog-mux | ${svcBadge('watchdog-mux')} |
- pve-ha-crm | ${svcBadge('pve-ha-crm')} |
- pve-ha-lrm | ${svcBadge('pve-ha-lrm')} |
-
-
-
-
`;
// Network config
const netRows = netcfg.map(n => {
@@ -442,8 +431,8 @@ function renderNodeDetailCard(d) {
${memCard}
-
HA services
- ${svcTable}
+
System details
+ ${sysDetails}