mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-09-10 22:14:48 -05:00
feat(xo6/host): host action button start shutdown (#10088)
* feat(xo6): add disable and enable host button * Add button enable disable * remove route map file * Refacto for PR * Add modal * refacto for PR * change key i18n * fix i18n * fix route map * change modale to VtsActionModale * refacto for MR * fix type VtsActionModal * refacto for MR * add enable disable un treeview * refacto treeview * wip * add shutdown and start button and job * Update changelog and remove route map * add check on disable button * fix job error type * update button for menuitem * wip i18n * add vtsaction modal * tests ok * fix build * refacto for mr * fix fr.json * refacto for PR * add check pending action * add check host not halted * fix enable disable button while host shutdown * returns from MR * fix changestate host header * wip * wip * add check pending operations * last fix for PR * fix menuItem in mobile vue * remove route map * returns from MR * fix start button * Add changelog
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<span>{{ modalTexts.title }}</span>
|
||||
</template>
|
||||
<template #content>
|
||||
<span>{{ modalTexts.message }}</span>
|
||||
<span v-if="modalTexts.message">{{ modalTexts.message }}</span>
|
||||
</template>
|
||||
<template #buttons>
|
||||
<VtsModalCancelButton>{{ t('action:go-back') }}</VtsModalCancelButton>
|
||||
@@ -27,7 +27,7 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
type ActionTexts = {
|
||||
title: string
|
||||
message: string
|
||||
message?: string
|
||||
action: string
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ const { action, object, hostName } = defineProps<
|
||||
}
|
||||
>()
|
||||
|
||||
const defaultActionByObject: { [O in ObjectType]: ActionsByObject[O] } = {
|
||||
const defaultActionByObject: ActionsByObject = {
|
||||
vm: 'shutdown',
|
||||
host: 'disable',
|
||||
}
|
||||
@@ -95,6 +95,15 @@ const textMappingsByObject: TextMappingByObject = {
|
||||
message: t('modal:host-disable-message'),
|
||||
action: t('action:disable-host'),
|
||||
},
|
||||
shutdown: {
|
||||
title: t('modal:confirm-host-shutdown', { host: hostName }),
|
||||
message: t('modal:host-shutdown-message'),
|
||||
action: t('action:shutdown-host'),
|
||||
},
|
||||
start: {
|
||||
title: t('modal:confirm-host-start', { host: hostName }),
|
||||
action: t('action:start-host'),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
"action:show-less": "Show less",
|
||||
"action:show-more": "Show more",
|
||||
"action:shutdown": "Shutdown",
|
||||
"action:shutdown-host": "Shutdown host",
|
||||
"action:sidebar-close": "Close sidebar",
|
||||
"action:sidebar-lock": "Lock sidebar open",
|
||||
"action:sidebar-open": "Open sidebar",
|
||||
@@ -131,6 +132,7 @@
|
||||
"action:sort-by": "Sort by",
|
||||
"action:sort-descending": "Sort descending",
|
||||
"action:start": "Start",
|
||||
"action:start-host": "Start host",
|
||||
"action:start-on-host": "Start on specific host",
|
||||
"action:suspend": "Suspend",
|
||||
"action:turn-into-group": "Turn into group",
|
||||
@@ -491,6 +493,14 @@
|
||||
"job:host-disable:bad-power-state": "Host is not running",
|
||||
"job:host-disable:missing-host": "Host is missing",
|
||||
"job:host-enable:missing-host": "Host is missing",
|
||||
"job:host-shutdown:bad-power-state": "Host already halted",
|
||||
"job:host-shutdown:in-progress": "Host shut down in progress…",
|
||||
"job:host-shutdown:missing-host": "No host to shut down",
|
||||
"job:host-start:bad-power-state": "Host already started",
|
||||
"job:host-start:bad-power-state-not-halted": "Host not halted",
|
||||
"job:host-start:in-progress": "Host start in progress…",
|
||||
"job:host-start:missing-host": "No host to start",
|
||||
"job:host-start:power-on-disabled": "Power on disabled",
|
||||
"job:migrate:in-progress": "Migrate in progess…",
|
||||
"job:network-delete:has-n-physical-pif-connected": "The network has one physical PIF connected | The network has {n} physical PIFs connected",
|
||||
"job:network-delete:has-n-vif-attached": "The network has one VIF attached | The network has {n} VIFs attached",
|
||||
@@ -640,6 +650,8 @@
|
||||
"modal:action:vm-shutdown": "@:action:shutdown VM",
|
||||
"modal:confirm-host-disable": "Are you sure you want to disable {host}?",
|
||||
"modal:confirm-host-enable": "Are you sure you want to enable {host}?",
|
||||
"modal:confirm-host-shutdown": "Are you sure you want to shut down {host}?",
|
||||
"modal:confirm-host-start": "Are you sure you want to start {host}?",
|
||||
"modal:confirm-vm-force-reboot": "Confirm VM force reboot",
|
||||
"modal:confirm-vm-force-shutdown": "Confirm VM force shutdown",
|
||||
"modal:confirm-vm-reboot": "Confirm VM reboot",
|
||||
@@ -647,6 +659,7 @@
|
||||
"modal:host-disable-message": "The host will be marked unavailable for starting or receiving new VMs. Already-running VMs are not affected.",
|
||||
"modal:host-enable-message": "The host becomes available again for starting or receiving VMs.",
|
||||
"modal:pool-disconnect-message": "The pool will no longer be manageable from XO. You can reconnect it later.",
|
||||
"modal:host-shutdown-message": "This will power off the host. Any VMs still running on it will be shut down immediately unless you've migrated them first.",
|
||||
"modal:vm-force-reboot-message": "This will immediately reboot the virtual machine without a clean shutdown. Data loss may occur and ongoing processes will be interrupted.",
|
||||
"modal:vm-force-shutdown-message": "This will immediately power off the virtual machine without a clean shutdown. Data loss may occur and ongoing processes will be interrupted.",
|
||||
"modal:vm-reboot-message": "Restarts the virtual machine gracefully by shutting it down and starting it again. Running processes will be closed properly.",
|
||||
@@ -790,10 +803,12 @@
|
||||
"operation:clean-shutdown": "Shutting down",
|
||||
"operation:destroy": "Deleting",
|
||||
"operation:duplicate": "Duplicating",
|
||||
"operation:enable": "Enabling",
|
||||
"operation:force-reboot": "Force rebooting",
|
||||
"operation:force-shutdown": "Force shutting down",
|
||||
"operation:pause": "Pausing",
|
||||
"operation:resume": "Resuming",
|
||||
"operation:shutdown": "Shutting down",
|
||||
"operation:snapshot": "Snapshotting",
|
||||
"operation:start": "Starting",
|
||||
"operation:start-on-host": "Starting on host",
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
"action:show-less": "Voir moins",
|
||||
"action:show-more": "Voir plus",
|
||||
"action:shutdown": "Arrêter",
|
||||
"action:shutdown-host": "Arrêter l'hôte",
|
||||
"action:sidebar-close": "Fermer la barre latérale",
|
||||
"action:sidebar-lock": "Verrouiller la barre latérale",
|
||||
"action:sidebar-open": "Ouvrir la barre latérale",
|
||||
@@ -131,6 +132,7 @@
|
||||
"action:sort-by": "Trier par",
|
||||
"action:sort-descending": "Trier par ordre décroissant",
|
||||
"action:start": "Démarrer",
|
||||
"action:start-host": "Démarrer l'hôte",
|
||||
"action:start-on-host": "Démarrer sur un hôte spécifique",
|
||||
"action:suspend": "Suspendre",
|
||||
"action:turn-into-group": "Transformer en groupe",
|
||||
@@ -491,6 +493,14 @@
|
||||
"job:host-disable:bad-power-state": "L'hôte n'est pas lancé",
|
||||
"job:host-disable:missing-host": "L'hôte est manquant",
|
||||
"job:host-enable:missing-host": "L'hôte est manquant",
|
||||
"job:host-shutdown:bad-power-state": "Hôte déjà arrêté",
|
||||
"job:host-shutdown:in-progress": "Arrêt de l'hôte en cours…",
|
||||
"job:host-shutdown:missing-host": "Aucun hôte à arrêter",
|
||||
"job:host-start:bad-power-state": "Hôte déjà démarré",
|
||||
"job:host-start:bad-power-state-not-halted": "Hôte non arrêté",
|
||||
"job:host-start:in-progress": "Démarrage de l'hôte en cours…",
|
||||
"job:host-start:missing-host": "Pas d'hôte à démarrer",
|
||||
"job:host-start:power-on-disabled": "Démarrage désactivé",
|
||||
"job:migrate:in-progress": "Migration en cours…",
|
||||
"job:network-delete:has-n-physical-pif-connected": "Le réseau a un PIF physique connecté | Le réseau a {n} PIFs physiques connectés",
|
||||
"job:network-delete:has-n-vif-attached": "Le réseau a un VIF attaché | Le réseau a {n} VIFs attachés",
|
||||
@@ -638,14 +648,17 @@
|
||||
"modal:action:vm-force-shutdown": "@:action:force-shutdown la VM",
|
||||
"modal:action:vm-reboot": "@:action:reboot la VM",
|
||||
"modal:action:vm-shutdown": "@:action:shutdown la VM",
|
||||
"modal:confirm-host-disable": "Êtes-vous sûr que vous voulez désactiver {host}?",
|
||||
"modal:confirm-host-enable": "Êtes-vous sûr que vous voulez activer {host}?",
|
||||
"modal:confirm-host-disable": "Êtes-vous sûr que vous voulez désactiver {host} ?",
|
||||
"modal:confirm-host-enable": "Êtes-vous sûr que vous voulez activer {host} ?",
|
||||
"modal:confirm-host-shutdown": "Êtes-vous sûr de vouloir arrêter {host} ?",
|
||||
"modal:confirm-host-start": "Êtes-vous sûr de vouloir démarrer {host} ?",
|
||||
"modal:confirm-vm-force-reboot": "Confirmer le redémarrage forcé de la VM",
|
||||
"modal:confirm-vm-force-shutdown": "Confirmer l'arrêt forcé de la VM",
|
||||
"modal:confirm-vm-reboot": "Confirmer le redémarrage de la VM",
|
||||
"modal:confirm-vm-shutdown": "Confirmer l'arrêt de la VM",
|
||||
"modal:host-disable-message": "L’hôte sera marqué comme indisponible pour le démarrage ou la réception de nouvelles VMs. Les VMs déjà en cours d’exécution ne sont pas affectées.",
|
||||
"modal:host-enable-message": "L’hôte redevient disponible pour démarrer ou recevoir des VMs.",
|
||||
"modal:host-disable-message": "L'hôte sera marqué comme indisponible pour le démarrage ou la réception de nouvelles VMs. Les VMs déjà en cours d’exécution ne sont pas affectées.",
|
||||
"modal:host-enable-message": "L'hôte redevient disponible pour démarrer ou recevoir des VMs.",
|
||||
"modal:host-shutdown-message": "Cela éteindra l'hôte. Toute VM encore en cours d’exécution sera immédiatement arrêtée, à moins que vous ne l'ayez migrée au préalable.",
|
||||
"modal:pool-disconnect-message": "Le pool ne pourra plus être géré depuis XO. Vous pourrez le reconnecter ultérieurement.",
|
||||
"modal:vm-force-reboot-message": "Cela redémarrera immédiatement la VM sans arrêt propre. Des pertes de données peuvent survenir et les processus en cours seront interrompus.",
|
||||
"modal:vm-force-shutdown-message": "Cela éteindra immédiatement la VM sans arrêt propre. Des pertes de données peuvent survenir et les processus en cours seront interrompus.",
|
||||
@@ -790,10 +803,12 @@
|
||||
"operation:clean-shutdown": "Arrêt",
|
||||
"operation:destroy": "Suppression",
|
||||
"operation:duplicate": "Duplication",
|
||||
"operation:enable": "Activation",
|
||||
"operation:force-reboot": "Redémarrage forcé",
|
||||
"operation:force-shutdown": "Arrêt forcé",
|
||||
"operation:pause": "Mise en pause",
|
||||
"operation:resume": "Reprise",
|
||||
"operation:shutdown": "Arrêt",
|
||||
"operation:snapshot": "Création d'instantané",
|
||||
"operation:start": "Démarrage",
|
||||
"operation:start-on-host": "Démarrage sur l'hôte",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export type ObjectType = 'vm' | 'host'
|
||||
|
||||
export type VmActions = 'reboot' | 'shutdown' | 'force-reboot' | 'force-shutdown'
|
||||
export type HostActions = 'enable' | 'disable'
|
||||
export type HostActions = 'enable' | 'disable' | 'shutdown' | 'start'
|
||||
|
||||
export type VmBlockedOperations =
|
||||
| 'clean_shutdown'
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
<template>
|
||||
<UiHeadBar>
|
||||
<template #icon>
|
||||
<VtsObjectIcon size="medium" type="host" :state="toLower(host.power_state)" />
|
||||
</template>
|
||||
{{ host.name_label }}
|
||||
<template #icon>
|
||||
<VtsObjectIcon
|
||||
v-tooltip="{
|
||||
placement: 'top',
|
||||
content: currentOperation ? currentOperation : '',
|
||||
}"
|
||||
size="medium"
|
||||
type="host"
|
||||
:state="toLower(host.power_state)"
|
||||
:busy="isChangingState"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="isMaster" #status>
|
||||
<VtsIcon v-tooltip="t('master')" name="status:primary-circle" size="medium" />
|
||||
</template>
|
||||
@@ -11,6 +20,13 @@
|
||||
<UiLink size="medium" :to="{ name: '/vm/new', query: { poolid: host.$pool } }" icon="fa:plus">
|
||||
{{ t('new-vm') }}
|
||||
</UiLink>
|
||||
<MenuList v-if="!uiStore.isSmall" placement="bottom-end">
|
||||
<template #trigger="{ open }">
|
||||
<UiDropdownButton @click="open($event)">{{ t('action:change-state') }}</UiDropdownButton>
|
||||
</template>
|
||||
<HostPowerStateActions :host />
|
||||
</MenuList>
|
||||
|
||||
<MenuList placement="bottom-end">
|
||||
<template #trigger="{ open }">
|
||||
<UiButtonIcon
|
||||
@@ -73,7 +89,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import HostPowerStateActions from '@/modules/host/components/actions/HostPowerStateActions.vue'
|
||||
import HostMoreActions from '@/modules/host/components/HostMoreActions.vue'
|
||||
import { useXoHostUtils } from '@/modules/host/composables/xo-host-utils.composable.ts'
|
||||
import { type FrontXoHost, useXoHostCollection } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
|
||||
import VtsIcon from '@core/components/icon/VtsIcon.vue'
|
||||
@@ -82,9 +100,11 @@ import VtsObjectIcon from '@core/components/object-icon/VtsObjectIcon.vue'
|
||||
import TabItem from '@core/components/tab/TabItem.vue'
|
||||
import TabList from '@core/components/tab/TabList.vue'
|
||||
import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
|
||||
import UiDropdownButton from '@core/components/ui/dropdown-button/UiDropdownButton.vue'
|
||||
import UiHeadBar from '@core/components/ui/head-bar/UiHeadBar.vue'
|
||||
import UiLink from '@core/components/ui/link/UiLink.vue'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive.ts'
|
||||
import { useUiStore } from '@core/stores/ui.store.ts'
|
||||
import { toLower } from 'lodash-es'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -95,6 +115,10 @@ const { host } = defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const uiStore = useUiStore()
|
||||
|
||||
const { isChangingState, currentOperation } = useXoHostUtils(() => host)
|
||||
|
||||
const { buildXo5Route } = useXoRoutes()
|
||||
const xo5HostStatsHref = computed(() => buildXo5Route(`/hosts/${host.id}/stats`))
|
||||
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
<template>
|
||||
<HostDisableButton v-if="host.enabled" :host />
|
||||
<MenuItem v-if="showChangeStateButton && !uiStore.isSmall" icon="action:change-state" class="change-state">
|
||||
{{ t('action:change-state') }}
|
||||
<template #submenu>
|
||||
<HostPowerStateActions :host />
|
||||
</template>
|
||||
</MenuItem>
|
||||
<div v-if="uiStore.isSmall">
|
||||
<HostPowerStateActions :host />
|
||||
<VtsDivider type="stretch" />
|
||||
</div>
|
||||
<HostDisableButton v-if="host.enabled || isHostHalted" :host />
|
||||
<HostEnableButton v-else :host />
|
||||
<VtsDivider type="stretch" />
|
||||
<HostDownloadButton :host-id="host.id" />
|
||||
@@ -9,10 +19,29 @@
|
||||
import HostDisableButton from '@/modules/host/components/actions/disable/HostDisableButton.vue'
|
||||
import HostDownloadButton from '@/modules/host/components/actions/download/HostDownloadButton.vue'
|
||||
import HostEnableButton from '@/modules/host/components/actions/enable/HostEnableButton.vue'
|
||||
import HostPowerStateActions from '@/modules/host/components/actions/HostPowerStateActions.vue'
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import VtsDivider from '@core/components/divider/VtsDivider.vue'
|
||||
import MenuItem from '@core/components/menu/MenuItem.vue'
|
||||
import { useUiStore } from '@core/stores/ui.store.ts'
|
||||
import { HOST_POWER_STATE } from '@vates/types'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
defineProps<{
|
||||
const { host } = defineProps<{
|
||||
host: FrontXoHost
|
||||
showChangeStateButton?: boolean
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const uiStore = useUiStore()
|
||||
|
||||
const isHostHalted = computed(() => host.power_state === HOST_POWER_STATE.HALTED)
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.change-state {
|
||||
color: var(--color-brand-txt-base);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<HostShutdownButton v-if="isHostRunning" :host />
|
||||
<HostStartButton v-else :host />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import HostShutdownButton from '@/modules/host/components/actions/shutdown/HostShutdownButton.vue'
|
||||
import HostStartButton from '@/modules/host/components/actions/start/HostStartButton.vue'
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import { HOST_POWER_STATE } from '@vates/types'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const { host } = defineProps<{
|
||||
host: FrontXoHost
|
||||
}>()
|
||||
|
||||
const isHostRunning = computed(() => host.power_state === HOST_POWER_STATE.RUNNING)
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<MenuItem
|
||||
v-tooltip="!canShutdownHost && shutdownHostErrorMessage"
|
||||
:disabled="!canShutdownHost"
|
||||
icon="action:shutdown"
|
||||
:busy="isShuttingDownHost"
|
||||
@click="openShutdownHostModal()"
|
||||
>
|
||||
{{ t('action:shutdown') }}
|
||||
</MenuItem>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useXoHostShutdownJob } from '@/modules/host/jobs/xo-host-shutdown.job.ts'
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import MenuItem from '@core/components/menu/MenuItem.vue'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive.ts'
|
||||
import { useModal } from '@core/packages/modal/use-modal.ts'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { host } = defineProps<{
|
||||
host: FrontXoHost
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const {
|
||||
run: shutdownHost,
|
||||
canRun: canShutdownHost,
|
||||
isRunning: isShuttingDownHost,
|
||||
errorMessage: shutdownHostErrorMessage,
|
||||
} = useXoHostShutdownJob(() => host)
|
||||
|
||||
const openShutdownHostModal = useModal({
|
||||
component: import('@core/components/modal/VtsActionModal.vue'),
|
||||
props: {
|
||||
accent: 'warning',
|
||||
action: 'shutdown',
|
||||
object: 'host',
|
||||
hostName: host.name_label,
|
||||
icon: 'status:warning-picto',
|
||||
},
|
||||
onConfirm: () => shutdownHost(),
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<MenuItem
|
||||
v-tooltip="!canStartHost && startHostErrorMessage"
|
||||
:disabled="!canStartHost"
|
||||
icon="fa:play"
|
||||
:busy="isStartingHost"
|
||||
@click="openStartHostModal()"
|
||||
>
|
||||
{{ t('action:start') }}
|
||||
</MenuItem>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useXoHostStartJob } from '@/modules/host/jobs/xo-host-start.job.ts'
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import MenuItem from '@core/components/menu/MenuItem.vue'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive.ts'
|
||||
import { useModal } from '@core/packages/modal/use-modal.ts'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { host } = defineProps<{
|
||||
host: FrontXoHost
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const {
|
||||
run: startHost,
|
||||
canRun: canStartHost,
|
||||
isRunning: isStartingHost,
|
||||
errorMessage: startHostErrorMessage,
|
||||
} = useXoHostStartJob(() => host)
|
||||
|
||||
const openStartHostModal = useModal({
|
||||
component: import('@core/components/modal/VtsActionModal.vue'),
|
||||
props: {
|
||||
accent: 'warning',
|
||||
action: 'start',
|
||||
object: 'host',
|
||||
hostName: host.name_label,
|
||||
icon: 'status:warning-picto',
|
||||
},
|
||||
onConfirm: () => startHost(),
|
||||
})
|
||||
</script>
|
||||
@@ -80,7 +80,7 @@ const { host } = defineProps<{
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
const { getMasterHostByPoolId, isMasterHost, areHostsReady } = useXoHostCollection()
|
||||
const { getPowerState } = useXoHostUtils()
|
||||
const { getPowerState } = useXoHostUtils(() => host)
|
||||
|
||||
const powerState = computed(() => getPowerState(host.power_state))
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ const { host } = defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { getPowerState, getRelativeStartTime } = useXoHostUtils()
|
||||
const { getPowerState, getRelativeStartTime } = useXoHostUtils(() => host)
|
||||
|
||||
const { useGetPoolById } = useXoPoolCollection()
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import { getHostPendingStateOperation } from '@/modules/host/utils/xo-host.util.ts'
|
||||
import type { IconName } from '@core/icons'
|
||||
import useRelativeTime from '@core/composables/relative-time.composable'
|
||||
import { createMapper } from '@core/packages/mapper'
|
||||
import { createMapper, useMapper } from '@core/packages/mapper'
|
||||
import { parseDateTime } from '@core/utils/time.util'
|
||||
import { toComputed } from '@core/utils/to-computed.util.ts'
|
||||
import { HOST_POWER_STATE } from '@vates/types'
|
||||
import { computed } from 'vue'
|
||||
import { computed, type MaybeRefOrGetter } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
export function useXoHostUtils() {
|
||||
export function useXoHostUtils(rawHost: MaybeRefOrGetter<FrontXoHost>) {
|
||||
const { t } = useI18n()
|
||||
|
||||
const host = toComputed(rawHost)
|
||||
|
||||
const getPowerState = createMapper<HOST_POWER_STATE, { text: string; icon: IconName | undefined }>(
|
||||
{
|
||||
[HOST_POWER_STATE.RUNNING]: { text: t('host:status:running'), icon: 'status:running-circle' },
|
||||
@@ -24,8 +29,25 @@ export function useXoHostUtils() {
|
||||
return useRelativeTime(date)
|
||||
}
|
||||
|
||||
const pendingStateOperation = computed(() => getHostPendingStateOperation(host.value))
|
||||
|
||||
const isChangingState = computed(() => pendingStateOperation.value !== undefined)
|
||||
|
||||
const currentOperation = useMapper<string, string>(
|
||||
() => pendingStateOperation.value,
|
||||
{
|
||||
power_on: t('operation:start'),
|
||||
shutdown: t('operation:shutdown'),
|
||||
enable: t('operation:enable'),
|
||||
unknown: '',
|
||||
},
|
||||
'unknown'
|
||||
)
|
||||
|
||||
return {
|
||||
getPowerState,
|
||||
getRelativeStartTime,
|
||||
isChangingState,
|
||||
currentOperation,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { xoHostArg } from '@/modules/host/jobs/xo-host-args.jobs.ts'
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import { isHostOperationPending } from '@/modules/host/utils/xo-host.util.ts'
|
||||
import { useXoTaskUtils } from '@/shared/composables/xo-task-utils.composable.ts'
|
||||
import { fetchPost } from '@/shared/utils/fetch.util.ts'
|
||||
import { defineJob, JobError, JobRunningError } from '@core/packages/job'
|
||||
import { HOST_ALLOWED_OPERATIONS, HOST_POWER_STATE, type XoTask } from '@vates/types'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
export const useXoHostShutdownJob = defineJob('host.shutdown', [xoHostArg], () => {
|
||||
const { t } = useI18n()
|
||||
const { monitorTask } = useXoTaskUtils()
|
||||
|
||||
return {
|
||||
async run(host: FrontXoHost) {
|
||||
const { taskId } = await fetchPost<{ taskId: XoTask['id'] }>(`hosts/${host.id}/actions/clean_shutdown`)
|
||||
await monitorTask(taskId)
|
||||
},
|
||||
|
||||
validate: (isRunning, host: FrontXoHost | undefined) => {
|
||||
if (!host) {
|
||||
throw new JobError(t('job:host-shutdown:missing-host'))
|
||||
}
|
||||
|
||||
if (isRunning || isHostOperationPending(host, HOST_ALLOWED_OPERATIONS.SHUTDOWN)) {
|
||||
throw new JobRunningError(t('job:host-shutdown:in-progress'))
|
||||
}
|
||||
|
||||
if (host.power_state !== HOST_POWER_STATE.RUNNING) {
|
||||
throw new JobError(t('job:host-shutdown:bad-power-state'))
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
import { xoHostArg } from '@/modules/host/jobs/xo-host-args.jobs.ts'
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import { isHostOperationPending } from '@/modules/host/utils/xo-host.util.ts'
|
||||
import { useXoTaskUtils } from '@/shared/composables/xo-task-utils.composable.ts'
|
||||
import { fetchPost } from '@/shared/utils/fetch.util.ts'
|
||||
import { defineJob, JobError, JobRunningError } from '@core/packages/job'
|
||||
import { HOST_ALLOWED_OPERATIONS, HOST_POWER_STATE, type XoTask } from '@vates/types'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
export const useXoHostStartJob = defineJob('host.start', [xoHostArg], () => {
|
||||
const { t } = useI18n()
|
||||
const { monitorTask } = useXoTaskUtils()
|
||||
|
||||
return {
|
||||
async run(host: FrontXoHost) {
|
||||
const { taskId } = await fetchPost<{ taskId: XoTask['id'] }>(`hosts/${host.id}/actions/start`)
|
||||
await monitorTask(taskId)
|
||||
},
|
||||
|
||||
validate: (isRunning, host: FrontXoHost | undefined) => {
|
||||
if (!host) {
|
||||
throw new JobError(t('job:host-start:missing-host'))
|
||||
}
|
||||
|
||||
if (isRunning || isHostOperationPending(host, HOST_ALLOWED_OPERATIONS.POWER_ON)) {
|
||||
throw new JobRunningError(t('job:host-start:in-progress'))
|
||||
}
|
||||
|
||||
if (host.power_state === HOST_POWER_STATE.RUNNING) {
|
||||
throw new JobError(t('job:host-start:bad-power-state'))
|
||||
}
|
||||
|
||||
if (host.power_state !== HOST_POWER_STATE.HALTED) {
|
||||
throw new JobError(t('job:host-start:bad-power-state-not-halted'))
|
||||
}
|
||||
|
||||
if (host.powerOnMode === '') {
|
||||
throw new JobError(t('job:host-start:power-on-disabled'))
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,8 +1,15 @@
|
||||
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import type { VtsLinkCellProps } from '@core/components/table/cells/VtsLinkCell.vue'
|
||||
import type { HOST_ALLOWED_OPERATIONS } from '@vates/types'
|
||||
import { HOST_ALLOWED_OPERATIONS, HOST_POWER_STATE } from '@vates/types'
|
||||
import { castArray } from 'lodash-es'
|
||||
|
||||
const RUNNING_CHANGING_STATE_OPERATIONS: Partial<HOST_ALLOWED_OPERATIONS>[] = [
|
||||
HOST_ALLOWED_OPERATIONS.SHUTDOWN,
|
||||
HOST_ALLOWED_OPERATIONS.ENABLE,
|
||||
]
|
||||
|
||||
const NOT_RUNNING_CHANGING_STATE_OPERATIONS: Partial<HOST_ALLOWED_OPERATIONS>[] = [HOST_ALLOWED_OPERATIONS.POWER_ON]
|
||||
|
||||
export function isHostOperationPending(
|
||||
host: FrontXoHost,
|
||||
operations: HOST_ALLOWED_OPERATIONS[] | HOST_ALLOWED_OPERATIONS
|
||||
@@ -12,6 +19,24 @@ export function isHostOperationPending(
|
||||
return castArray(operations).some(operation => currentOperations.includes(operation))
|
||||
}
|
||||
|
||||
export function getHostPendingOperation(
|
||||
host: FrontXoHost,
|
||||
operations: HOST_ALLOWED_OPERATIONS[] | HOST_ALLOWED_OPERATIONS
|
||||
) {
|
||||
const currentOperations = Object.values(host.current_operations)
|
||||
|
||||
return castArray(operations).find(operation => currentOperations.includes(operation))
|
||||
}
|
||||
|
||||
export function getHostPendingStateOperation(host: FrontXoHost) {
|
||||
return getHostPendingOperation(
|
||||
host,
|
||||
host.power_state === HOST_POWER_STATE.RUNNING
|
||||
? RUNNING_CHANGING_STATE_OPERATIONS
|
||||
: NOT_RUNNING_CHANGING_STATE_OPERATIONS
|
||||
)
|
||||
}
|
||||
|
||||
export function getHostInfo(host: FrontXoHost | undefined): VtsLinkCellProps & { label: string } {
|
||||
return host ? { label: host.name_label, to: `/host/${host.id}/dashboard` } : { label: '' }
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@
|
||||
/>
|
||||
</template>
|
||||
<template #addons>
|
||||
<UiLoader
|
||||
v-if="isChangingState"
|
||||
v-tooltip="{
|
||||
placement: 'top',
|
||||
content: currentOperation,
|
||||
}"
|
||||
/>
|
||||
<VtsIcon v-if="isMaster" v-tooltip="t('master')" name="status:primary-circle" size="medium" />
|
||||
<UiCounter
|
||||
v-tooltip="t('running-vm', runningVmsCount)"
|
||||
@@ -29,7 +36,7 @@
|
||||
@click="open($event)"
|
||||
/>
|
||||
</template>
|
||||
<HostMoreActions :host="branch.data" />
|
||||
<HostMoreActions :host="branch.data" show-change-state-button />
|
||||
</MenuList>
|
||||
</template>
|
||||
</UiTreeItemLabel>
|
||||
@@ -38,6 +45,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import HostMoreActions from '@/modules/host/components/HostMoreActions.vue'
|
||||
import { useXoHostUtils } from '@/modules/host/composables/xo-host-utils.composable.ts'
|
||||
import { useXoHostCollection } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
|
||||
import type { HostBranch } from '@/modules/treeview/types/tree.type.ts'
|
||||
import { useXoVmCollection } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
|
||||
@@ -48,6 +56,7 @@ 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 UiCounter from '@core/components/ui/counter/UiCounter.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 { computed } from 'vue'
|
||||
@@ -62,6 +71,8 @@ const { t } = useI18n()
|
||||
const { isMasterHost } = useXoHostCollection()
|
||||
const { runningVmsCountByContainer } = useXoVmCollection()
|
||||
|
||||
const { isChangingState, currentOperation } = useXoHostUtils(() => branch.data)
|
||||
|
||||
const isMaster = computed(() => isMasterHost(branch.data.id))
|
||||
|
||||
const runningVmsCount = computed(() => runningVmsCountByContainer.value.get(branch.data.id) ?? 0)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
> Users must be able to say: "Nice enhancement, I'm eager to test it"
|
||||
|
||||
- [XO6/Host] Add possibility to shut down and start an host (PR [#10088](https://github.com/vatesfr/xen-orchestra/pull/10088))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
> Users must be able to say: "I had this issue, happy to know it's fixed"
|
||||
@@ -31,4 +33,6 @@
|
||||
|
||||
<!--packages-start-->
|
||||
|
||||
- @xen-orchestra/web minor
|
||||
|
||||
<!--packages-end-->
|
||||
|
||||
Reference in New Issue
Block a user