mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-09-10 22:14:48 -05:00
feat(xo-lite): add vm tree actions and loader (#10304)
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -5,6 +5,24 @@
|
||||
<template #icon>
|
||||
<VtsObjectIcon size="medium" :state="vmPowerState!" type="vm" />
|
||||
</template>
|
||||
<template #addons>
|
||||
<UiLoader v-if="isChangingState" v-tooltip="{ placement: 'top', content: currentOperation }" />
|
||||
<MenuList placement="bottom-start">
|
||||
<template #trigger="{ open }">
|
||||
<UiButtonIcon
|
||||
v-tooltip="{
|
||||
placement: 'top',
|
||||
content: t('quick-actions'),
|
||||
}"
|
||||
icon="action:more-actions"
|
||||
accent="brand"
|
||||
size="small"
|
||||
@click="open($event)"
|
||||
/>
|
||||
</template>
|
||||
<VmTreeActions :vm-opaque-ref />
|
||||
</MenuList>
|
||||
</template>
|
||||
</UiTreeItemLabel>
|
||||
</VtsTreeItem>
|
||||
</template>
|
||||
@@ -12,17 +30,26 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VM_POWER_STATE } from '@/libs/xen-api/xen-api.enums.ts'
|
||||
import type { XenApiVm } from '@/libs/xen-api/xen-api.types.ts'
|
||||
import VmTreeActions from '@/modules/vm/components/actions/VmTreeActions.vue'
|
||||
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'
|
||||
import VtsTreeItem from '@core/components/tree/VtsTreeItem.vue'
|
||||
import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
|
||||
import UiLoader from '@core/components/ui/loader/UiLoader.vue'
|
||||
import UiTreeItemLabel from '@core/components/ui/tree-item-label/UiTreeItemLabel.vue'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive.ts'
|
||||
import { useIntersectionObserver } from '@vueuse/core'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { vmOpaqueRef } = defineProps<{
|
||||
vmOpaqueRef: XenApiVm['$ref']
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { getByOpaqueRef } = useVmStore().subscribe()
|
||||
const vm = computed(() => getByOpaqueRef(vmOpaqueRef))
|
||||
const rootElement = ref()
|
||||
@@ -36,4 +63,6 @@ const { stop } = useIntersectionObserver(rootElement, ([entry]) => {
|
||||
})
|
||||
|
||||
const vmPowerState = computed(() => vm.value?.power_state.toLowerCase() as Lowercase<VM_POWER_STATE> | undefined)
|
||||
|
||||
const { isChangingState, currentOperation } = useVmOperation(vm)
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
<UiHeadBar icon="object:vm">
|
||||
{{ vm?.name_label }}
|
||||
<template #icon>
|
||||
<VtsObjectIcon size="medium" :state="toLower(vm?.power_state)" type="vm" />
|
||||
<VtsObjectIcon
|
||||
size="medium"
|
||||
:state="toLower(vm?.power_state)"
|
||||
type="vm"
|
||||
:busy="isChangingState"
|
||||
:busy-tooltip="currentOperation"
|
||||
/>
|
||||
</template>
|
||||
<template #actions>
|
||||
<MenuList v-if="vm !== undefined" placement="bottom-start">
|
||||
@@ -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)
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<MenuItem class="change-state" icon="action:change-state">
|
||||
{{ t('action:change-state') }}
|
||||
<template #submenu>
|
||||
<VmActionPowerStateItems :vm-refs />
|
||||
</template>
|
||||
</MenuItem>
|
||||
<VmActionSnapshotItem :vm-refs />
|
||||
<VmActionCopyItem :selected-refs="vmRefs" is-single-action />
|
||||
<VmActionExportItem :vm-refs is-single-action />
|
||||
<MenuSeparator />
|
||||
<VmActionDeleteItem :vm-refs />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import VmActionCopyItem from '@/components/vm/VmActionItems/VmActionCopyItem.vue'
|
||||
import VmActionDeleteItem from '@/components/vm/VmActionItems/VmActionDeleteItem.vue'
|
||||
import VmActionExportItem from '@/components/vm/VmActionItems/VmActionExportItem.vue'
|
||||
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 MenuItem from '@core/components/menu/MenuItem.vue'
|
||||
import MenuSeparator from '@core/components/menu/MenuSeparator.vue'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { vmOpaqueRef } = defineProps<{
|
||||
vmOpaqueRef: XenApiVm['$ref']
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const vmRefs = computed(() => [vmOpaqueRef])
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.change-state {
|
||||
color: var(--color-brand-txt-base);
|
||||
}
|
||||
</style>
|
||||
@@ -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<XenApiVm | undefined>) {
|
||||
const { t } = useI18n()
|
||||
|
||||
const vm = toValue(rawVm)
|
||||
|
||||
const isChangingState = computed(() => {
|
||||
return vm !== undefined && isVmOperationPending(vm, CHANGING_STATE_OPERATIONS)
|
||||
})
|
||||
|
||||
const currentOperation = useMapper<string, string>(
|
||||
() => 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,
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<VtsIcon :name="iconName" :size />
|
||||
<VtsIcon :name="iconName" :size :busy :busy-tooltip />
|
||||
</template>
|
||||
|
||||
<script generic="TType extends ObjectType, TState extends ObjectState<TType>" lang="ts" setup>
|
||||
@@ -11,6 +11,8 @@ const { type, state } = defineProps<{
|
||||
type: TType
|
||||
state?: TState
|
||||
size: IconSize
|
||||
busy?: boolean
|
||||
busyTooltip?: string
|
||||
}>()
|
||||
|
||||
const iconName = computed(() => (state ? objectIcon(type, state) : icon(`object:${type}`)))
|
||||
|
||||
@@ -848,8 +848,10 @@
|
||||
"operation:duplicate": "Duplicating",
|
||||
"operation:enable": "Enabling",
|
||||
"operation:evacuate": "Evacuating",
|
||||
"operation:export": "Exporting",
|
||||
"operation:force-reboot": "Force rebooting",
|
||||
"operation:force-shutdown": "Force shutting down",
|
||||
"operation:import": "Importing",
|
||||
"operation:pause": "Pausing",
|
||||
"operation:resume": "Resuming",
|
||||
"operation:shutdown": "Shutting down",
|
||||
|
||||
@@ -848,8 +848,10 @@
|
||||
"operation:duplicate": "Duplication",
|
||||
"operation:enable": "Activation",
|
||||
"operation:evacuate": "Évacuation",
|
||||
"operation:export": "Exportation",
|
||||
"operation:force-reboot": "Redémarrage forcé",
|
||||
"operation:force-shutdown": "Arrêt forcé",
|
||||
"operation:import": "Importation",
|
||||
"operation:pause": "Mise en pause",
|
||||
"operation:resume": "Reprise",
|
||||
"operation:shutdown": "Arrêt",
|
||||
|
||||
@@ -3,14 +3,11 @@
|
||||
{{ vm.name_label }}
|
||||
<template #icon>
|
||||
<VtsObjectIcon
|
||||
v-tooltip="{
|
||||
placement: 'top',
|
||||
content: currentOperation ? currentOperation : '',
|
||||
}"
|
||||
size="medium"
|
||||
:state="toLower(vm.power_state)"
|
||||
type="vm"
|
||||
:busy="isChangingState"
|
||||
:busy-tooltip="currentOperation"
|
||||
/>
|
||||
</template>
|
||||
<template #actions>
|
||||
|
||||
@@ -114,14 +114,15 @@ export function useXoVmUtils(rawVm: MaybeRefOrGetter<FrontXoVm>) {
|
||||
resume: t('operation:resume'),
|
||||
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'),
|
||||
destroy: t('operation:destroy'),
|
||||
snapshot: t('operation:snapshot'),
|
||||
clone: t('operation:duplicate'),
|
||||
copy: t('operation:duplicate'),
|
||||
export: t('operation:duplicate'),
|
||||
import: t('operation:duplicate'),
|
||||
export: t('operation:export'),
|
||||
import: t('operation:import'),
|
||||
unknown: '',
|
||||
},
|
||||
'unknown'
|
||||
|
||||
Reference in New Issue
Block a user