* 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.
The `backup-archive` SSE collection (#10472) only announced a change when a client listed the
repository: XO never read one on its own, so a subscriber learnt of a backup created, merged or
deleted at the next listing by any client, and after a restart heard nothing from a repository
until a client listed it.
XO now reads the repositories itself:
- every enabled repository is listed once XO has started, one after another, and a repository
enabled later as soon as `updateRemote()` has stored it;
- the changes this XO makes are pushed at once, for a repository whose journal can be replayed: at
the end of each VM export to it (worker, no-worker mode and proxy alike), at the end of the job
and after a deletion. A read in progress when `refresh()` is called reads again, since it may
have missed the change, at most 3 times and never when that means listing the repository in full;
- the other changes (the size the merge worker rewrites, another XO sharing the repository) are
read by two timers, 2 repositories at a time and through the listing backoff: every minute from
the journal, every 10 minutes by a full listing for a repository whose journal cannot be read,
e.g. behind a proxy which does not expose it;
- a repository is listed in full again an hour after its entry was built, rather than when it
crosses a UTC day, which bounds the drift from what the journal does not record. The
`fullListingInterval` parameter of its URL sets this delay, in whole minutes; the XO 6 and XO 5
repository forms now keep it when the repository is edited.
The archives are registered through `registerCollection` like any other collection, which drops
the REST API special case for them. Their events now carry an array of archives (`add`, `update`)
or of ids (`remove`); SSE subscribers still receive one event per archive.
* feat(xo6): add actions on BRs
* change monitorTask
* actions jobs ok
* fix monitorTask and add actions buttons on detail and side panel
* action bar wip
* table actions change state ok
* multi test speed ok
* add type to confirm modal
* forget ok and create new component bulk
* fix tests
* refacto for PR
* refacto for PR
* refacto for PR ok
* refacto for merge
* add and update tests
* last refacto
Introduced by #8491
See #10201 (same workaround)
`VM.set_VCPUs_at_startup` ran concurrently and re-added `cores-per-socket`
between its removal and its re-addition, making the VM creation fail.
The deploy form now offers Small (2 vCPUs, 4 GiB), Medium (4 vCPUs,
8 GiB) and Large (8 vCPUs, 16 GiB, default) presets. Update the sizing
table to match, and drop the manual --max-old-space-size procedure:
since 6.8, xo-server sets its heap limit to 70% of the VM RAM.
Signed-off-by: Olivier Lambert <olivier.lambert@vates.tech>
* fix(@vates/iscsi): only replace the session once the newcomer has logged in
A new TCP connection used to tear down the active session as soon as it
was accepted, before sending anything. Any peer able to reach the port,
a port scan, a failed CHAP login or a Discovery session, interrupted the
disk in use.
New connections are now kept aside until they establish a Normal session
(CHAP included when configured), and only then replace the active one.
That keeps the ability to recover from a stale connection, which is why
connections are replaced rather than refused.
Connections without a session are bounded in time (`loginTimeoutMs`,
15 s by default, also closing lingering Discovery sessions) and in number
(`maxPendingConnections`, 8 by default, oldest dropped first).
* chore: add PR number to changelog
* fix(@vates/iscsi): refuse extra connections and guard the login race
Review follow-up:
- let net.Server refuse connections past maxPendingConnections (+1 for
the one serving the LUN) instead of evicting the oldest pending one
- a connection destroyed while its final Login Response is being written
must not go on and replace the current session
* feat(@vates/iscsi): bound what a peer can make the target do
- allowedAddresses: close connections from other initiators as soon as
they are accepted, so that they cannot take the pending connection
slots and block a legitimate login
- reject a PDU whose data segment exceeds 8 KiB during login, or the
declared MaxRecvDataSegmentLength afterwards, before reading it
- maxTransferLength (8 MiB by default): advertised in the block limits VPD
page, longer READ/WRITE refused, which also bounds the WRITE buffer
- BlockDevice.readOnly: report the LUN write-protected and refuse WRITE
with DATA PROTECT before soliciting any data; DiskBlockDevice sets it
* chore: add PR number to changelog
* feat(@vates/iscsi): rename allowedInitiatorAddresses, report refused connections
`allowedAddresses` did not tell whose addresses: these are the source
addresses of the initiators. `onConnectionRefused` reports each refused
one, e.g. so that a caller can tell that an initiator connects through a
NAT.
---------
Co-authored-by: Florent BEAUCHAMP <florent.beauchamp@vates.tech>
The CHAP interop test failed on CI with ENXIO on /dev/sdb (master 641f98878,
and #10549), right after waitForDevice() returned the disk.
waitForDevice() took a nonzero /sys/class/block/<name>/size as proof the disk
was ready, but the sd driver sets the size before device_add_disk(), which
creates the sysfs entry, then the /dev node, then registers the block device:
open() fails with ENOENT or ENXIO until then. Polling sysfs in a tight loop
after login hit that window in 15 of 100 logins.
isDeviceReady() now opens the device, and treats ENXIO and ENOENT as not ready
yet.
* feat(fs, backups): don't concatenate buffers but write them directly
maximum throughput with a perfect source and no write goes from
4.5 to 9.6GB/s
* fix: remove Buffer concat in qcow2 stream generationonly needed for last block
* feat(nbd-client): support nbd insecure networks
* feat(xo-web): support insecure nbd
also add a confirm on insecure enabling
The dialog is titled "Enable insecure NBD?" and says: "The disk data of the backups will travel unencrypted on this network. Only enable it on a network dedicated to backups and isolated from the VMs and the users (dedicated VLAN or physical network)." Both texts are new entries in messages.js, and the dialog itself is a new confirmInsecureNbd() helper in xo/index.js.
It appears in two places:
Pool Network tab (tab-network.js):
The dialog only appears when switching a network to insecure NBD. Reselecting it on a network that already has it, or switching to "NBD" or "No NBD", doesn't ask.
On cancel, nothing is sent and the drop-down keeps showing the network's current setting.
Network creation form (new/network/index.js):
The dialog appears on "Create" when insecure NBD is selected (bonded and private networks are never affected).
On cancel, the network isn't created and the form stays as is, with no error and no redirect.
* feat: update xo5,xo6 and lite ui for this settings
---------
Co-authored-by: MlssFrncJrg <119158464+MelissaFrncJrg@users.noreply.github.com>