feat(xo6): add host scan PIFs button and job (#10191)

* changelog

* feat(xo6): add host scan PIFs button and job

* add changelog

* missing translation

* review

* review

---------

Co-authored-by: MathieuRA <contact@mathieu-raisin.fr>
This commit is contained in:
Joris K
2026-07-31 16:10:46 +02:00
committed by GitHub
parent be868a307b
commit 03d2af39ad
19 changed files with 92 additions and 31 deletions

View File

@@ -913,7 +913,6 @@
"runs": "Běhy",
"s3-backup-repository": "S3 repozitář záloh",
"scan-pifs": "Skenovat fyzická rozhraní",
"scan-pifs-in-xo-5": "@:scan-pifs v XO-5",
"schedule": "Plán",
"schedules": "Plány",
"secure-boot": "Secure boot",

View File

@@ -791,7 +791,6 @@
"runs": "Läufe",
"s3-backup-repository": "S3-Backupdepot",
"scan-pifs": "PIFs scannen",
"scan-pifs-in-xo-5": "@:scan-pifs in XO-5",
"schedule": "Zeitplan",
"schedules": "Zeitpläne",
"secure-boot": "Secure Boot",

View File

@@ -493,6 +493,9 @@
"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-scan-pifs:bad-power-state": "Host is not running",
"job:host-scan-pifs:in-progress": "@:scan-pifs in progress…",
"job:host-scan-pifs: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",
@@ -658,8 +661,8 @@
"modal:confirm-vm-shutdown": "Confirm VM shutdown",
"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:pool-disconnect-message": "The pool will no longer be manageable from XO. You can reconnect it later.",
"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.",
@@ -960,7 +963,6 @@
"runs": "Runs",
"s3-backup-repository": "S3 backup repository",
"scan-pifs": "Scan PIFs",
"scan-pifs-in-xo-5": "@:scan-pifs in XO-5",
"schedule": "Schedule",
"schedules": "Schedules",
"secure-boot": "Secure boot",

View File

@@ -763,7 +763,6 @@
"runs": "Ejecuciones",
"s3-backup-repository": "Repositorio S3 de salvaguardas",
"scan-pifs": "Escanear PIFs",
"scan-pifs-in-xo-5": "@:scan-pifs en XO-5",
"schedule": "Planificación",
"schedules": "Planificaciones",
"secure-boot": "Arranque seguro",

View File

@@ -493,6 +493,9 @@
"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-scan-pifs:bad-power-state": "L'hôte n'est pas lancé",
"job:host-scan-pifs:in-progress": "@:scan-pifs en cours…",
"job:host-scan-pifs: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",
@@ -960,7 +963,6 @@
"runs": "Runs",
"s3-backup-repository": "Dépôt de sauvegarde S3",
"scan-pifs": "Scan PIFs",
"scan-pifs-in-xo-5": "@:scan-pifs dans XO-5",
"schedule": "Planification",
"schedules": "Planifications",
"secure-boot": "Démarrage sécurisé",

View File

@@ -508,7 +508,6 @@
"runs": "Kjøringer",
"s3-backup-repository": "S3-sikkerhetskopilager",
"scan-pifs": "Skann PIF-er",
"scan-pifs-in-xo-5": "@:scan-pifs i XO-5",
"schedule": "Tidsplan",
"schedules": "Tidsplaner",
"secure-boot": "Sikker oppstart",

View File

@@ -893,7 +893,6 @@
"runs": "Uitvoeringen",
"s3-backup-repository": "S3-back-up opslag repository",
"scan-pifs": "Scan PIF's",
"scan-pifs-in-xo-5": "@:scan-pifs in XO-5",
"schedule": "Planning",
"schedules": "Schema's",
"secure-boot": "Beveiligd opstarten",

View File

@@ -711,7 +711,6 @@
"runs": "Ações",
"s3-backup-repository": "Repositório de cópias de segurança do S3",
"scan-pifs": "Digitalizar PIFs",
"scan-pifs-in-xo-5": "@:scan-pifs em XO-5",
"schedule": "Calendário",
"schedules": "Calendários",
"secure-boot": "Arranque seguro",

View File

@@ -913,7 +913,6 @@
"runs": "Behy",
"s3-backup-repository": "S3 repozitár záloh",
"scan-pifs": "Skenovať PIFs",
"scan-pifs-in-xo-5": "@:scan-pifs v XO-5",
"schedule": "Plán",
"schedules": "Plány",
"secure-boot": "Secure boot",

View File

@@ -788,7 +788,6 @@
"runs": "Körningar",
"s3-backup-repository": "S3 säkerhetskopieringsförvar",
"scan-pifs": "Skanna PIF",
"scan-pifs-in-xo-5": "@:scan-pifs i XO-5",
"schedule": "Schema",
"schedules": "Schema",
"secure-boot": "Säker boot",

View File

@@ -848,7 +848,6 @@
"runs": "执行记录",
"s3-backup-repository": "S3对象存储备份仓库",
"scan-pifs": "扫描物理网卡",
"scan-pifs-in-xo-5": "前往XO5扫描物理网卡",
"schedule": "定时计划",
"schedules": "定时计划列表",
"secure-boot": "安全启动",

View File

@@ -0,0 +1,35 @@
<template>
<UiButton
v-tooltip="!canScanPifs && scanPifsErrorMessage"
size="medium"
variant="secondary"
accent="brand"
:disabled="!canScanPifs"
left-icon="action:scan"
:busy="isScanningPifs"
@click="scanPifs()"
>
{{ t('scan-pifs') }}
</UiButton>
</template>
<script lang="ts" setup>
import { useXoHostScanPifsJob } from '@/modules/host/jobs/xo-host-scan-pifs.job.ts'
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
import UiButton from '@core/components/ui/button/UiButton.vue'
import { vTooltip } from '@core/directives/tooltip.directive.ts'
import { useI18n } from 'vue-i18n'
const { host } = defineProps<{
host: FrontXoHost
}>()
const { t } = useI18n()
const {
run: scanPifs,
canRun: canScanPifs,
isRunning: isScanningPifs,
errorMessage: scanPifsErrorMessage,
} = useXoHostScanPifsJob(() => host)
</script>

View File

@@ -1,9 +1,10 @@
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 type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.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_POWER_STATE, type XoTask } from '@vates/types'
import { HOST_POWER_STATE } from '@vates/types'
import { useI18n } from 'vue-i18n'
export const useXoHostDisableJob = defineJob('host.disable', [xoHostArg], () => {
@@ -12,7 +13,7 @@ export const useXoHostDisableJob = defineJob('host.disable', [xoHostArg], () =>
return {
async run(host: FrontXoHost) {
const { taskId } = await fetchPost<{ taskId: XoTask['id'] }>(`hosts/${host.id}/actions/disable`)
const { taskId } = await fetchPost<{ taskId: FrontXoTask['id'] }>(`hosts/${host.id}/actions/disable`)
await monitorTask(taskId)
},

View File

@@ -1,10 +1,11 @@
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 type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.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, type XoTask } from '@vates/types'
import { HOST_ALLOWED_OPERATIONS } from '@vates/types'
import { useI18n } from 'vue-i18n'
export const useXoHostEnableJob = defineJob('host.enable', [xoHostArg], () => {
@@ -13,7 +14,7 @@ export const useXoHostEnableJob = defineJob('host.enable', [xoHostArg], () => {
return {
async run(host: FrontXoHost) {
const { taskId } = await fetchPost<{ taskId: XoTask['id'] }>(`hosts/${host.id}/actions/enable`)
const { taskId } = await fetchPost<{ taskId: FrontXoTask['id'] }>(`hosts/${host.id}/actions/enable`)
await monitorTask(taskId)
},

View File

@@ -0,0 +1,35 @@
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 type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.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_POWER_STATE } from '@vates/types'
import { useI18n } from 'vue-i18n'
export const useXoHostScanPifsJob = defineJob('host.scan-pifs', [xoHostArg], () => {
const { t } = useI18n()
const { monitorTask } = useXoTaskUtils()
return {
async run(host: FrontXoHost) {
const { taskId } = await fetchPost<{ taskId: FrontXoTask['id'] }>(`hosts/${host.id}/actions/scan_pifs`)
await monitorTask(taskId)
},
validate: (isRunning, host: FrontXoHost | undefined) => {
if (!host) {
throw new JobError(t('job:host-scan-pifs:missing-host'))
}
if (isRunning) {
throw new JobRunningError(t('job:host-scan-pifs:in-progress'))
}
if (host.power_state !== HOST_POWER_STATE.RUNNING) {
throw new JobError(t('job:host-scan-pifs:bad-power-state'))
}
},
}
})

View File

@@ -1,10 +1,11 @@
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 type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.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 { HOST_ALLOWED_OPERATIONS, HOST_POWER_STATE } from '@vates/types'
import { useI18n } from 'vue-i18n'
export const useXoHostShutdownJob = defineJob('host.shutdown', [xoHostArg], () => {
@@ -13,7 +14,7 @@ export const useXoHostShutdownJob = defineJob('host.shutdown', [xoHostArg], () =
return {
async run(host: FrontXoHost) {
const { taskId } = await fetchPost<{ taskId: XoTask['id'] }>(`hosts/${host.id}/actions/clean_shutdown`)
const { taskId } = await fetchPost<{ taskId: FrontXoTask['id'] }>(`hosts/${host.id}/actions/clean_shutdown`)
await monitorTask(taskId)
},

View File

@@ -1,10 +1,11 @@
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 type { FrontXoTask } from '@/modules/task/remote-resources/use-xo-task-collection.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 { HOST_ALLOWED_OPERATIONS, HOST_POWER_STATE } from '@vates/types'
import { useI18n } from 'vue-i18n'
export const useXoHostStartJob = defineJob('host.start', [xoHostArg], () => {
@@ -13,7 +14,7 @@ export const useXoHostStartJob = defineJob('host.start', [xoHostArg], () => {
return {
async run(host: FrontXoHost) {
const { taskId } = await fetchPost<{ taskId: XoTask['id'] }>(`hosts/${host.id}/actions/start`)
const { taskId } = await fetchPost<{ taskId: FrontXoTask['id'] }>(`hosts/${host.id}/actions/start`)
await monitorTask(taskId)
},

View File

@@ -3,9 +3,7 @@
<UiCard class="container">
<PifsTable :pifs>
<template #title-actions>
<UiLink :href="xo5ScanPifsHref" icon="fa:plus" size="medium">
{{ t('scan-pifs-in-xo-5') }}
</UiLink>
<HostScanPifsButton :host />
</template>
</PifsTable>
</UiCard>
@@ -14,29 +12,22 @@
</template>
<script setup lang="ts">
import HostScanPifsButton from '@/modules/host/components/actions/scan-pifs/HostScanPifsButton.vue'
import type { FrontXoHost } from '@/modules/host/remote-resources/use-xo-host-collection.ts'
import PifSidePanel from '@/modules/pif/components/list/panel/PifSidePanel.vue'
import PifsTable from '@/modules/pif/components/list/PifsTable.vue'
import { useXoPifCollection, type FrontXoPif } from '@/modules/pif/remote-resources/use-xo-pif-collection.ts'
import { useXoRoutes } from '@/shared/remote-resources/use-xo-routes.ts'
import VtsContentSidePanel from '@core/components/layout/VtsContentSidePanel.vue'
import UiCard from '@core/components/ui/card/UiCard.vue'
import UiLink from '@core/components/ui/link/UiLink.vue'
import { useRouteQuery } from '@core/composables/route-query.composable'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { host } = defineProps<{
host: FrontXoHost
}>()
const { buildXo5Route } = useXoRoutes()
const xo5ScanPifsHref = computed(() => buildXo5Route(`/hosts/${host.id}/network`))
const { pifsByHost } = useXoPifCollection()
const { t } = useI18n()
const pifs = computed(() => pifsByHost.value.get(host.id) ?? [])
const selectedPif = useRouteQuery<FrontXoPif | undefined>('id', {

View File

@@ -13,6 +13,7 @@
- [XO6/Host] Add possibility to shut down and start an host (PR [#10088](https://github.com/vatesfr/xen-orchestra/pull/10088))
- [REST API] Add `hosts/:id/actions/scan_pifs` endpoint (PR [#10187](https://github.com/vatesfr/xen-orchestra/pull/10187))
- [XO6/Host] Add possibility to scan PIFs directly from the host (PR [#10191](https://github.com/vatesfr/xen-orchestra/pull/10191))
### Bug fixes