Files
pxe-server/webapp/templates/hardware.html
cproudlock b691a82530 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.
2026-10-09 09:23:52 -04:00

82 lines
3.8 KiB
HTML

{% extends "base.html" %}
{% block title %}Hardware Coverage - PXE Server Manager{% endblock %}
{% macro driver_badge(d) -%}
{% if d.status == 'ok' %}<span class="badge badge-success" title="{{ d.file }}">OK</span>
{% elif d.status == 'no-entry' %}<span class="badge badge-danger" title="No HardwareDriver.json entry matches this model">Missing</span>
{% elif d.status == 'zip-missing' %}<span class="badge badge-danger" title="Entry {{ d.file }} matches, but the zip is not on the server">Zip missing</span>
{% else %}<span class="text-muted">-</span>{% endif %}
{%- endmacro %}
{% macro bios_badge(b) -%}
{% if b.status == 'current' %}<span class="badge badge-success">{{ b.target }} (current)</span>
{% elif b.status == 'update' %}<span class="badge badge-info" title="{{ b.file }}">Flashes to {{ b.target }}</span>
{% elif b.status == 'file-missing' %}<span class="badge badge-danger" title="{{ b.file }} not in the BIOS folder">File missing</span>
{% else %}<span class="badge badge-danger">No BIOS entry</span>{% endif %}
{%- endmacro %}
{% block content %}
<div class="page-header">
<h1><i class="bi bi-cpu"></i> Hardware Coverage</h1>
<div class="header-actions">
<span class="badge badge-secondary badge-lg">{{ rows | length }} model{{ 's' if rows | length != 1 }} seen</span>
</div>
</div>
<p class="text-muted">
Every model that has booted WinPE here, as reported by the machine itself (WMI model, Dell system SKU,
BIOS version), checked against each image's driver map and the WinPE BIOS manifest. A model missing a
Win11 driver pack or a BIOS entry also shows on the Technician To-Do until it is added or dismissed.
</p>
<div class="section-card">
{% if rows %}
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>Model (WMI)</th>
<th>System SKU</th>
<th>BIOS seen</th>
{% for image, os_label in images %}<th title="{{ image }}">{{ image | replace('gea-', '') }} <span class="text-muted">({{ os_label }})</span></th>{% endfor %}
<th>BIOS update</th>
<th>Last seen</th>
<th class="text-end"></th>
</tr>
</thead>
<tbody>
{% for r in rows %}
<tr title="Serials: {{ r.serials | join(', ') }}{% if r.pctypes %} | Types: {{ r.pctypes | join(', ') }}{% endif %}">
<td>
<span class="mono">{{ r.model }}</span>
{% if r.manufacturer %}<div class="small text-muted">{{ r.manufacturer }}</div>{% endif %}
</td>
<td class="mono">{{ r.skus | join(', ') or '-' }}</td>
<td class="mono">{{ r.last_bios_version or '-' }}</td>
{% for image, os_label in images %}<td>{{ driver_badge(r.drivers[image]) }}</td>{% endfor %}
<td>{{ bios_badge(r.bios) }}</td>
<td class="mono text-nowrap">{{ r.last_seen | local_dt }}<div class="small text-muted">{{ r.count }} boot{{ 's' if r.count != 1 }}</div></td>
<td class="text-end text-nowrap">
{% if r.gap %}
<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="done" value="{{ '0' if r.dismissed else '1' }}">
<button type="submit" class="pxe-btn pxe-btn-ghost btn-row-action">{{ 'Reopen' if r.dismissed else 'Dismiss' }}</button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center text-muted py-5">
<p class="mb-1">No machines have reported yet.</p>
<p class="small">Models appear here the next time a machine boots WinPE from this server.</p>
</div>
{% endif %}
</div>
{% endblock %}