vm management

This commit is contained in:
Mateusz Gruszczyński
2025-10-17 16:27:03 +02:00
parent b9586a3517
commit d53860a9c3
2 changed files with 23 additions and 3 deletions

View File

@@ -694,7 +694,7 @@ async function renderVMAdmin() {
const sel = `<select class="form-select form-select-sm target-node" style="min-width:140px">
${nodes.map(n => `<option value="${n}" ${n === x.node ? 'disabled' : ''}>${n}${n === x.node ? ' (src)' : ''}</option>`).join('')}
</select>`;
const migrateBtn = `<button class="btn btn-outline-primary btn-sm act-migrate">Migrate (offline)</button>`;
const migrateBtn = `<div class="d-flex align-items-center gap-2"><button class="btn btn-outline-primary btn-sm act-migrate">Migrate (offline)</button><button class="btn btn-outline-secondary btn-sm act-status">Status</button></div>`;
return rowHTML([sid, type.toUpperCase(), name, node, st, actions, sel, migrateBtn], `data-sid="${sid}"`);
});
@@ -743,6 +743,8 @@ async function renderVMAdmin() {
log.textContent += (log.textContent ? '\n' : '') + (ok ? 'Migration finished successfully.' : ('Migration failed: ' + (exit || 'unknown error')));
setRowBusy(tr, false);
await doRefresh();
// auto-collapse on success after 2s
if (ok) { setTimeout(() => { const row = tr.nextElementSibling; if (row && row.classList.contains('mig-row')) setMigRowVisible(row, false); }, 2000); }
});
} else {
await vmAction(sid, action, needsTarget ? getTarget() : undefined);
@@ -762,6 +764,13 @@ async function renderVMAdmin() {
bind('.act-stop', 'stop');
bind('.act-shutdown', 'shutdown');
bind('.act-migrate', 'migrate', true);
const statusBtn = tr.querySelector('.act-status');
if (statusBtn) {
statusBtn.onclick = () => {
const row = ensureMigRow(tr, colSpan);
setMigRowVisible(row, row.classList.contains('d-none'));
};
}
});
}
@@ -860,6 +869,7 @@ async function tailTaskLog(upid, node, preEl, stopSignal) {
const append = (txt) => {
if (!preEl) return;
preEl.textContent = (preEl.textContent ? preEl.textContent + '\n' : '') + txt;
trimLogEl(preEl);
preEl.scrollTop = preEl.scrollHeight;
};
while (!stopSignal.done) {
@@ -878,4 +888,14 @@ async function tailTaskLog(upid, node, preEl, stopSignal) {
}
await delay(1500);
}
}
const LOG_MAX_CHARS = 64 * 1024; // 64KB cap for log panel
function trimLogEl(preEl) {
if (!preEl) return;
const txt = preEl.textContent || '';
if (txt.length > LOG_MAX_CHARS) {
preEl.textContent = txt.slice(-LOG_MAX_CHARS);
}
}