changes
This commit is contained in:
10
README.md
10
README.md
@@ -132,7 +132,7 @@ git pull --rebase
|
|||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
deactivate
|
deactivate
|
||||||
sudo systemctl restart pve-ha-web
|
systemctl restart pve-ha-web
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -140,10 +140,10 @@ sudo systemctl restart pve-ha-web
|
|||||||
## Uninstall
|
## Uninstall
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl disable --now pve-ha-web
|
systemctl disable --now pve-ha-web
|
||||||
sudo rm -f /etc/systemd/system/pve-ha-web.service
|
rm -f /etc/systemd/system/pve-ha-web.service
|
||||||
sudo systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
sudo rm -rf /opt/pve-ha-web
|
rm -rf /opt/pve-ha-web
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
103
static/main.js
103
static/main.js
@@ -121,27 +121,6 @@ async function fetchNodeDetail(name) {
|
|||||||
return await r.json();
|
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 ------
|
// ------ VM detail card ------
|
||||||
function renderVmDetailCard(d) {
|
function renderVmDetailCard(d) {
|
||||||
const meta = d.meta || {};
|
const meta = d.meta || {};
|
||||||
@@ -266,8 +245,7 @@ function renderVmDetailCard(d) {
|
|||||||
<div class="text-muted small">Memory (used/free/total)</div>
|
<div class="text-muted small">Memory (used/free/total)</div>
|
||||||
<div class="fw-semibold">${(used != null && maxmem != null)
|
<div class="fw-semibold">${(used != null && maxmem != null)
|
||||||
? `${humanBytes(used)} / ${humanBytes(free)} / ${humanBytes(maxmem)}`
|
? `${humanBytes(used)} / ${humanBytes(free)} / ${humanBytes(maxmem)}`
|
||||||
: '—'
|
: '—'}</div>
|
||||||
}</div>
|
|
||||||
<div class="small mt-1">Ballooning: ${balloonEnabled ? badge('enabled', 'ok') : badge('disabled', 'err')}</div>
|
<div class="small mt-1">Ballooning: ${balloonEnabled ? badge('enabled', 'ok') : badge('disabled', 'err')}</div>
|
||||||
${binfo ? `<div class="small text-muted mt-1">Balloon actual: ${humanBytes(binfo.actual)} | guest free: ${humanBytes(binfo.free_mem)} | guest total: ${humanBytes(binfo.total_mem)}</div>` : ''}
|
${binfo ? `<div class="small text-muted mt-1">Balloon actual: ${humanBytes(binfo.actual)} | guest free: ${humanBytes(binfo.free_mem)} | guest total: ${humanBytes(binfo.total_mem)}</div>` : ''}
|
||||||
</div></div></div>
|
</div></div></div>
|
||||||
@@ -316,7 +294,6 @@ function renderNodeDetailCard(d) {
|
|||||||
const st = d.status || {};
|
const st = d.status || {};
|
||||||
const ver = d.version || {};
|
const ver = d.version || {};
|
||||||
const tm = d.time || {};
|
const tm = d.time || {};
|
||||||
const svcs = ensureArr(d.services);
|
|
||||||
const netcfg = ensureArr(d.network_cfg);
|
const netcfg = ensureArr(d.network_cfg);
|
||||||
const disks = ensureArr(d.disks);
|
const disks = ensureArr(d.disks);
|
||||||
const sub = d.subscription || {};
|
const sub = d.subscription || {};
|
||||||
@@ -334,6 +311,40 @@ function renderNodeDetailCard(d) {
|
|||||||
const root = st.rootfs || {};
|
const root = st.rootfs || {};
|
||||||
const load = st.loadavg || '';
|
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 = `
|
const top = `
|
||||||
<div class="d-flex flex-wrap align-items-center gap-3 mb-2">
|
<div class="d-flex flex-wrap align-items-center gap-3 mb-2">
|
||||||
<div class="fw-bold">${safe(d.node)}</div>
|
<div class="fw-bold">${safe(d.node)}</div>
|
||||||
@@ -349,17 +360,15 @@ function renderNodeDetailCard(d) {
|
|||||||
<div class="row row-cols-2 row-cols-md-4 g-2">
|
<div class="row row-cols-2 row-cols-md-4 g-2">
|
||||||
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
||||||
<div class="text-muted small">Memory</div>
|
<div class="text-muted small">Memory</div>
|
||||||
<div class="fw-semibold">${(mem.used != null && mem.total != null) ? `${humanBytes(mem.used)} / ${humanBytes(mem.total)} (${pct(mem.used / mem.total)})` : '—'
|
<div class="fw-semibold">${(mem.used != null && mem.total != null) ? `${humanBytes(mem.used)} / ${humanBytes(mem.total)} (${pct(mem.used / mem.total)})` : '—'}</div>
|
||||||
}</div>
|
|
||||||
</div></div></div>
|
</div></div></div>
|
||||||
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
||||||
<div class="text-muted small">RootFS</div>
|
<div class="text-muted small">RootFS</div>
|
||||||
<div class="fw-semibold">${(root.used != null && root.total != null) ? `${humanBytes(root.used)} / ${humanBytes(root.total)} (${pct(root.used / root.total)})` : '—'
|
<div class="fw-semibold">${(root.used != null && root.total != null) ? `${humanBytes(root.used)} / ${humanBytes(root.total)} (${pct(root.used / root.total)})` : '—'}</div>
|
||||||
}</div>
|
|
||||||
</div></div></div>
|
</div></div></div>
|
||||||
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
||||||
<div class="text-muted small">Kernel / QEMU</div>
|
<div class="text-muted small">Kernel / QEMU</div>
|
||||||
<div class="fw-semibold">${safe(ver.kernel || (ver['release'] || ''))} / ${safe(ver.qemu || ver['running-qemu'] || '')}</div>
|
<div class="fw-semibold">${safe(tech['Kernel'])} / ${safe(tech['QEMU'])}</div>
|
||||||
</div></div></div>
|
</div></div></div>
|
||||||
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
||||||
<div class="text-muted small">Time</div>
|
<div class="text-muted small">Time</div>
|
||||||
@@ -367,33 +376,13 @@ function renderNodeDetailCard(d) {
|
|||||||
</div></div></div>
|
</div></div></div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
// --- NORMALIZE service names + badge
|
// --- SYSTEM DETAILS GRID
|
||||||
const svcMap = {};
|
const sysDetails = kvGrid(tech, Object.keys(tech), {
|
||||||
svcs.forEach(s => {
|
'QEMU': 'QEMU version', 'KVM': 'KVM',
|
||||||
const raw = (s && s.name) ? s.name : '';
|
'CPU model': 'CPU model', 'Architecture': 'Arch',
|
||||||
const key = raw.replace(/\.service$/, ''); // normalize
|
'Cores/socket': 'Cores per socket', 'Threads/core': 'Threads per core',
|
||||||
svcMap[key] = normSvcState(s);
|
'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 = `
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-sm table-striped align-middle table-nowrap">
|
|
||||||
<thead><tr><th>Service</th><th>State</th><th>Service</th><th>State</th><th>Service</th><th>State</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><strong>watchdog-mux</strong></td><td>${svcBadge('watchdog-mux')}</td>
|
|
||||||
<td><strong>pve-ha-crm</strong></td><td>${svcBadge('pve-ha-crm')}</td>
|
|
||||||
<td><strong>pve-ha-lrm</strong></td><td>${svcBadge('pve-ha-lrm')}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
// Network config
|
// Network config
|
||||||
const netRows = netcfg.map(n => {
|
const netRows = netcfg.map(n => {
|
||||||
@@ -442,8 +431,8 @@ function renderNodeDetailCard(d) {
|
|||||||
${memCard}
|
${memCard}
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<div class="fw-semibold mb-1">HA services</div>
|
<div class="fw-semibold mb-1">System details</div>
|
||||||
${svcTable}
|
${sysDetails}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
|
Reference in New Issue
Block a user