From 2ebf18a636a3d71f60d395d4e049d9dfc4710979 Mon Sep 17 00:00:00 2001
From: Joris K <43381087+J0ris-K@users.noreply.github.com>
Date: Thu, 27 Aug 2026 10:56:34 +0200
Subject: [PATCH] feat(xo-lite): add vm tree actions and loader (#10304)
---
@xen-orchestra/lite/CHANGELOG.md | 1 +
.../lite/src/components/infra/InfraVmItem.vue | 29 ++++++++++++
.../lite/src/components/vm/VmHeader.vue | 11 ++++-
.../vm/components/actions/VmTreeActions.vue | 40 ++++++++++++++++
.../vm/composables/vm-operation.composable.ts | 46 +++++++++++++++++++
.../lite/src/modules/vm/utils/vm.util.ts | 21 +++++++++
.../components/object-icon/VtsObjectIcon.vue | 4 +-
@xen-orchestra/web-core/lib/locales/en.json | 2 +
@xen-orchestra/web-core/lib/locales/fr.json | 2 +
.../src/modules/vm/components/VmHeader.vue | 5 +-
.../vm/composables/xo-vm-utils.composable.ts | 5 +-
11 files changed, 158 insertions(+), 8 deletions(-)
create mode 100644 @xen-orchestra/lite/src/modules/vm/components/actions/VmTreeActions.vue
create mode 100644 @xen-orchestra/lite/src/modules/vm/composables/vm-operation.composable.ts
diff --git a/@xen-orchestra/lite/CHANGELOG.md b/@xen-orchestra/lite/CHANGELOG.md
index bbeb8f93c2..1faa9223c2 100644
--- a/@xen-orchestra/lite/CHANGELOG.md
+++ b/@xen-orchestra/lite/CHANGELOG.md
@@ -2,6 +2,7 @@
## **next**
+- [Treeview] Add VM tree actions (PR [#10304](https://github.com/vatesfr/xen-orchestra/pull/10304))
- [Pool/networks] Add the possibility to create new network or bonded network (PR [#10145](https://github.com/vatesfr/xen-orchestra/pull/10145))
- [VM] Add VDIs page with table and side panel (PR [#10269](https://github.com/vatesfr/xen-orchestra/pull/10269))
- [Host/Network] Add ability to rescan physical network interfaces (PIFs) (PR [#10147](https://github.com/vatesfr/xen-orchestra/pull/10147))
diff --git a/@xen-orchestra/lite/src/components/infra/InfraVmItem.vue b/@xen-orchestra/lite/src/components/infra/InfraVmItem.vue
index 62e7c05dba..e217bed34c 100644
--- a/@xen-orchestra/lite/src/components/infra/InfraVmItem.vue
+++ b/@xen-orchestra/lite/src/components/infra/InfraVmItem.vue
@@ -5,6 +5,24 @@
+
+
+
+
+
+
+
+
+
@@ -12,17 +30,26 @@
diff --git a/@xen-orchestra/lite/src/components/vm/VmHeader.vue b/@xen-orchestra/lite/src/components/vm/VmHeader.vue
index 6da3c2e5e7..b521af528c 100644
--- a/@xen-orchestra/lite/src/components/vm/VmHeader.vue
+++ b/@xen-orchestra/lite/src/components/vm/VmHeader.vue
@@ -2,7 +2,13 @@
{{ vm?.name_label }}
-
+
@@ -40,6 +46,7 @@ import VmActionMigrateItem from '@/components/vm/VmActionItems/VmActionMigrateIt
import VmActionPowerStateItems from '@/components/vm/VmActionItems/VmActionPowerStateItems.vue'
import VmActionSnapshotItem from '@/components/vm/VmActionItems/VmActionSnapshotItem.vue'
import type { XenApiVm } from '@/libs/xen-api/xen-api.types.ts'
+import { useVmOperation } from '@/modules/vm/composables/vm-operation.composable.ts'
import { useVmStore } from '@/stores/xen-api/vm.store.ts'
import MenuList from '@core/components/menu/MenuList.vue'
import VtsObjectIcon from '@core/components/object-icon/VtsObjectIcon.vue'
@@ -58,4 +65,6 @@ const { getByUuid: getVmByUuid } = useVmStore().subscribe()
const route = useRoute<'/vm/[uuid]'>()
const vm = computed(() => getVmByUuid(route.params.uuid as XenApiVm['uuid']))
+
+const { isChangingState, currentOperation } = useVmOperation(vm)
diff --git a/@xen-orchestra/lite/src/modules/vm/components/actions/VmTreeActions.vue b/@xen-orchestra/lite/src/modules/vm/components/actions/VmTreeActions.vue
new file mode 100644
index 0000000000..7548fdc4a2
--- /dev/null
+++ b/@xen-orchestra/lite/src/modules/vm/components/actions/VmTreeActions.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/@xen-orchestra/lite/src/modules/vm/composables/vm-operation.composable.ts b/@xen-orchestra/lite/src/modules/vm/composables/vm-operation.composable.ts
new file mode 100644
index 0000000000..24a1a2971d
--- /dev/null
+++ b/@xen-orchestra/lite/src/modules/vm/composables/vm-operation.composable.ts
@@ -0,0 +1,46 @@
+import { isVmOperationPending } from '@/libs/vm.ts'
+import type { XenApiVm } from '@/libs/xen-api/xen-api.types.ts'
+import { CHANGING_STATE_OPERATIONS } from '@/modules/vm/utils/vm.util.ts'
+import { useMapper } from '@core/packages/mapper'
+import { computed, type MaybeRefOrGetter, toValue } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+export function useVmOperation(rawVm: MaybeRefOrGetter) {
+ const { t } = useI18n()
+
+ const vm = toValue(rawVm)
+
+ const isChangingState = computed(() => {
+ return vm !== undefined && isVmOperationPending(vm, CHANGING_STATE_OPERATIONS)
+ })
+
+ const currentOperation = useMapper(
+ () => Object.values(vm?.current_operations ?? {})[0],
+ {
+ start: t('operation:start'),
+ start_on: t('operation:start-on-host'),
+ pause: t('operation:pause'),
+ unpause: t('operation:unpause'),
+ resume: t('operation:resume'),
+ suspend: t('operation:suspend'),
+ clean_reboot: t('operation:clean-reboot'),
+ hard_reboot: t('operation:force-reboot'),
+ shutdown: t('operation:shutdown'),
+ clean_shutdown: t('operation:clean-shutdown'),
+ hard_shutdown: t('operation:force-shutdown'),
+ snapshot: t('operation:snapshot'),
+ destroy: t('operation:destroy'),
+ clone: t('operation:duplicate'),
+ copy: t('operation:duplicate'),
+ export: t('operation:export'),
+ import: t('operation:import'),
+ unknown: '',
+ },
+ 'unknown'
+ )
+
+ return {
+ isChangingState,
+ currentOperation,
+ }
+}
diff --git a/@xen-orchestra/lite/src/modules/vm/utils/vm.util.ts b/@xen-orchestra/lite/src/modules/vm/utils/vm.util.ts
index 68f24551e9..452b2e385b 100644
--- a/@xen-orchestra/lite/src/modules/vm/utils/vm.util.ts
+++ b/@xen-orchestra/lite/src/modules/vm/utils/vm.util.ts
@@ -1,5 +1,26 @@
+import { VM_OPERATION } from '@/libs/xen-api/xen-api.enums.ts'
import type { XenApiVmGuestMetrics } from '@/libs/xen-api/xen-api.types.ts'
+export const CHANGING_STATE_OPERATIONS: VM_OPERATION[] = [
+ VM_OPERATION.START,
+ VM_OPERATION.START_ON,
+ VM_OPERATION.PAUSE,
+ VM_OPERATION.UNPAUSE,
+ VM_OPERATION.RESUME,
+ VM_OPERATION.SUSPEND,
+ VM_OPERATION.CLEAN_REBOOT,
+ VM_OPERATION.HARD_REBOOT,
+ VM_OPERATION.SHUTDOWN,
+ VM_OPERATION.CLEAN_SHUTDOWN,
+ VM_OPERATION.HARD_SHUTDOWN,
+ VM_OPERATION.SNAPSHOT,
+ VM_OPERATION.DESTROY,
+ VM_OPERATION.CLONE,
+ VM_OPERATION.COPY,
+ VM_OPERATION.EXPORT,
+ VM_OPERATION.IMPORT,
+]
+
/**
* Adapted from `getVmGuestToolsProps` in xo-server (`packages/xo-server/src/xapi-object-to-xo.mjs`),
* which computes `managementAgentDetected` and `pvDriversDetected` on XO VM objects.
diff --git a/@xen-orchestra/web-core/lib/components/object-icon/VtsObjectIcon.vue b/@xen-orchestra/web-core/lib/components/object-icon/VtsObjectIcon.vue
index 52de012ed9..04f3ca123e 100644
--- a/@xen-orchestra/web-core/lib/components/object-icon/VtsObjectIcon.vue
+++ b/@xen-orchestra/web-core/lib/components/object-icon/VtsObjectIcon.vue
@@ -1,5 +1,5 @@
-
+