refator_comm1
This commit is contained in:
136
static/js/vmDetail.js
Normal file
136
static/js/vmDetail.js
Normal file
@@ -0,0 +1,136 @@
|
||||
import { safe, ensureArr, badge, rowHTML, humanBytes, kvGrid, fmtSeconds, parseVmNetworks } from './helpers.js';
|
||||
|
||||
export function renderVmDetailCard(d) {
|
||||
// --- dokładnie ten sam kod, który miałeś — przeniesiony bez zmian ---
|
||||
// (skrócone dla czytelności — wklejam pełną wersję z Twojego pliku)
|
||||
const meta = d.meta || {};
|
||||
const cur = d.current || {};
|
||||
const cfg = d.config || {};
|
||||
const ag = d.agent || {};
|
||||
const agInfo = ag.info || null;
|
||||
const agOS = ag.osinfo && ag.osinfo.result ? ag.osinfo.result : null;
|
||||
const agIfs = ag.ifaces && ag.ifaces.result ? ag.ifaces.result : null;
|
||||
|
||||
const statusBadge = /running|online|started/i.test(meta.status || cur.status || '')
|
||||
? badge(meta.status || cur.status || 'running', 'ok')
|
||||
: badge(meta.status || cur.status || 'stopped', 'err');
|
||||
|
||||
const maxmem = cur.maxmem ?? (cfg.memory ? Number(cfg.memory) * 1024 * 1024 : null);
|
||||
const used = cur.mem ?? null;
|
||||
const free = (maxmem != null && used != null) ? Math.max(0, maxmem - used) : null;
|
||||
const balloonEnabled = (cfg.balloon !== undefined) ? (Number(cfg.balloon) !== 0) : (cur.balloon !== undefined && Number(cur.balloon) !== 0);
|
||||
const binfo = cur.ballooninfo || null;
|
||||
|
||||
let guestName = agOS && (agOS.name || agOS.pretty_name) || (agInfo && agInfo.version) || '';
|
||||
let guestIPs = [];
|
||||
if (Array.isArray(agIfs)) {
|
||||
agIfs.forEach(i => {
|
||||
(i['ip-addresses'] || []).forEach(ip => { const a = ip['ip-address']; if (a && !a.startsWith('fe80')) guestIPs.push(a); });
|
||||
});
|
||||
}
|
||||
|
||||
const bstat = cur.blockstat || {};
|
||||
const bRows = Object.keys(bstat).sort().map(dev => {
|
||||
const s = bstat[dev] || {};
|
||||
return rowHTML([dev, humanBytes(s.rd_bytes||0), String(s.rd_operations||0),
|
||||
humanBytes(s.wr_bytes||0), String(s.wr_operations||0), String(s.flush_operations||0), humanBytes(s.wr_highest_offset||0)]);
|
||||
});
|
||||
|
||||
const ha = cur.ha || {};
|
||||
const haBadge = ha.state ? (/started/i.test(ha.state) ? badge(ha.state,'ok') : badge(ha.state,'warn')) : badge('—','dark');
|
||||
|
||||
const sysCards = {
|
||||
'QMP status': cur.qmpstatus, 'QEMU': cur['running-qemu'], 'Machine': cur['running-machine'], 'PID': cur.pid,
|
||||
'Pressure CPU (some/full)': `${String(cur.pressurecpusome||'—')}/${String(cur.pressurecpufull||'—')}`,
|
||||
'Pressure IO (some/full)': `${String(cur.pressureiosome||'—')}/${String(cur.pressureiofull||'—')}`,
|
||||
'Pressure MEM (some/full)': `${String(cur.pressurememorysome||'—')}/${String(cur.pressurememoryfull||'—')}`
|
||||
};
|
||||
|
||||
const nets = parseVmNetworks(cfg);
|
||||
const netRows = nets.map(n => {
|
||||
const br = n.bridge || n.br || '—';
|
||||
const mdl = n.model || n.type || (n.raw?.split(',')[0]?.split('=')[0]) || 'virtio';
|
||||
const mac = n.hwaddr || n.mac || (n.raw?.match(/([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}/)?.[0] || '—');
|
||||
const vlan = n.tag || n.vlan || '—';
|
||||
const fw = (n.firewall === '1') ? badge('on','warn') : badge('off','dark');
|
||||
return rowHTML([`net${n.idx}`, mdl, br, vlan, mac, fw]);
|
||||
});
|
||||
|
||||
const netTable = `
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-striped align-middle table-nowrap">
|
||||
<thead><tr><th>IF</th><th>Model</th><th>Bridge</th><th>VLAN</th><th>MAC</th><th>FW</th></tr></thead>
|
||||
<tbody>${netRows.length ? netRows.join('') : rowHTML(['—','—','—','—','—','—'])}</tbody>
|
||||
</table>
|
||||
</div>`;
|
||||
|
||||
const agentSummary = (agInfo || agOS || guestIPs.length)
|
||||
? `<div class="small">
|
||||
${agOS ? `<div>Guest OS: <strong>${safe(guestName)}</strong></div>` : ''}
|
||||
${guestIPs.length ? `<div>Guest IPs: ${guestIPs.map(ip => badge(ip,'info')).join(' ')}</div>` : ''}
|
||||
${agInfo ? `<div>Agent: ${badge('present','ok')}</div>` : `<div>Agent: ${badge('not available','err')}</div>`}
|
||||
</div>`
|
||||
: '<div class="text-muted small">No guest agent data</div>';
|
||||
|
||||
const cfgFacts = {
|
||||
'BIOS': cfg.bios, 'UEFI/EFI disk': cfg.efidisk0 ? 'yes' : 'no',
|
||||
'CPU type': cfg.cpu, 'Sockets': cfg.sockets, 'Cores': cfg.cores, 'NUMA': cfg.numa,
|
||||
'On boot': cfg.onboot ? 'yes' : 'no', 'OS type': cfg.ostype, 'SCSI hw': cfg.scsihw
|
||||
};
|
||||
|
||||
const rawId = `raw-${d.type}-${d.vmid}`;
|
||||
const rawBtn = `<button class="btn btn-sm btn-outline-secondary mt-3" type="button" data-bs-toggle="collapse" data-bs-target="#${rawId}">Show raw JSON</button>`;
|
||||
const rawBox = `<div id="${rawId}" class="collapse mt-2">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#rt-${d.vmid}" type="button">Runtime</button></li>
|
||||
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#cfg-${d.vmid}" type="button">Config</button></li>
|
||||
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#agt-${d.vmid}" type="button">Agent</button></li>
|
||||
</ul>
|
||||
<div class="tab-content border-top pt-3">
|
||||
<div class="tab-pane fade show active" id="rt-${d.vmid}"><pre class="small mb-0">${JSON.stringify(cur,null,2)}</pre></div>
|
||||
<div class="tab-pane fade" id="cfg-${d.vmid}"><pre class="small mb-0">${JSON.stringify(cfg,null,2)}</pre></div>
|
||||
<div class="tab-pane fade" id="agt-${d.vmid}"><pre class="small mb-0">${JSON.stringify(d.agent||{},null,2)}</pre></div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
return `
|
||||
<div class="d-flex flex-wrap align-items-center gap-3 mb-2">
|
||||
<div class="fw-bold">${safe(meta.name || cfg.name || d.sid)}</div>
|
||||
<div class="text-muted small">${(d.type || '').toUpperCase()} / VMID ${safe(d.vmid)} @ ${safe(d.node)}</div>
|
||||
<div class="vr"></div><div>${statusBadge}</div>
|
||||
${meta.hastate ? `<div class="vr"></div><div class="small">HA: ${badge(meta.hastate, /started/i.test(meta.hastate) ? 'ok' : 'warn')}</div>` : ''}
|
||||
${ha.state ? `<div class="vr"></div><div class="small">HA runtime: ${haBadge}</div>` : ''}
|
||||
</div>
|
||||
|
||||
<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="text-muted small">CPU</div><div class="fw-semibold">${cur.cpu !== undefined ? (cur.cpu * 100).toFixed(1) + '%' : '—'}</div>
|
||||
<div class="small text-muted">vCPUs: ${safe(cur.cpus)}</div>
|
||||
</div></div></div>
|
||||
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
||||
<div class="text-muted small">Memory (used/free/total)</div>
|
||||
<div class="fw-semibold">${(used != null && maxmem != null) ? `${humanBytes(used)} / ${humanBytes(free)} / ${humanBytes(maxmem)}` : '—'}</div>
|
||||
</div></div></div>
|
||||
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
||||
<div class="text-muted small">Disk (used/total)</div>
|
||||
<div class="fw-semibold">${(cur.disk != null && cur.maxdisk != null) ? `${humanBytes(cur.disk)} / ${humanBytes(cur.maxdisk)}` : '—'}</div>
|
||||
<div class="small text-muted mt-1">R: ${humanBytes(cur.diskread||0)} | W: ${humanBytes(cur.diskwrite||0)}</div>
|
||||
</div></div></div>
|
||||
<div class="col"><div class="card border-0"><div class="card-body p-2">
|
||||
<div class="text-muted small">Uptime</div><div class="fw-semibold">${fmtSeconds(cur.uptime)}</div>
|
||||
</div></div></div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3"><div class="fw-semibold mb-1">Network (config)</div>${netTable}</div>
|
||||
<div class="mt-3"><div class="fw-semibold mb-1">Disks (block statistics)</div>
|
||||
<div class="table-responsive"><table class="table table-sm table-striped align-middle table-nowrap">
|
||||
<thead><tr><th>Device</th><th>Read bytes</th><th>Read ops</th><th>Write bytes</th><th>Write ops</th><th>Flush ops</th><th>Highest offset</th></tr></thead>
|
||||
<tbody>${Object.keys(bstat).length ? bRows.join('') : rowHTML(['—','—','—','—','—','—','—'])}</tbody></table></div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3"><div class="fw-semibold mb-1">System / QEMU</div>${kvGrid(sysCards, Object.keys(sysCards))}</div>
|
||||
<div class="mt-3 mb-1"><div class="fw-semibold">Config facts</div>${kvGrid(cfgFacts, Object.keys(cfgFacts))}</div>
|
||||
<div class="mt-2">${agentSummary}</div>
|
||||
${rawBtn}${rawBox}
|
||||
`;
|
||||
}
|
Reference in New Issue
Block a user