* feat(web-core): adapt card rows to their content in side panels
- Key and value stay on one line when they fit, the value moves below the key otherwise
- New `values` prop on `VtsCardRowKeyValue`: one copy button per value, a "Copy all"
action and an optional "see more" limit for fields with multiple values
- New `VtsCardRowList` component holding the 0.8rem spacing between rows
- `UiInfo` text is small inside side panels (icon stays medium), medium elsewhere
- Side panel links use the small size
* docs: add changelog entries
* feat(web-core): make the side panel resizable
- Resize the side panel like the treeview, with a shared `VtsResizeHandle`
component and `useResizableWidth` composable
- Default width of the treeview and the side panel is 1/5 of the screen
(280px minimum), max width is 1/3 of the screen (400px minimum)
- Double-click (or Enter) on the handle restores the default width
- The handle is keyboard accessible (arrows, Home, End) and exposed as an
ARIA separator
* docs: add changelog entries
`VtsRemoteConsole` watched `[consoleContainer, …]` where `consoleContainer`
comes from `useTemplateRef`, i.e. a shallow ref. Vue forces a multi-source
watch to run its callback when one of the sources is a shallow ref, even if
no value has changed. The parent page builds a new (identical) `URL` object
on each VM update, so every SSE update tore down and recreated the VNC
connection.
This was mostly visible while a VM boots (Windows especially), because the VM
gets updated every few hundred milliseconds then.
Watching `() => consoleContainer.value` restores the normal value comparison.
See XO-1758
The migration drawer has two steps:
1. Select the destination host (and optionally the migration network).
2. Map the VM disks to SRs and the VM interfaces to networks on the destination.
* refactor(mixins,proxy,xo-server): pass the XAPI names as one xapiLabels object
The SR name was a lone `nameLabel` threaded through xo-server, xo-proxy and
the mixin; the VDI names are about to join it. No change in behavior: the
proxy API still takes `nameLabel`.
Also export OC_MOUNT from _utils.mjs, the cache module will need it.
* feat(iscsi): FileBlockDevice can serve a block device node
With `size`, the path must be a block device (e.g. a VDI hot-plugged onto the
appliance, /dev/xvdX): its inode reports a size of 0, so the capacity comes
from the caller, and a short read is an I/O error instead of a sparse tail.
Also add unit tests for FileBlockDevice, which had none, and integration
tests against a loop device.
* feat(iscsi): advertise read-only LUNs as write protected
A read-only BlockDevice now sets the Write Protect bit of MODE SENSE, so the
guest mounts the disk read-only instead of failing on its first write. This is
the case of a live mounted disk, served by a read-only DiskBlockDevice.
* feat(live-mount): name the SR and VDI after the backed up VM and restore point
The SR is now `[XO live mount] <VM> (<backup date>)` and the VDI keeps the
name of the backed up disk, so a live mounted disk reads like the disk the
user knows. The VDI is also tagged with `xo:live-mount`, like the SR.
The proxy API takes them as `xapiLabels`; `nameLabel` is still accepted from
older XOs.
* feat(live-mount): optional local VDI cache, making the mount read/write
Without a cache, a live mounted disk is read-only and every read goes to the
backup repository. With `cacheSrId` (xo-server) / `cacheSr` (proxy API), the
disk is materialized block by block into a VDI of that SR, hot-plugged onto
the appliance serving the mount (XOA or the proxy):
- a block is read from the backup repository at most once;
- the LUN is read/write, writes land in the cache VDI and the backup is never
modified;
- the VDI is created with the mount and destroyed with it: what was written
is lost on unmount.
The appliance must be a VM of the target pool, and the SR plugged on the host
running it. On a local SR, the appliance must not be migrated while the mount
lasts, since the cache VDI cannot follow it: this is documented, not enforced.
The cache VDI is tagged `[NOBAK] [NOSNAP]` to stay out of the appliance's own
backups, and the SR of a cached mount is named `[XO live restore] …`. The
cache device is opened with retries, since its node may lag behind the
hot-plug.
* feat(xo6): add backup job side panel
* wip refacto for merge
* refacto for merge
* add tests
* refacto ok
* last refacto
* fix report when field
* fix post rebase
* feat(xo6): add page plugins
* improve error summary component
* plugin configuration form review
* fix(xo6): adapt plugins to master after rebase
- FrontXoPlugin keeps the plugin types of @vates/types (strict union since #10588)
- tests follow the state checks, task params and loading state of the master backend
* chore(xo6): add plugins page to the changelog
* fix(xo6): pluralize the examples count and unprefix generic plugin i18n keys
* fix(xo6): drop JSDoc from the json-schema form
* test(xo6): align the plugin tests with the existing test patterns
Component tests mock the plugin jobs and assert their run and arguments instead of the REST requests, job tests mock fetch.util directly with run/validate blocks, composable tests use a local mockJob, and createPlugin gets the factory JSDoc.
* refactor(xo6): name the json-schema form conditions and share the object schema guard
* refactor(xo6): expose the summary errors from the json-schema form
* refactor(xo6): name the plugin save and test steps
* fix(xo6): use the delete action icon in the plugin delete buttons
* revert(xo6): drop the capitalize prop of UiToggle
* refactor(xo6): remove the go-to-first-error button of the plugin configuration
* refactor(xo6): use VtsDeleteButton in the plugin configuration deletion
* revert(web-core): keep useValidatedForm and useFormSelect as on master
The plugin forms focus their first missing field through useJsonSchemaForm, so the
error summary and focus-on-validate of useValidatedForm had no consumer and changed
the behaviour of the existing forms.
* feat(web-core): use the puzzle icon for plugins and the window icon for templates
* feat(xo6): plugin load and startup actions in the plugins table, panel and header
- Side panel: Load/Unload button + "Enable/Disable load at startup" in more actions,
loaded / load at startup as statuses, copiable version and description, no bottom link button
- Table: action column (select + quick actions) replaces the select column
- Detail head bar: same actions, PluginStateToggles removed
- One plugin.change-state job replaces the load and unload jobs; updatePluginAutoload
and isPluginLoaded move to xo-plugin.util.ts
- useXoPluginUtils no longer exposes description
- Shared test helpers: createJobResult, createActionItem, findTooltipContent
* feat(xo6): read-only plugin detail page, configuration and test in drawers
- Detail page: general info + schema-driven read-only configuration card (VtsColumns)
- Configure: primary action in head bar, side panel and table row, opens a drawer
- Test: drawer from the more-actions menus; delete configuration in the same menus
- Errors follow the app pattern: the drawer stays open on job failure
- Delete configuration is blocked while the plugin is loaded (the API rejects it)
- Secrets: masked by xo-server's name rule or by the value obfuscated by the API
- web-core: VtsActionItems extracted from VtsActionsMenu for menu-slot reuse
* feat(xo6): show the plugin Configure action first, next to Load
* refactor(xo6): remove the unused ApiErrorAlert, useActionError and api-error utils
* refactor(xo6): de-duplicate the plugin drawer composables and test fixtures
- one useXoPluginDrawerJob replaces the near-identical configure and test composables
- useXoPluginUtils exposes the description and loaded state used by the templates
- shared plugin schema fixture and JSON schema item-label test helpers
* refactor(xo6): de-duplicate the JSON schema fields and the plugin tests
- JsonSchemaInputWrapper holds the input wrapper, message and validation of the input, select and XO object fields
- JsonSchemaField wraps the object and array fields in their fieldset, which owns the optional value
- shared test helpers for drawers, plugin actions, titles and JSON schema forms
* refactor(xo6): reuse @vates/obfuscate and cover the JSON schema select fields
- @vates/obfuscate exports isSensitiveParam and ships its types; XO 6 imports it and OBFUSCATED_VALUE instead of copies
- tests for the select, XO object field and useXoObjectOptions; drop the select guard that could never run
- JsonSchemaValue titles its sections in a computed and checks the secret mask
* feat(xo6): show the arrays of a plugin configuration as a list
* refactor(xo6): share the plugin route and the item label type, remove the unused required key
* docs(lite): add a story for VtsActionItems
* feat(xo6): validate the numeric limits, integers and list sizes of the plugin forms
The JSON schema form only checked the required fields, so a value under minimum, over maximum, a decimal number for an integer or a list shorter than minItems was only rejected by the server. oneOf is still left to the server.
* test(xo6): share the drawer, button and tag test helpers, test the preset application
Replaces the local submit, button and tag lookups of the backup repository, plugin, host and VM tests with the src/test helpers, makes PluginMoreActions assert its wiring instead of its mock, and documents the new helpers.
* test(xo6): test the plugin form lists, sections, messages and links, extract the property grouping
* fix(xo6): do not test a plugin while it is being loaded or unloaded
* refactor(xo6): give fetchPut and fetchPatch no default type, like fetchPost
* refactor(xo6): identify a missing plugin in the plugin job argument instead of in defineJob
* refactor(xo6): remove the unused disabled prop from the JSON schema form
* refactor(xo6): detect the plugin secrets in the front only, leave @vates/obfuscate as on master
- the secret property names and the obfuscated value are local to XO 6 again, no @vates/obfuscate export, types or release
- the backup repository and plugin tests import the obfuscated value from the shared constants
* refactor(xo6): use checkboxes instead of toggles in the plugin forms
* test(xo6): get a checkbox by its label with a shared test helper
* test(xo6): reset the state of the array items when the list is replaced from the outside
* refactor(xo6): split the JSON schema form dir into the plugin components, utils, composables and form dirs
---------
Co-authored-by: Olivier Floch <olivier.floch@vates.fr>
When privateNetwork.addNetwork() fails during xapi connection, the network is
already inserted in privateNetwork.networks and a center may have been set
without being recorded in _starCenters, leaving a broken PrivateNetwork
registered.
After the registration loop, remove the network that failed to register, drop
the orphaned center, and delete the PrivateNetwork once it has no network left,
like _handleDisconnectedXapi already does.
Also defer pushing to noVniNetworks until the network is registered, so a failed
network gets no VNI and does not trigger _electNewCenter outside of any
try/catch.
Wrap the entire per-network registration body (after the privateNetwork creation
check) in a try/catch so one network's failure doesn't affect others.
Reorder `xo:sdn-controller:vni` to after `privateNetwork.addNetwork()` call to
manage more easily the undo.
The value stored for preferred-center could be wrong or out-of-date. If no XAPI
object exists with such uuid (the host designed as preferred center was
removed from XAPI), avoid to hard fail, and consider the value is absent.