{% extends "layout.html" %} {% from "_partials/page_description.html" import page_description %} {% block title %}{{ machine.mac }} - pixie{% endblock %} {% block subnav %} {% endblock %} {% block content %} {{ page_description('pc-display-horizontal', 'Everything pixie knows about one target: telemetry, current binding, inventory the live env posts back, and events scoped to this MAC. Editing the binding changes what pixie serves on this target\'s next PXE; labels and inventory are stored independently of the binding.') }} {# Bind failure surfaced from POST /ui/machines/bind (e.g. an overlay alias held single-writer by another machine). The message is the caught exception text, passed back via the ?bind_error= redirect; Jinja auto-escapes it. Nothing rendered on the happy path. #} {% if bind_error %} {% endif %}

{{ machine.mac }}

{% if machine.boot_mode == 'nbdboot' %} nbdboot {% else %} {{ machine.boot_mode }} {% endif %}
Image content sha
{% if machine.image_content_sha256 %} {{ machine.image_content_sha256 }} {% else %} (unbound) {% endif %}
Labels
{# Labels are metadata (search / filter / grouping) and have no effect on the plan render. Edit them here on their own form; the bind form below does NOT touch labels, so a boot-mode flip cannot clobber tags an operator set up ahead of time. #}
Comma-separated. Alnum-leading; a-z / 0-9 / space / . _ -; max 64 chars per tag, 16 tags per machine. Blank clears.
Discovered at
{{ machine.discovered_at | fmt_ts }}
Last seen
{{ machine.last_seen_at | fmt_ts }}
Last seen IP
{% if machine.last_seen_ip %}{{ machine.last_seen_ip }} {% else %}-{% endif %}

Edit binding

Unsaved changes
{# ---- Boot-mode radio-card picker ------------------------ Six cards in a 3-across grid. The underlying ```` is visually hidden but keeps the form serialisation + keyboard accessibility that a plain click-a-card widget would drop. The label wraps both the radio and the visual card so anywhere on the card selects the mode. #}
{% for mode_value, meta in boot_mode_meta %}
{% endfor %}
{# ---- Image picker --------------------------------- #}
{% set entry_shas = bindable_entries | map(attribute='content_sha256') | list %} {% if not bindable_entries %} {% endif %}
{# ---- Target disk picker --------------------------- #}
{% set disks = (machine.inventory or {}).get("disks") or [] %} {% if disks %}
The live env matches this SERIAL against the current disks at flash time; a swapped or renumbered drive refuses the flash rather than risking the wrong disk.
{% else %}
No inventory on file yet. Pick Discover inventory above, power-cycle this target, and its disks will populate here.
{% endif %}
{# ---- Per-machine extra kernel cmdline ------------------- Overrides the deploy-wide PIXIE_LIVE_ENV_EXTRA_CMDLINE for THIS machine. Blank falls back to the global; a non-blank value fully replaces (does not concatenate) so an operator can pin a hardware quirk to one target without every other machine seeing the same tokens. Consumed by the nbdboot + pixie-live-env templates. #}
Tokens appended verbatim to the kernel cmdline for this machine only. A non-blank value fully overrides the deploy-wide PIXIE_LIVE_ENV_EXTRA_CMDLINE. Blank falls back to the global (currently effective: {% if global_live_env_extra_cmdline %} {{ global_live_env_extra_cmdline }} {% else %}none{% endif %}). Whitespace-separated; single line. Known-good values per hardware in docs/hardware-quirks.md.
{# ---- Overlay alias (nbdboot only) ---------------------- An alias names a globally-unique writable qcow2 volume over one base image; picking one flips this machine from ephemeral (writes vanish on reboot) to persistent (writes land on the qcow2 and survive). Attaching an existing alias IMPLIES its base image. Aliases free or held by THIS machine are selectable; an alias held by ANOTHER machine is single-writer-locked and shown disabled (with the holder MAC) so the operator sees it exists but is in use. ``__new`` reveals a text input for a fresh alias name (created over the image selected above); the bind handler merges the two fields into a single ``overlay_alias`` value. #}
{% if machine.overlay_alias %} {% endif %}
Blank = ephemeral tmpfs (writes vanish on reboot; original nbdboot behaviour). Named = a globally-unique qcow2 kept at data/overlays/<alias>.qcow2 on pixie's data volume with backing_file=<base blob>. An alias is single-writer: at most one machine holds it at a time, and attaching it binds this machine to the alias's base image. Manage every overlay on the Overlays page. See boot-modes docs.
{# ---- Preview panel -------------------------------------- Plain-English description of what will happen on the next PXE from this target. Updated by JS on any input change. Sits ABOVE the flash-guard hint so an operator scanning the form always knows what "Save" will do before they're asked whether they can Save. #}
On the next PXE from this target
{{ initial_bind_preview }}
{# ---- Flash-mode guard hint ------------------------------ Client-side echo of the server-side 422 guard: the JS disables Save + surfaces this hint when the picked boot_mode is pixie-flash-{once,always} AND either no inventory is on file OR no target_disk_serial is picked. The server rejects the same combination regardless. #}
No changes to save.

Inventory

{% if machine.inventory %} Recorded at {{ machine.inventory_at | fmt_ts }} {% endif %}

Posted by the live env's pixie CLI on boot (lsblk + lshw); best-effort render, raw JSON below.

{% if machine.inventory %} {# Best-effort render of the machine hardware summary. The raw payload posted by the live env's ``pixie`` CLI is ``{"lshw": , "disks": []}``; the ``inv`` context var (built by ``pixie.web._inventory.normalise_inventory`` at render time) walks the lshw tree into the system/cpu/memory/nics shape rendered below. Each block is guarded so a partial payload still renders the sections that DID come through. Raw lshw JSON sits collapsed at the bottom as the always-available fallback for anything the summary render doesn't cover. #} {# ---- Hardware summary (compact) ------------------------ System + CPU + memory folded into one dense definition grid instead of three stacked full-width cards with oversized stat numbers. One scannable line per subsystem; the per-DIMM slot map is kept (it reads at a glance) and every number that used to sit in a display-6 block now lives inline or in the slot tooltips. #} {% set system = inv.system %} {% set cpu = inv.cpu %} {% set memory = inv.memory %} {% if system or cpu.sockets or memory.total_bytes %}
{% if system %}
System
{% if system.get("vendor") or system.get("model") %} {{ system.get("vendor") }} {{ system.get("model") }} {% endif %} {% if system.get("serial") %} · SN {{ system.get("serial") }}{% endif %} {% if system.get("firmware") %} · BIOS {{ system.get("firmware") }}{% if system.get("firmware_vendor") %} ({{ system.get("firmware_vendor") }}){% endif %}{% endif %}
{% endif %} {% if cpu.sockets %}
CPU
{% for socket in cpu.sockets %} {{ socket.model or "Unknown CPU" }} {% if socket.arch %}{{ socket.arch }}{% endif %} {% if cpu.sockets|length > 1 %}Socket {{ socket.slot or loop.index0 }}{% endif %} — {{ socket.cores or "?" }} cores / {{ socket.threads or "?" }} threads{% if socket.mhz_current %} @ {{ socket.mhz_current | humanize_hz }}{% endif %}{% if socket.mhz_max %} (max {{ socket.mhz_max | humanize_hz }}){% endif %}{% if socket.vendor %} · {{ socket.vendor }}{% endif %}
{% endfor %} {% if cpu.sockets|length > 1 and cpu.total_cores %}
{{ cpu.sockets|length }} sockets · {{ cpu.total_cores }} cores / {{ cpu.total_threads }} threads total
{% endif %} {% endif %} {% if memory.total_bytes %}
Memory
{{ memory.total_bytes | humanize_bytes }} {% if memory.slots_total %} · {{ memory.slots_populated }} / {{ memory.slots_total }} slots populated{% endif %} {% if memory.dominant_type %} · {{ memory.dominant_type }}{% endif %} {% if memory.dominant_speed_hz %} @ {{ (memory.dominant_speed_hz / 1000000) | round | int }} MHz{% endif %} {% if memory.dimms %}
{% for d in memory.dimms %} {% if d.empty %} {% else %} {% endif %} {% endfor %}
{% endif %}
{% endif %} {% endif %} {% set nics = inv.nics %} {% if nics %}

Network ({{ nics|length }})

{% for n in nics %} {% endfor %}
Interface MAC Vendor / Driver Link
{{ n.get("name") or "-" }} {{ n.get("mac") or "-" }} {{ n.get("vendor") or "-" }} {% if n.get("driver") %}
{{ n.get("driver") }}{% endif %}
{% if n.get("speed") %}{{ n.get("speed") }}{% else %}-{% endif %}
{% endif %} {% set disks = inv.disks %} {% if disks %}

Disks ({{ disks|length }})

{% for d in disks %} {% endfor %}
Path Size Vendor / Model Serial Transport
{{ d.get("path") or "-" }} {{ d.get("size") or "-" }} {{ d.get("vendor") or "-" }} {% if d.get("model") %}
{{ d.get("model") }}{% endif %}
{{ d.get("serial") or "-" }} {{ d.get("tran") or "-" }}
{% else %}

No disks in the posted inventory.

{% endif %} {% if machine.inventory.get("lshw") %}
Raw lshw JSON
{{ machine.inventory.get("lshw") | tojson(indent=2) }}
{% endif %} {% else %}

No inventory has been posted for this machine yet. The live env's pixie CLI posts one on boot; check the target's PXE reach to pixie.

{% endif %}

Recent events

All events
{% if events %}
{% for e in events %} {% endfor %}
When Kind Summary
{{ e.ts | fmt_ts }} {{ e.kind }} {{ e.summary or "" }}
{% else %}

No events recorded for this machine yet.

{% endif %}
{% endblock %}