Hardware coverage: flag models the server has no drivers or BIOS for

WinPE now reports each machine's WMI model, manufacturer, Dell system
SKU and BIOS version to /hardware/seen (winpe-status-push.ps1, best
effort, wmic only). The webapp records every model seen and checks it
against what the server has:
  - each image's Deploy/Control/HardwareDriver.json, matched exactly as
    PESetup's GetDriverByModel does (as reimplemented in
    lint-driver-catalogue.py), plus whether the pack's zip is on disk;
  - the WinPE BIOS manifest (substring match like check-bios.cmd).

/hardware lists every model with per-image driver status and BIOS
status. A model missing a Win11 pack or a BIOS entry becomes item 5 on
the Technician To-Do (and the banner count) until the pack/BIOS is added
or it is dismissed. Virtual machines are ignored.

Also deploys the repo winpe-status-push.ps1 over the live copy, which
predated the -CurrentStage/-StageIndex parameters startnet.cmd passes
for the BIOS-flash stage, so that status post was failing.
This commit is contained in:
cproudlock
2026-10-09 09:23:52 -04:00
parent e43ab2a652
commit b691a82530
8 changed files with 485 additions and 1 deletions

View File

@@ -117,4 +117,57 @@
<p class="text-muted mb-0">Every report with a known upload status reached the cloud.</p>
{% endif %}
</div>
{# 5. Hardware the server cannot image yet #}
<div class="section-card">
<div class="section-title">5. Add drivers / BIOS for new hardware</div>
{% if hardware %}
<p class="mb-2">These models booted WinPE here but the server has no Win11 driver pack or no BIOS entry for them.
Download the Dell driver pack and BIOS for the exact model below, add them, and they drop off this list.
<a href="{{ url_for('hardware_page') }}">Hardware coverage</a> shows every model.</p>
<div class="table-container">
<table class="data-table">
<thead><tr><th>Model (WMI)</th><th>System SKU</th><th>BIOS seen</th><th>Missing</th><th>Last seen</th><th class="text-end">Actions</th></tr></thead>
<tbody>
{% for r in hardware %}
<tr title="Serials: {{ r.serials | join(', ') }}">
<td class="mono">{{ r.model }}</td>
<td class="mono">{{ r.skus | join(', ') or '-' }}</td>
<td class="mono">{{ r.last_bios_version or '-' }}</td>
<td class="small wrap">
{% if r.missing %}Driver pack: {{ r.missing | join(', ') }}{% endif %}
{% if r.bios.status in ('none', 'file-missing') %}{% if r.missing %}<br>{% endif %}BIOS: {{ 'no manifest entry' if r.bios.status == 'none' else r.bios.file ~ ' not on server' }}{% endif %}
</td>
<td class="mono text-nowrap">{{ r.last_seen | local_dt }}</td>
<td class="text-end text-nowrap">
<button type="button" class="btn btn-sm btn-outline-secondary btn-row-action copy-btn" data-copy-text="{{ r.model }}">copy model</button>
<form method="post" action="{{ url_for('hardware_dismiss') }}" style="display:inline;">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="model" value="{{ r.model }}">
<input type="hidden" name="next" value="{{ url_for('blancco_todo') }}">
<button type="submit" class="pxe-btn pxe-btn-ghost btn-row-action">Dismiss</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-muted mb-0">Every model seen has a Win11 driver pack and a BIOS entry.</p>
{% endif %}
</div>
{% endblock %}
{% block extra_scripts %}
<script>
document.querySelectorAll('.copy-btn').forEach(function (b) {
b.addEventListener('click', function () {
var t = b.getAttribute('data-copy-text') || '';
var done = function () { var o = b.textContent; b.textContent = 'copied'; setTimeout(function () { b.textContent = o; }, 1200); };
if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(t).then(done); return; }
var ta = document.createElement('textarea'); ta.value = t; document.body.appendChild(ta); ta.select();
try { document.execCommand('copy'); done(); } finally { document.body.removeChild(ta); }
});
});
</script>
{% endblock %}