A shut-off clone kept the VM's TPM element as it was: an emulated TPM
with a state path of its own, a host's TPM or an external one would be
shared with the VM, not the new, empty TPM the clone form promises. An
emulated TPM loses its state path, and the others are refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
libvirt refused the copy only after the job had begun; recovery then
found a volume of the planned name and ended the job uncertain, holding
the VM. The names are checked when the job is queued and before it runs,
as a live clone did; that check now refreshes the pool first, so a live
clone's free space is read current too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
The copy was read while the guest wrote: a raw disk was copied with
data of different instants, and a qcow2 one failed on qemu-img's image
lock with a message that did not say why. Such a clone is refused, with
the running VMs named, when it is queued and again before it runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
Only a file in libvirt's NVRAM directory was copied; one elsewhere was
skipped without a word, and the VM started on the destination from the
firmware's template, its saved boot entries lost. Such a VM is now
refused before anything changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
A worker that stopped during the copy left the partial files on the
destination: libvirt ends the migration and the VM stays, but the files
took the space and refused the next try, as a copy never replaces a
file. The job records the copies before libvirt makes them, and
recovery deletes them while the destination has no VM of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
A copied disk was found by its target as well as its path: after a bus
change waiting for the next start (vda to sda), the next start's
definition kept the source path, which the destination does not have,
and the VM would not start again there. The path alone finds it, so two
disks of one file can no longer be copied to two places.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
The source host alone was asked: a file on storage the computes share,
used by a VM or volume of another compute, was deleted under it. The
check the disk move makes, every compute and a host that cannot tell
counting as using it, moves to instances.utils and serves both. The
destination is not asked when its copy has the source's path: its VM
uses that copy, another file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
Each host's logical volumes are its own: a VM whose disk is /dev/vg/x
migrated, uncopied, onto the destination's volume of the same name and
kept running on other data. Such a volume now counts as missing there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
A disk of the user's VMs whose size could not be read (its volume not in a
pool, the pool stopped) counted as 0 GiB, so a disk-limited user's increase
could pass over the limit. A disk no pool holds now has the size libvirt
reads from the file or device (blockInfo, also for a shut-off VM); one whose
size is still unknown makes the usage unverified and the increase is
refused, as when a host cannot be reached. Users without a disk limit are
not affected.
A running VM's page read its addresses while it rendered: the guest agent,
when its channel is connected but it does not answer, holds the page (and
a gunicorn worker) for its whole 10 s timeout, on every load. The page now
shows them once it has loaded: vm_addresses.js asks the new
instances/<pk>/addresses/ for each NIC's addresses and fills the header's
IPv4 and each network card's IPv4 and IPv6.
The Computes page, the host overview and the new-VM dialog's capacity line
read each VM's XML for its memory and vCPUs: a round trip a VM, about
200 ms for 150 VMs on a LAN, more with each host. libvirt's domain stats
give them for every VM in one call; a running VM busy with a job is not
waited for (NOWAIT) and its sizes come from its XML as before.
- A migration whose libvirt call failed but which recovery finds moved (or
cannot tell) gets a row of that end; only a failure already logged by
migrate_instance is not logged twice.
- A clone made with its resize or start failed, and a recovered migration
whose autostart or NVRAM step failed, write a failed row of that step, as
migrate_instance does for its later steps.
- Setting the link state of an interface or the video model of a VM that
has none is refused: it changed nothing and was logged as done.
- The rows of a removed VM record (deleted or expired) name its host, VM
and UUID, so the VM's history has them.
The create page and the API's create take the architecture from the
URL. One the host's capabilities do not list raised a KeyError in
get_dom_cap_xml: an HTML 500 for both. It is now "This host has no
architecture noarch": the page shows it, the API answers 400.
Since the VM create became a job (6f0c7300) every new VM was defined with
no VNC password, and a clone copied its source's password (or its lack
of one) exactly: every clone of a template shared it. Computes listen
for VNC on all their addresses (dev/libvirt-bootstrap.sh), so anyone who
could reach a compute's VNC ports opened any of these consoles directly,
without the panel; the README's firewall rule was the only guard.
The create job asks for create.py's random 8-character password again,
and a clone's VNC graphics get a new random one. The console page sends
the password to a user with passwordless_console, which the user create
form now ticks by default next to change_password; a user without it is
asked for the password, as the permission intends. Existing VMs keep
their settings.
Live create, clone and VM page tests pass on a TCP compute.
Tests that build vrtManager objects and touch no view, form or model of
an app lived in that app; vrtManager/tests already holds such tests, so
the convention was split. They move to vrtManager/tests: the firmware,
CPU mode and PCIe port definitions of a new VM (test_create), USB and PCI
host devices, the console listener, host CPU reads, interface reads,
deletes and XML, network filters, PCI passthrough rules, node devices,
network changes, addresses and leases, pool XML, secrets, and the live
tests that use vrtManager only (disk resize, ISO, snapshots, XML, live
clone). livetest.py, the live-test helper six apps import, and the
network test driver move with them.
Others go to their app: the quota with a host down to instances, the
401/403 Logs rows (webvirtcloud's middleware) to webvirtcloud, the VM
page's tab script test to instances, novncd's own tests out of the
console views tests, the compute lock and input validation tests out of
the computes views tests, the interface views out of the forms tests.
instances/tests/test_views.py, entirely live, is test_live_views.py.
Test modules imported fixtures from each other; shared ones are now in
vrtManager/tests/fakes.py (migration fakes, node devices, IOMMU groups,
leases, a NAT network, a Ceph secret), interfaces/tests/fakes.py,
console/tests/daemons.py (novncd loaded once), livetest.wait_for and
instances/tests/create_page.BODY. Every live test checks
livetest.enabled(). The suite runs the same 2097 tests.
Five tests only acquired and released the lock and asserted nothing.
They now check, from another thread, that a reader lets readers in and
keeps writers out, that a writer keeps everyone out, and that a nested
or upgraded lock is held until its last release.
- 25 vrtManager test modules configured settings themselves when Django
had none (MAC_OUI, and in test_network_ip a fake django module): under
manage.py test settings are always configured, so it never ran.
- webvirtcloud/tests/test_common_tags.py tested no tag: it fetched an
unknown URL, as test_error_pages and test_logging already do.
- .gitignore covers the parallel runs' test_db_N.sqlite3 files too.
Each was checked with git grep across every tracked file (templates, JS,
shell, unit files and the extensionless console daemons) and for
dynamic use (URL names, template names, registries):
- vrtManager: host_is_up, is_kvm_supported, get_emulators,
get_file_extensions, both get_network_forward, get_snapshots,
get_storages_images, get_os_type, get_host_arch, the get_pretty_* trio
and, in util, is_kvm_available, randomMAC, get_max_vcpus, compareMAC,
pretty_mem, pretty_bytes and validate_uuid; with them the port
constants only host_is_up read. Some were reached only by their tests,
which go too.
- The flavor edit page: nothing linked to it; flavors are created and
deleted on the Flavors page and edited through the API.
- computes.validate_name, UserForm.identity_has_errors, the has_perm
template tag, the ALLOW_EMPTY_PASSWORD setting nothing reads,
hidePleaseWaitDialog, default_app_config (ignored since Django 4.1)
and the Python < 3.6 lines of secret_generator.py.
Each poll called info(), then blockStats() for every disk and
interfaceStats() for every NIC: nine calls for a VM with four disks and
two NICs, every 5 s for each open Stats tab. domainListGetStats now gives
the CPU time and every disk's and NIC's counters at once; a counter the
host does not report is -1, as before.
libvirt's default handler printed each error on stderr without saying
where it came from: in Docker those lines went to docker logs, and a
VM page read of a shut-off VM left one there. Every process that talks to
libvirt imports vrtManager.connection, which now registers a silent
handler; the errors still reach their callers as exceptions. The
console callback's own registration is dropped.
A run of failed opens is logged once, at its first failure, and the next
successful open ends it; a close callback and a request refused because the
host's requests still wait are logged each time. The lines go to the app log
of the process (web or job worker).
other_hosts counted the request on its own host again only when the
block ended normally: a quota check that raised left the request
counted on the other host while it went on with its own. The count is
now restored however the block ends.
upload_volume opens the libvirt stream before it creates the volume, but
created the volume outside the block that aborts the stream: a pool that
refused the volume (full, a name taken) left an unfinished stream to be
freed. The creation is now inside that block.
A host's capacity (Computes page, new VM dialog) came from
get_host_instances, which listed the VMs by ID and by name and then
looked each up, read its XML and its info: four calls a VM, for every
host on the page. It now uses the VM lists' get_domain_rows: the states
in one call and each VM's XML once. get_host_instances had no other
caller and is gone.
Checked on QEMU against the per-VM info of every domain (running, paused,
shut off): the same vCPUs and memory. A VM gone while the host is read
is left out instead of making the host's capacity unreadable.
libvirt requires a stream that did not finish to be aborted before it is
freed. The NVRAM download of a shut-off move and upload_volume (NVRAM
upload, cloud-init seed) freed theirs after a failed send, receive or
finish without aborting, on the shared host connection. They abort now;
upload_volume opens its stream before it makes the volume, so the
cleanup has both.
A quota refusal put the English name of the quota (cpu, memory, disk,
instance) into a translated sentence, so a Turkish page read "memory
kotası doldu". check_user_quota now returns which quota and its numbers
(QuotaExceeded), each quota has a whole sentence, and the refusals put
that sentence before what is refused; QUOTA_DEBUG adds the numbers as
before. The clone job stores the refusal as a message, so the Jobs page
shows it in its reader's language.
The connection errors of vrtManager (did not answer within, still
waited for, closed, failed with libvirt's text) were English f-strings;
they are messages now, each close reason a whole one. A connection
that died without a close callback raised an error reading "None"; it
now names the host. Logs keep the English text.
wvmInstance kept the guest agent's and the ARP table's addresses from
the first NIC it was asked about, and never read them again on that
object; it carried a lease lookup that was never filled.
The addresses are now one {MAC: (IPv4, IPv6)} read, kept like the
domain XML only inside a cached_reads block: the VM page, which reads
its NICs twice, asks the agent once (it may take its whole timeout),
and a read outside a page's block is a new one. The agent's addresses
still come before the ARP table's.
A request counts on the host it connected to last. A quota check reads
every host of the user's VMs, and a VM's destruction the other hosts'
files, in the middle of a request that then goes on with the VM's own
host through the objects it kept: it was counted on the last host it
read while it used its own, so a host that does not answer could take
more requests than its limit, and the last host read was charged.
Both checks now give the request back to the host it counted on before.
vrtManager's errors (some 140: a disk that does not move, a network
address outside its subnet, a host device given to another VM, the
disks a migration cannot copy) were English f-strings, so no catalog
could translate them, on pages or in jobs.
They are marked texts with their values now; a variant that differs by
a word (internal or external, running or persistent, with a subnet or
without) is a whole message of its own, and a list of problems is a
list of messages. OperationError reads in the active language, so the
pages that show one translate it as they are; its args stay English, and
so do the logs and the audit log. Host device problems and the Migrate
tab's reasons a disk is not copied are messages too.
A job stored its error and its result's warning as English text, much
of it built with the values in it (volume paths, a disk, libvirt's
error), so no catalog could translate it, and the job pages and panel
showed English whatever the language.
A job now stores them as messages: our English text, marked for
translation, and its values; libvirt's own text is a message without
values and is shown as it is. The jobs API and pages put them in the
reader's language. OperationError keeps its text and values apart for
the same reason, so a refusal raised while a job is submitted is shown
translated too. Messages one after another still read as sentences.
Errors stored before are kept as one message each, shown as they were.
With threaded workers a request waiting on a stuck host holds one
thread, but each new request to that host (a VM list's poll every 5 s,
an F5) held another until the threads ran out.
In a process at most two web requests now use one host at a time,
counted by its name whatever the login. Another waits up to the connect
timeout (5 s) for one of them to end, and is then refused as the host
not answering: VM lists show the host as not reachable, pages show the
connection error. A request counts on the host it connected to last,
until it ends, so one that reads several hosts in turn holds none it
has left. Threads outside a request (the job worker's, the consoles')
are not counted: a job holds its host for as long as it runs.
Two disks of a VM with the same file name in different directories
were both offered the same destination pool by default. The form was
sent, and the migration job refused it only then: both copies would
be one file.
The plan now gives each such disk, by default, a pool no earlier disk
of that name is copied into, and says under its choice that another
disk has the file name. A choice that still puts them in one pool is
refused by the job, as before.
A pool whose refresh failed, one busy with a volume copy for example,
was skipped without a word, so a hand-copied image stayed missing with
no reason given. The other pools are still refreshed; each failure is
shown with libvirt's error.
The API comment no longer says listing the ISO images refreshes pools.
The VM page refreshed every active storage pool to list the ISO images
for its CD-ROM drives. A pool holding large disk images took up to
seconds to refresh, most of the page's time, on every page load.
The page now lists the ISO images libvirt already knows. Images
uploaded through the panel are known at once. For an image copied to a
pool by hand, the Removable media section has a Refresh ISO list button
that refreshes the active pools and returns to the page.
A libvirtError made in a test takes the message of the thread's last real
libvirt error: in a live run, after another test's closed connection,
"no marker" read "client socket is closed" and the test failed. It now
checks that the move raised that very error, as the test after it does.
The Migrate tab shows a running VM's live migration switch, on by
default. A VM shut down after the tab opened, as one is to move it shut
off, was then refused: "A shut-off VM cannot be migrated live". A
shut-off VM now moves offline whatever the request says about live, as
the tab tells for a shut-off VM; the tuning options are for a running VM
only, as before. An offline request for a running or paused VM is still
refused.
A shut-off VM with TPM state, as every Windows 11 VM, was refused for
migration: the state is a file of the source host, and an offline move
would start it there with a new TPM. Such a VM now migrates as a shut-off
VM with disks to copy does: its UEFI variables are copied, it is started
paused, its CPUs never run, migrates live, which carries the TPM state,
and is shut off on the destination. A shut-off VM with a saved state is
refused before any of that, for every way it moves: started paused, it
would resume and then lose the saved memory.
The clone tab says that a clone gets a new, empty TPM, and the destroy
page that the TPM state goes with the VM.
A VM made without a TPM, a Windows 10 to be upgraded to Windows 11, had
no way to get one but the XML editor. The Options tab has a TPM 2.0
switch, as the RNG one: it adds an emulated TPM of the host's model to
the definition for the next start, or removes it. Its state stays on the
host until the VM is undefined, so a TPM added again has it back. A TPM
the switch did not make (passthrough, 1.2, encrypted, with its own state
path) is left to the XML editor, and a host without swtpm says so.
A running VM got its new CD-ROM on SCSI, the one bus the panel could plug
in then, and Windows does not see a virtio-scsi drive before the driver
on that ISO is installed. Add CD-ROM is now a dialog that chooses the bus
and the ISO: a running VM takes USB, first, which every guest sees and
which hot-plugs, or SCSI; a shut-off one SATA (IDE on pc), SCSI or USB.
libvirt has no empty USB drive, so a USB CD-ROM takes its ISO when it is
added and is detached instead of ejected, also while the VM runs.
A USB CD-ROM is a CD drive on QEMU's usb-bot only. Where QEMU has none
(RHEL 9), libvirt took usb-storage, which shows the guest a hard disk
that Windows does not read; the drive now asks for usb-bot, and such a
host refuses it with libvirt's message.
The Edit dialog of a VM's network device selected the listed network
or interface whose name matched the device's source, and the first one
when none did: saving only a new model moved a macvtap device or a
bridge the host's lists did not show onto the first network, a bridge
named like a network became that network, and a macvtap device chosen
again lost its mode.
The dialog now offers the device's own source first, "<name>, as it
is", and posts nothing for it; the view passes no source and the
device's source element is left as it is. Choosing another source
changes it as before.
The Migrate tab offered every other host alike, and its plan showed
neither a file of the same name in a destination pool nor the disks of
the VM's next start, which the job then refused. Now:
- When the tab opens, the hosts are checked first: one that cannot be
reached, or the VM's own host under another record (the same libvirt
host UUID), is shown with why and cannot be chosen, and the disks of
the first one left are checked. The job refuses the VM's own host
under another name too.
- A pool of the destination that has a file of a disk's name is shown
but cannot be chosen for it; a disk no pool can take refuses the form.
- The next start's file disks the migration does not carry are listed;
one not on storage both hosts share refuses the form, as the job does.
Destroying a VM with its disk files forced it off first and then
worked out which files to delete, so a check that failed left the VM
stopped, and only this host's VMs and volumes were checked: a file on
shared storage that a VM on another host uses was deleted.
The files are now worked out before the VM is stopped: the disks it
runs with and those of its next start, as before with their snapshot
files and seed. A file a VM or volume of another of the panel's hosts
uses at the same path (shared storage is mounted at one path on every
host, as migration needs) is kept like one used here; another record
of the VM's own host is skipped, as there the VM itself uses them. A
host that cannot be read stops the destruction with its name, before
anything is stopped or deleted.
The Stats tab stacked one large card per chart, so a VM with a few
devices made a long page, and its "Real Time" and "Logs" sub-tabs were
shown to every user although only a superuser may read the logs. The
owner chose the layout from rendered options: a state line, CPU and
memory side by side, one chart per NIC and disk below, each with its
latest reading next to its title, and a small Graphs/Logs switch for
superusers.
Codex reviewed the tab; this is fixed:
- Memory is shown in MiB (it was labelled MB), and its axis grows to
the RSS when the RSS passes the balloon size instead of clipping it.
- The CPU reading says it is of the host's CPUs.
- A NIC is titled by its MAC, not an invented "eth0", and its readings
are matched to its chart by the MAC: they were matched by position, so
removing a NIC while the page was open moved the next one's readings
into its chart.
- A VM that is not running says nothing is measured.
- The logs load whenever the Logs view is shown, by keyboard too; an
empty answer says "None" and a failed one says so, instead of an
empty table. The tab shows the latest 50 rows, with a link to all of
the VM's rows on the Logs page.
- The VM's header shows memory in MiB; it said MB.
The Destroy tab was a sub-tab with one sentence and a link; its
confirmation page said only "This action is irreversible!" over a
"Remove Instance's data" switch. The tab now shows the state and
"Review destruction". The owner chose the confirmation layout from
three rendered options: the VM and its host, that its definition,
saved state and snapshot records go and that a running VM is forced
off, two plans for the files (delete the disk files, or keep them), the
VM's disks, what is always kept, the NVRAM choice, and Cancel.
Codex reviewed destroying; this is fixed:
- A disk file that cannot be deleted keeps the files below it, which
it needs: they were deleted after it, leaving it broken. The files
are deleted overlays first (split_disk_paths_by_use orders them by
how many files each grows from), and if the files a kept one needs
cannot be read, none of the rest goes. A message names each file
kept so, and a failed deletion says the VM is removed.
The Snapshots tab had three sub-tabs: internal, external and the
lists, with the two kinds side by side as equals. The owner set the
direction: external snapshots are the way; internal ones pause the VM
while its memory is saved and can take long, so they stay only as a
fallback for a VM that cannot take an external one.
The tab now has the external snapshot form on top, its name and
description on one line, with its memory and freeze options; the
internal snapshot under a folded "fallback" line with its warning; and
one list of all snapshots, newest first, each on one line with its
name, description, kind and date, and Revert and Delete.
Codex reviewed snapshots end to end; these are fixed:
- The pause and memory lines follow the VM's state and the kind.
- An internal snapshot is reverted to on a shut-off VM only, on the
server too; the tab alone held that rule.
- A paused VM's revert says it comes back paused, not that it starts.
- An external snapshot waits, with the reason, while snapshots newer
than the one reverted to exist; the server refused it unexplained.
- Deleting an external snapshot says its files are merged with the
disks; "into the next one" was wrong for the last one.
- The description has no 45-character limit only the browser kept.
- Freezing is not shown ticked where it cannot be offered, and
unticking memory gives back the earlier freeze choice.
- Internal create, revert and delete refuse a saved state or a running
disk job, as external ones do.
The Resize tab's CPU, Memory and Disk panes were horizontal forms with
bold host figures. The owner chose the ledger layout from three
rendered options: each section with its heading, the VM's figures and
the host's on the left, its fields on the right, and one line on what
applies now and what at the next start.
Codex reviewed resizing end to end; these are fixed:
- A vCPU turned on or off while the VM runs is kept at its next start:
libvirt was asked without flags, which changes the running VM only.
- A paused VM offers to turn vCPUs on only: turning one off needs the
guest, which does not run.
- A smaller disk size is refused, and so is the rest of the form,
before anything grows; it was skipped without a word.
- vCPUs and memory below 1 are refused.
- The memory fields are numbers filled with the VM's values: the
"Custom value" box, its script and the preset list are gone; an empty
custom field silently used the preset.
- Units and words: MiB, not MB; a disk's field is its new size, with
its size now above it; the messages say what the VM gets.
Two settings the owner chose to add to Settings > Options after the
page's review with Codex. Both change the persistent definition, so
they are saved while the VM runs and used from its next start.
- Random number generator: a switch for a virtio RNG device fed from
the host's /dev/urandom, so a new VM does not wait for entropy at
boot. Another RNG device, or the panel's with a rate or driver, is
shown as the VM's own and left to the XML editor.
- When the guest crashes: Restart or Stop (on_crash restart or
destroy); libvirt's default, Stop, when the definition has none.
Stop keeps a VM that crashes again and again down for a look. Any
other action is shown and left to the XML editor.
Settings > Options was four forms under bold sentences, most of them
disabled while the VM ran. The owner chose this layout from three
rendered options: one list, each row a setting with its description on
the left and its control on the right: VM details (title, description),
Template, primary video model, QEMU guest agent. vCPU hot plug moved to
Resize > CPU, next to the vCPU counts.
Codex reviewed the four forms; these are fixed:
- Title, description and the video model are saved while the VM runs:
they are in the persistent definition, used at the next start.
- The template flag has its own form and view, set_template, for
superusers and staff, and only for a shut-off VM: a running VM made a
template left the quota while it kept running. Turning a template
back into a VM checks the quota there, and each change is audited.
The title form no longer touches the flag.
- A new video model stays the primary one; the old model's memory and
head settings, which are its own, go with it.
- The agent and vCPU hot plug forms show the persistent definition; they
showed the running VM's, so a pending change looked undone.
- Enabling the guest agent adds its channel only where it is missing:
saving Enabled again failed, and a channel pending the next start kept
a running VM from getting one.
- The agent line tells a VM that is not running, one without a channel,
and a connected or not connected agent apart.
Settings > Clone was a horizontal form with unlabelled checkboxes. The
owner chose this layout from three rendered options: the clone's name
on top, a "Network" card with a new MAC address per NIC, a "Storage"
card with a new volume name per disk, then the optional title and
description and a footer with Clone.
Codex reviewed cloning end to end; these are fixed:
- Every NIC of the clone gets a new random MAC when the form opens.
Only the first did, so a VM with two NICs gave its clone the second
NIC's MAC. Guess MAC is offered for the first NIC only: a name's DHCP
host entry holds one address.
- A VM without a NIC can be cloned: the first NIC's MAC was required.
- The disk name script uses the persistent disks, as the form does; a
disk added pending the next start kept the source's volume name.
- A running or paused VM is not cloned while a copied disk changes at
the next start: the backup copies the running disk, so the clone
would get the old contents under the new definition.
- A derived volume name that does not start with the source's name now
starts with the clone's name and the disk's device, so a second clone
or a second disk does not take the same name.
- Guess for the clone name says when the DHCP file has no free name; it
stopped with a script error.
- The qcow2 metadata preallocation box shows only for a shut-off VM: a
live clone's backup ignores it.
Other users no longer get hidden MAC and disk inputs or the scripts that
filled them: the server derives those values and ignored the posted ones.
The Boot pane shows Autostart and the boot menu as switches that save on
change, and the boot order as one list of the VM's disks, CD-ROM drives
and NICs: the devices in the order first, checked and numbered, the
others below. Up and Down move the checked devices; Apply order saves.
- The boot menu can be changed while the VM runs: it is in the
persistent definition, so it applies at the next start, as the boot
order does.
- The boot order editor no longer needs the boot menu enabled.
- A VM that boots by device type (<os><boot dev='hd'/>) names those
types; checking devices replaces that order.
- An order is refused while a device the list does not show (a host
device, a floppy) has a boot place, so Apply does not drop it; the
message points to the XML editor.
- An empty order is refused with a message instead of being ignored.
- get_bootorder gives a NIC's full MAC as its target.
- The unused jQuery multiselect plugin is removed.