diff --git a/@xen-orchestra/xapi/vm.mjs b/@xen-orchestra/xapi/vm.mjs index 817dd52d7fe..bdc8a2a3d89 100644 --- a/@xen-orchestra/xapi/vm.mjs +++ b/@xen-orchestra/xapi/vm.mjs @@ -599,6 +599,33 @@ class Vm { } } + async coalesceLeaf($defer, vmRef) { + try { + await this.callAsync('VM.suspend', vmRef) + $defer(() => this.callAsync('VM.resume', vmRef, false, true)) + } catch (error) { + if (error.code !== 'VM_BAD_POWER_STATE') { + throw error + } + + const powerState = error.params[2].toLowerCase() + if (powerState !== 'halted' && powerState !== 'suspended') { + throw error + } + } + + // plugin doc: https://docs.xenserver.com/en-us/xenserver/8/storage/manage.html#reclaim-space-by-using-the-offline-coalesce-tool + // result can be: `Success` or `VM has no leaf-coalesceable VDIs` + // https://github.com/xapi-project/sm/blob/eb292457c5fd5f00f6fc82454a915068ab15aa6f/drivers/coalesce-leaf#L48 + const result = await this.callAsync('host.call_plugin', this.pool.master, 'coalesce-leaf', 'leaf-coalesce', { + vm_uuid: await this.getField('VM', vmRef, 'uuid'), + }) + + if (result.toLowerCase() !== 'success') { + throw new Error(result) + } + } + async snapshot( $defer, vmRef, @@ -708,5 +735,6 @@ decorateClass(Vm, { checkpoint: defer, create: defer, export: defer, + coalesceLeaf: defer, snapshot: defer, }) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 4f694647f42..f0adf8714e7 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -14,6 +14,7 @@ - [i18n] Add Persian translation (based on the contribution made by [@Jokar-xen](https://github.com/Jokar-xen)) (PR [#7775](https://github.com/vatesfr/xen-orchestra/pull/7775)) - [i18n] Improve Russian translation (Thanks [@TristisOris](https://github.com/TristisOris)!) (PR [#7807](https://github.com/vatesfr/xen-orchestra/pull/7807)) - [REST API] Expose XO6 dashboard informations at the `/rest/v0/dashboard` endpoint (PR [#7823](https://github.com/vatesfr/xen-orchestra/pull/7823)) +- [VM/Advanced] Possibility to manually [_Coalesce Leaf_](https://docs.xenserver.com/en-us/xenserver/8/storage/manage.html#reclaim-space-by-using-the-offline-coalesce-tool) [#7757](https://github.com/vatesfr/xen-orchestra/issues/7757) (PR [#7810](https://github.com/vatesfr/xen-orchestra/pull/7810)) ### Bug fixes @@ -35,6 +36,7 @@ +- @xen-orchestra/xapi minor - xo-server minor - xo-web minor diff --git a/packages/xo-server/src/api/vm.mjs b/packages/xo-server/src/api/vm.mjs index 0232e26ed26..b175a18f07b 100644 --- a/packages/xo-server/src/api/vm.mjs +++ b/packages/xo-server/src/api/vm.mjs @@ -1826,3 +1826,15 @@ deleteVgpu.params = { deleteVgpu.resolve = { vgpu: ['vgpu', 'vgpu', ''], } + +// ------------------------------------------------------------------- + +export async function coalesceLeaf({ vm }) { + await this.getXapi(vm).VM_coalesceLeaf(vm._xapiRef) +} +coalesceLeaf.params = { + id: { type: 'string' }, +} +coalesceLeaf.resolve = { + vm: ['id', 'VM', 'administrate'], +} diff --git a/packages/xo-web/src/common/intl/messages.js b/packages/xo-web/src/common/intl/messages.js index 4ca95daa240..358f8e136f4 100644 --- a/packages/xo-web/src/common/intl/messages.js +++ b/packages/xo-web/src/common/intl/messages.js @@ -1452,6 +1452,9 @@ const messages = { 'If the VTPM is in use, removing it will result in a dangerous data loss. Are you sure you want to remove the VTPM?', infoUnknownPciOnNonRunningVm: "When a VM is offline, it's not attached to any host, and therefore, it's impossible to determine the associated PCI devices, as it depends on the hardware environment in which it would be deployed.", + coalesceLeaf: 'Coalesce leaf', + coalesceLeafSuccess: 'Coalesce leaf success', + coalesceLeafSuspendVm: 'This will suspend the VM during the operation. Do you want to continue?', poolAutoPoweronDisabled: 'Auto power on is disabled at pool level, click to fix automatically.', vmRemoveButton: 'Remove', vmConvertToTemplateButton: 'Convert to template', diff --git a/packages/xo-web/src/common/xo/index.js b/packages/xo-web/src/common/xo/index.js index 5ded951fce0..672288f38d1 100644 --- a/packages/xo-web/src/common/xo/index.js +++ b/packages/xo-web/src/common/xo/index.js @@ -2004,6 +2004,18 @@ export const deleteVms = async vms => { } } +export const coalesceLeafVm = async vm => { + if (vm.power_state !== 'Halted' && vm.power_state !== 'Suspended') { + await confirm({ + title: _('coalesceLeaf'), + body: _('coalesceLeafSuspendVm'), + }) + } + await _call('vm.coalesceLeaf', { id: resolveId(vm) }) + + success(_('coalesceLeaf'), _('coalesceLeafSuccess')) +} + export const importBackup = ({ remote, file, sr }) => _call('vm.importBackup', resolveIds({ remote, file, sr })) export const importDeltaBackup = ({ remote, file, sr, mapVdisSrs }) => diff --git a/packages/xo-web/src/icons.scss b/packages/xo-web/src/icons.scss index af5f6a07c5a..56bc7981d0e 100644 --- a/packages/xo-web/src/icons.scss +++ b/packages/xo-web/src/icons.scss @@ -511,6 +511,11 @@ @extend .fa; @extend .fa-fire; } + + &-coalesce-leaf { + @extend .fa; + @extend .fa-compress; + } } // Generic states diff --git a/packages/xo-web/src/xo-app/vm/tab-advanced.js b/packages/xo-web/src/xo-app/vm/tab-advanced.js index 7b6cd177d92..930c8087e4a 100644 --- a/packages/xo-web/src/xo-app/vm/tab-advanced.js +++ b/packages/xo-web/src/xo-app/vm/tab-advanced.js @@ -41,6 +41,7 @@ import { getVmsHaValues, isPciPassthroughAvailable, isVmRunning, + coalesceLeafVm, pauseVm, recoveryStartVm, removeAcl, @@ -698,6 +699,13 @@ export default class TabAdvanced extends Component { icon='vm-suspend' labelId='suspendVmLabel' /> + + )} {vm.power_state === 'Suspended' && ( @@ -756,6 +771,13 @@ export default class TabAdvanced extends Component { icon='vm-start' labelId='resumeVmLabel' /> +