Skip to content

feat(instances): a new VM is made on one form in three steps - #172

Merged
catborise merged 1 commit into
masterfrom
create-vm-single-form
Oct 23, 2026
Merged

catborise merged 1 commit into
masterfrom
create-vm-single-form

Conversation

@catborise

@catborise catborise commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

This closes U-06, the "wizard consolidation" item on the roadmap. The design was discussed with Codex and agy, and you reviewed it as a spike before this PR.

What changes for the user

  • Three steps, one look. Creating a VM is three steps: host, machine, details. They share one step indicator, and done steps link back.
  • Host. The dialog lists hosts as clickable rows that show CPU and memory use.
  • Machine. Architecture and machine type sit on one card with Cancel/Next. Defining the VM from XML is a collapsible section instead of a tab.
  • Details. One form replaces the three tabs (flavor, custom, template):
    • Name and size: the name, plus flavor presets as buttons that fill vCPU, memory and disk size. Flavors are added and deleted in a dialog outside the form.
    • Disk: choose New disk, Existing volumes or Clone from template. Only the chosen source's fields are shown and sent. A template's copy goes into the template's pool unless you pick another.
    • Networks: one row per interface with its MAC, the same for every source.
    • Advanced: a collapsible section with the remaining options.
  • Errors. A refused form comes back as it was sent, with the focus on the first error.
  • Behaviour changes:
    • A new disk no longer requires a MAC. libvirt assigns one, as it already did for the other two sources.
    • Without a storage pool or a network, the page says so instead of showing the form.
    • A host that cannot be read now shows a message instead of a 500.

The template is 617 lines instead of 1112. Overall the diff removes about 480 lines.

Server side

  • The create job now refuses a request that sends more than one disk source. The page sends only one, but a crafted request could send two, and the job would have used the first.
  • error_attrs takes an optional help id, so a field is described by both its help text and its error.

Verification

  • Full suite on SQLite and on PostgreSQL 16: 1387 tests each, OK (32 skipped). ruff check . is clean.

  • The page's scripts are tested in Node:

    • rows and MACs;
    • switching disk sources;
    • presets;
    • restoring a refused form (volumes, buses, template, the Advanced section);
    • the first network;
    • the template's copy pool.
  • axe-core 4.10.3 checked the host dialog, the machine page (with the XML section open) and the details page (each disk source, with presets, Advanced and the flavor dialog open).

  • Live test on a Vagrant compute: one VM created with each disk source.

    • New disk: audit-new.qcow2.
    • Clone from template: audit-tpl.img.
    • Existing volume: audit-ext.qcow2.

    Each VM had the right disk and one NIC with a random MAC. All three VMs and their volumes were removed afterwards.

  • An independent review found nothing critical. Its two important findings (the template's copy pool, missing restore tests) and most minor ones are fixed here.

Left for later (minor): if the machine types fail to load, Next stays disabled without a message; when every host is offline, the host dialog shows an empty list.

Frontend review (Codex, as a frontend/UX/a11y specialist, with screenshots)

Codex reviewed the whole branch. Applied here:

  • Keyboard focus: the preset buttons and the disclosure buttons (Advanced, XML) now show a focus ring (focus-ring); before, keyboard users had no visible focus on them.
  • Tom Select on dark themes: the control's text was invisible on dark Bootswatch themes, because typed text was white on a white input in darkly. The control now keeps the text colour of the select it replaces. This is fixed in webvirtcloud.css, so it covers every page that uses Tom Select.
  • Disclosures: a disclosure spans its card, and its chevron turns while the section is open.
  • Presets: a preset now says it picks a new disk, in a help text and in its announcement.
  • Mobile keyboards: numeric fields bring up a numeric keyboard, and MAC fields have no autocorrection or capitalisation.
  • Machine page: Next stays disabled until a machine type is loaded.

The full suite passes again on SQLite and on PostgreSQL (1388 tests each). The axe scan is still clean on bootstrap-orj and darkly.

Larger points from the review are separate issues: #173 (managing flavors discards the form), #174 (XML lost on refusal), #175 (no minimum for vCPU/memory), #176 (empty host dialog when all hosts are offline), #177 (stale dependent selects).

🤖 Generated with Claude Code

The create page had three tabs (flavor, custom, template), each with its
own copy of the same create form; every flavor rendered one more copy in
a dialog, and fixes had to be made in each copy. All of them posted the
same form to the same job, differing only in where the disk comes from.

The page is now one form in sections: name and size, disk, networks and
an Advanced section. The disk section offers three sources as radio
cards (a new disk, existing volumes, a copy of a template); only the
chosen source's fields are shown, and the others are disabled, so they
are neither sent nor validated. A template's copy goes into the
template's pool unless another is picked. Flavors are presets that fill
vCPU, memory and disk size and pick a new disk; they are added and
deleted in a dialog outside the form. Every source uses the
per-interface network rows, so a new disk no longer requires a MAC:
libvirt gives an interface without one its address, as it did for the
other sources. A refused form comes back with its source, volumes,
template and networks, and the focus on its first error. Without a
storage pool or a network the page shows why instead of the form, and a
host that cannot be read is a message, not a 500.

The host dialog and the machine page share the form's look: hosts are
rows with their CPU and memory use, the machine page is a card with
Cancel/Next and the XML way is a collapsible section. All three steps
show the same step indicator, done steps linking back.

The create job refuses more than one disk source (the page sends one; a
crafted request could send two, and the job would have used the first).
A field with help text is described by its help and its error.

Frontend review fixes: the presets and the disclosure buttons show a
focus ring for keyboard users; a disclosure spans its card and its
chevron turns while open; a preset says it picks a new disk; numeric
fields bring a numeric keyboard and MAC fields no autocorrection; the
machine page's Next waits for a machine type. Tom Select's control
keeps the text colour of the select it replaces: its own fixed colour
made typed text invisible on dark Bootswatch themes, on every page that
uses it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant