{% extends "ui/_layout.html" %} {% block title %}Machines - bty-web{% endblock %} {% block subnav %} {% if active_filter %} {% set right_html %}filter: {{ active_filter }} show all{% endset %} {% else %} {% set right_html %}{{ machines|length }} total ยท live{% endset %} {% endif %} {% with sections=[ {"key": "list", "label": "List", "icon": "list-ul", "href": "/ui/machines"}, {"key": "add", "label": "Add by MAC", "icon": "plus-circle", "href": "/ui/machines?section=add"}, ] , active=section, right_html=right_html %} {% include "ui/_subnav.html" %} {% endwith %} {% endblock %} {% block intro %} {% from "ui/_intro_box.html" import render as intro_box %} {% call intro_box() %} The known fleet, keyed by MAC. Each row carries an optional image binding, target-disk pin, and boot policy. Machines auto-appear on first PXE contact; explicitly add via Add by MAC to stage an image + policy before the box has even booted. {% endcall %} {% endblock %} {% block content %} {% if section == "add" %}
Add machine
{# The form posts to /ui/machines/; the existing upsert handler creates the row with ``discovered_at=NULL`` if the MAC isn't known yet. Once the box PXE-boots, ``discovered_at`` flips to the first-seen timestamp and the boot_policy already configured here takes effect on that first ``GET /pxe/``. Lets operators stage a machine + its image binding before the box is even racked. #}
{# Pattern mirrors the server-side Pydantic regex on ``MachineUpsert.hostname``: each dot-separated label is alnum, hyphen-internal-only, no leading/trailing hyphen, no consecutive dots. Browser-side validation gives the operator an inline error before the form posts; without this match, the server returns 422 and the operator hits the form-flash redirect. #}
flash / flash-once need the target's disk inventory (posted by the box itself on first PXE contact) to pick a target_disk_serial; pick one of those on the machine's detail page after the inventory has landed. MAC is the only required field here. Image binding is set per-machine on the detail page (click the row); the boot policy defaults to "local" so a freshly added machine doesn't re-flash itself unexpectedly on first boot.
{% else %} {# section == "list" (default): the live table of all machines. This is what an operator wants to see when they click "Machines" in the top nav -- "show me what's there", not "give me the add form". The Add flow is one sub-nav click away. #}
{# The tbody subscribes to the SSE stream so it auto-updates when the operator (or auto-discovery) mutates machines. The browser carries the bty-token cookie automatically; that is what authenticates the EventSource connection. ``sse-target`` opts in to the layout-level flash animation. When a server-side filter is active (``?filter=...``) the SSE wiring is dropped: the next ``machines-update`` would replace the filtered tbody with the full un-filtered list, which is operator-confusing. The page becomes static under filter; reload to refresh. #} {% include "ui/_machines_tbody.html" %}
MAC Image Boot Hostname Last seen Last flashed
{% endif %} {% endblock %}