Add DHCP network boot management to API and UI
Enhance the dashboard API with new endpoints for managing DHCP network boot options, allowing devices to enable or disable network boot via POST requests. Update the device action handling to include a 'reboot' action, specifically for network devices. Modify the home.html template to display the current state of network boot and provide a button for disabling it. Update provisioning scripts to disable network boot after deployment or backup completion, ensuring devices boot from eMMC on the next startup. Improve user feedback and error handling throughout the changes.
This commit is contained in:
@@ -104,6 +104,7 @@
|
||||
|
||||
<div class="card">
|
||||
<h2 class="card-title">Capture or deploy</h2>
|
||||
<p id="dhcpNetbootWrap" class="status-row" style="margin-bottom: 0.5rem; font-size: 0.85rem;"><span class="text-dim">Network boot (DHCP):</span> <span id="dhcpNetbootState">—</span> <button type="button" id="dhcpNetbootDisableBtn" class="btn btn-outline btn-sm" style="display:none;">Disable network boot</button></p>
|
||||
<p id="shrinkOptionWrap" style="display:none; margin-bottom: 0.5rem; font-size: 0.8rem;"><label><input type="checkbox" id="shrinkAfterBackup" /> Shrink after backup</label></p>
|
||||
<div id="pendingDevices"></div>
|
||||
<p id="noPending" class="empty-msg" style="display:none;">No device connected. Use USB boot mode or register over network.</p>
|
||||
@@ -182,7 +183,7 @@
|
||||
hasAny = true;
|
||||
const el = document.createElement('div');
|
||||
el.className = 'device-item';
|
||||
el.innerHTML = '<div class="device-desc">' + escapeHtml(d.ip || '') + ' · ' + escapeHtml(d.mac || '') + '</div><div><button type="button" class="btn btn-outline btn-sm" data-source="network" data-mac="' + escapeHtml(d.mac || '') + '" data-action="backup">Backup</button> <button type="button" class="btn btn-primary btn-sm" data-source="network" data-mac="' + escapeHtml(d.mac || '') + '" data-action="deploy">Deploy</button></div>';
|
||||
el.innerHTML = '<div class="device-desc">' + escapeHtml(d.ip || '') + ' · ' + escapeHtml(d.mac || '') + '</div><div><button type="button" class="btn btn-outline btn-sm" data-source="network" data-mac="' + escapeHtml(d.mac || '') + '" data-action="backup">Backup</button> <button type="button" class="btn btn-primary btn-sm" data-source="network" data-mac="' + escapeHtml(d.mac || '') + '" data-action="deploy">Deploy</button> <button type="button" class="btn btn-outline btn-sm btn-disable-netboot" title="Stop advertising network boot via DHCP so devices boot from eMMC">Disable network boot</button></div>';
|
||||
container.appendChild(el);
|
||||
});
|
||||
noPending.style.display = hasAny ? 'none' : 'block';
|
||||
@@ -193,7 +194,15 @@
|
||||
if (body.action === 'backup' && document.getElementById('shrinkAfterBackup').checked) body.shrink = true;
|
||||
fetch('/api/device-action', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) { if (data.ok) { fetchPending(); fetchStatus(); } else alert(data.error || 'Failed'); })
|
||||
.then(function(data) { if (data.ok) { fetchPending(); fetchStatus(); fetchDhcpNetboot(); } else alert(data.error || 'Failed'); })
|
||||
.catch(function() { alert('Request failed'); });
|
||||
};
|
||||
});
|
||||
container.querySelectorAll('button.btn-disable-netboot').forEach(function(btn) {
|
||||
btn.onclick = function() {
|
||||
fetch('/api/dhcp-network-boot', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled: false }) })
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) { if (data.ok) { fetchDhcpNetboot(); } else alert(data.error || 'Failed'); })
|
||||
.catch(function() { alert('Request failed'); });
|
||||
};
|
||||
});
|
||||
@@ -213,12 +222,28 @@
|
||||
el.innerHTML = t;
|
||||
}).catch(function(){ document.getElementById('goldenInfo').textContent = 'Could not load.'; });
|
||||
}
|
||||
function fetchDhcpNetboot() {
|
||||
fetch('/api/dhcp-network-boot').then(function(r){ return r.json(); }).then(function(d){
|
||||
const stateEl = document.getElementById('dhcpNetbootState');
|
||||
const btn = document.getElementById('dhcpNetbootDisableBtn');
|
||||
if (d.error) { stateEl.textContent = '—'; if (btn) btn.style.display = 'none'; return; }
|
||||
stateEl.textContent = d.enabled ? 'on' : 'off';
|
||||
if (btn) btn.style.display = d.enabled ? 'inline-block' : 'none';
|
||||
}).catch(function(){ document.getElementById('dhcpNetbootState').textContent = '—'; });
|
||||
}
|
||||
document.getElementById('statusClearBtn').addEventListener('click', function(){ fetch('/api/status-clear', { method: 'POST' }).then(function(r){ return r.json(); }).then(function(d){ if(d.ok) fetchStatus(); }); });
|
||||
fetchStatus(); fetchLog(); fetchPending(); fetchGolden();
|
||||
document.getElementById('dhcpNetbootDisableBtn').addEventListener('click', function(){
|
||||
fetch('/api/dhcp-network-boot', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled: false }) })
|
||||
.then(function(r){ return r.json(); })
|
||||
.then(function(d){ if(d.ok) fetchDhcpNetboot(); else alert(d.error || 'Failed'); })
|
||||
.catch(function(){ alert('Request failed'); });
|
||||
});
|
||||
fetchStatus(); fetchLog(); fetchPending(); fetchGolden(); fetchDhcpNetboot();
|
||||
setInterval(fetchStatus, 2000);
|
||||
setInterval(fetchLog, 4000);
|
||||
setInterval(fetchPending, 2000);
|
||||
setInterval(fetchGolden, 10000);
|
||||
setInterval(fetchDhcpNetboot, 10000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user