From a3340a7a7e8dcd15ec01304007c08092e32f053e Mon Sep 17 00:00:00 2001 From: Thierry Goettelmann Date: Fri, 15 Nov 2024 09:14:28 +0100 Subject: [PATCH] feat(lite,core,web): control domain store, refactor context access (#8130) --- .../lite/src/libs/xen-api/xen-api.types.ts | 1 + .../stores/xen-api/control-domain.store.ts | 21 ++++++++++ .../lite/src/stores/xen-api/host.store.ts | 9 +++-- .../lite/src/stores/xen-api/vm-raw.store.ts | 12 ++++++ .../lite/src/stores/xen-api/vm.store.ts | 40 +++++++++---------- .../docs/stores/subscribable-stores.md | 9 +++-- .../lib/types/subscribable-store.type.ts | 4 +- .../create-subscribable-store-context.util.ts | 2 +- .../web-core/lib/utils/if-else.utils.ts | 4 +- .../web/src/stores/xo-rest-api/host.store.ts | 4 +- 10 files changed, 73 insertions(+), 33 deletions(-) create mode 100644 @xen-orchestra/lite/src/stores/xen-api/control-domain.store.ts create mode 100644 @xen-orchestra/lite/src/stores/xen-api/vm-raw.store.ts diff --git a/@xen-orchestra/lite/src/libs/xen-api/xen-api.types.ts b/@xen-orchestra/lite/src/libs/xen-api/xen-api.types.ts index b9d416c2a5f..02f48770b02 100644 --- a/@xen-orchestra/lite/src/libs/xen-api/xen-api.types.ts +++ b/@xen-orchestra/lite/src/libs/xen-api/xen-api.types.ts @@ -110,6 +110,7 @@ export interface XenApiHost extends XenApiRecord<'host'> { resident_VMs: XenApiVm['$ref'][] cpu_info: { cpu_count: string } software_version: { product_version: string } + control_domain: XenApiVm['$ref'] } export interface XenApiSr extends XenApiRecord<'sr'> { diff --git a/@xen-orchestra/lite/src/stores/xen-api/control-domain.store.ts b/@xen-orchestra/lite/src/stores/xen-api/control-domain.store.ts new file mode 100644 index 00000000000..182f857df0b --- /dev/null +++ b/@xen-orchestra/lite/src/stores/xen-api/control-domain.store.ts @@ -0,0 +1,21 @@ +import { useVmRawStore } from '@/stores/xen-api/vm-raw.store' +import { createSubscribableStoreContext } from '@core/utils/create-subscribable-store-context.util' +import { defineStore } from 'pinia' +import { computed } from 'vue' + +export const useControlDomainStore = defineStore('xen-api-control-domain', () => { + const deps = { + vmRawStore: useVmRawStore(), + } + + const vmRawContext = deps.vmRawStore.getContext() + + const records = computed(() => vmRawContext.records.value.filter(vm => vm.is_control_domain)) + + const context = { + ...vmRawContext, + records, + } + + return createSubscribableStoreContext({ context }, deps) +}) diff --git a/@xen-orchestra/lite/src/stores/xen-api/host.store.ts b/@xen-orchestra/lite/src/stores/xen-api/host.store.ts index c8e8c34bffe..ed9782939a6 100644 --- a/@xen-orchestra/lite/src/stores/xen-api/host.store.ts +++ b/@xen-orchestra/lite/src/stores/xen-api/host.store.ts @@ -8,14 +8,15 @@ import { defineStore } from 'pinia' import { computed } from 'vue' export const useHostStore = defineStore('xen-api-host', () => { - const deps = { metrics: useHostMetricsStore() } + const deps = { metricsStore: useHostMetricsStore() } + + const metricsContext = deps.metricsStore.getContext() + const xenApiStore = useXenApiStore() const { context: baseContext, ...configRest } = createXapiStoreConfig('host') - const runningHosts = computed(() => - baseContext.records.value.filter(host => deps.metrics.$context.isHostRunning(host)) - ) + const runningHosts = computed(() => baseContext.records.value.filter(host => metricsContext.isHostRunning(host))) const getStats = ((hostUuid, granularity, ignoreExpired = false, { abortSignal }) => { const host = baseContext.getByUuid(hostUuid) diff --git a/@xen-orchestra/lite/src/stores/xen-api/vm-raw.store.ts b/@xen-orchestra/lite/src/stores/xen-api/vm-raw.store.ts new file mode 100644 index 00000000000..2fd74840f0f --- /dev/null +++ b/@xen-orchestra/lite/src/stores/xen-api/vm-raw.store.ts @@ -0,0 +1,12 @@ +import { createXapiStoreConfig } from '@/stores/xen-api/create-xapi-store-config' +import { createSubscribableStoreContext } from '@core/utils/create-subscribable-store-context.util' +import { sortByNameLabel } from '@core/utils/sort-by-name-label.util' +import { defineStore } from 'pinia' + +export const useVmRawStore = defineStore('xen-api-vm-raw', () => { + const config = createXapiStoreConfig('vm', { + sortBy: (vm1, vm2) => sortByNameLabel(vm1, vm2), + }) + + return createSubscribableStoreContext(config, {}) +}) diff --git a/@xen-orchestra/lite/src/stores/xen-api/vm.store.ts b/@xen-orchestra/lite/src/stores/xen-api/vm.store.ts index c6d9afb95a5..843cd4ab005 100644 --- a/@xen-orchestra/lite/src/stores/xen-api/vm.store.ts +++ b/@xen-orchestra/lite/src/stores/xen-api/vm.store.ts @@ -2,34 +2,35 @@ import type { GetStats } from '@/composables/fetch-stats.composable' import type { VmStats } from '@/libs/xapi-stats' import { VM_POWER_STATE } from '@/libs/xen-api/xen-api.enums' import type { XenApiHost, XenApiVm } from '@/libs/xen-api/xen-api.types' -import { createXapiStoreConfig } from '@/stores/xen-api/create-xapi-store-config' import { useHostStore } from '@/stores/xen-api/host.store' +import { useVmRawStore } from '@/stores/xen-api/vm-raw.store' import { useXenApiStore } from '@/stores/xen-api.store' import { createSubscribableStoreContext } from '@core/utils/create-subscribable-store-context.util' -import { sortByNameLabel } from '@core/utils/sort-by-name-label.util' import { defineStore } from 'pinia' import { computed } from 'vue' export const useVmStore = defineStore('xen-api-vm', () => { - const deps = { host: useHostStore() } + const deps = { + hostStore: useHostStore(), + vmRawStore: useVmRawStore(), + } - const { context: baseContext, ...configRest } = createXapiStoreConfig('vm', { - beforeAdd(vm) { - if (vm.is_a_snapshot || vm.is_control_domain || vm.is_a_template) { - return undefined - } + const xenApiStore = useXenApiStore() - return vm - }, - sortBy: (vm1, vm2) => sortByNameLabel(vm1, vm2), - }) + const hostContext = deps.hostStore.getContext() - const runningVms = computed(() => baseContext.records.value.filter(vm => vm.power_state === VM_POWER_STATE.RUNNING)) + const vmRawContext = deps.vmRawStore.getContext() + + const records = computed(() => + vmRawContext.records.value.filter(vm => !vm.is_a_snapshot && !vm.is_control_domain && !vm.is_a_template) + ) + + const runningVms = computed(() => records.value.filter(vm => vm.power_state === VM_POWER_STATE.RUNNING)) const recordsByHostRef = computed(() => { const vmsByHostOpaqueRef = new Map() - baseContext.records.value.forEach(vm => { + records.value.forEach(vm => { if (!vmsByHostOpaqueRef.has(vm.resident_on)) { vmsByHostOpaqueRef.set(vm.resident_on, []) } @@ -41,19 +42,17 @@ export const useVmStore = defineStore('xen-api-vm', () => { }) const getStats = ((id, granularity, ignoreExpired = false, { abortSignal }) => { - const xenApiStore = useXenApiStore() - if (!xenApiStore.isConnected) { return undefined } - const vm = baseContext.getByUuid(id) + const vm = vmRawContext.getByUuid(id) if (vm === undefined) { throw new Error(`VM ${id} could not be found.`) } - const host = deps.host.$context.getByOpaqueRef(vm.resident_on) + const host = hostContext.getByOpaqueRef(vm.resident_on) if (host === undefined) { throw new Error(`VM ${id} is halted or host could not be found.`) @@ -69,11 +68,12 @@ export const useVmStore = defineStore('xen-api-vm', () => { }) as GetStats const context = { - ...baseContext, + ...vmRawContext, + records, runningVms, recordsByHostRef, getStats, } - return createSubscribableStoreContext({ context, ...configRest }, deps) + return createSubscribableStoreContext({ context }, deps) }) diff --git a/@xen-orchestra/web-core/docs/stores/subscribable-stores.md b/@xen-orchestra/web-core/docs/stores/subscribable-stores.md index 44bd7b1ceb2..df9a6ffb0ee 100644 --- a/@xen-orchestra/web-core/docs/stores/subscribable-stores.md +++ b/@xen-orchestra/web-core/docs/stores/subscribable-stores.md @@ -17,7 +17,7 @@ The configuration object must have the following properties: The function will return an object with the following properties: - `subscribe(options?: { defer: boolean })`: a function which will register a subscription then return the `context` -- `$context`: a way to access the `context` object without subscribing (helpful for dependencies) +- `getContext()`: a way to access the `context` object without subscribing (helpful for dependencies) ## Basic store @@ -71,8 +71,11 @@ export const useGreetingStore = defineStore('greeting', () => { groupStore: useGroupStore(), } - const userGreeting = computed(() => `Hello ${deps.userStore.$context.user.value.name}`) - const groupGreeting = computed(() => `Hello ${deps.groupStore.$context.group.value.name}`) + const userContext = deps.userStore.getContext() + const groupContext = deps.groupStore.getContext() + + const userGreeting = computed(() => `Hello ${userContext.user.value.name}`) + const groupGreeting = computed(() => `Hello ${groupContext.group.value.name}`) const context = { userGreeting, diff --git a/@xen-orchestra/web-core/lib/types/subscribable-store.type.ts b/@xen-orchestra/web-core/lib/types/subscribable-store.type.ts index 927ee324385..41c0bf8363a 100644 --- a/@xen-orchestra/web-core/lib/types/subscribable-store.type.ts +++ b/@xen-orchestra/web-core/lib/types/subscribable-store.type.ts @@ -15,7 +15,7 @@ export type Subscribe = (options?: { export type SubscribableStoreConfig = { context: TContext - onSubscribe: () => void - onUnsubscribe: () => void + onSubscribe?: () => void + onUnsubscribe?: () => void isEnabled?: MaybeRefOrGetter } diff --git a/@xen-orchestra/web-core/lib/utils/create-subscribable-store-context.util.ts b/@xen-orchestra/web-core/lib/utils/create-subscribable-store-context.util.ts index dc3fda560a8..6440e658eda 100644 --- a/@xen-orchestra/web-core/lib/utils/create-subscribable-store-context.util.ts +++ b/@xen-orchestra/web-core/lib/utils/create-subscribable-store-context.util.ts @@ -60,7 +60,7 @@ export function createSubscribableStoreContext( } return { - $context: config.context, + getContext: () => config.context, subscribe, } } diff --git a/@xen-orchestra/web-core/lib/utils/if-else.utils.ts b/@xen-orchestra/web-core/lib/utils/if-else.utils.ts index e6232100feb..bbfb77f5bf2 100644 --- a/@xen-orchestra/web-core/lib/utils/if-else.utils.ts +++ b/@xen-orchestra/web-core/lib/utils/if-else.utils.ts @@ -6,8 +6,8 @@ export interface IfElseOptions extends Pick {} export function ifElse( source: WatchSource, - onTrue: MaybeArray, - onFalse: MaybeArray, + onTrue: MaybeArray | undefined, + onFalse: MaybeArray | undefined, options?: IfElseOptions ) { const onTrueFunctions = toArray(onTrue) diff --git a/@xen-orchestra/web/src/stores/xo-rest-api/host.store.ts b/@xen-orchestra/web/src/stores/xo-rest-api/host.store.ts index c9331fd780a..05e0f820361 100644 --- a/@xen-orchestra/web/src/stores/xo-rest-api/host.store.ts +++ b/@xen-orchestra/web/src/stores/xo-rest-api/host.store.ts @@ -12,11 +12,13 @@ export const useHostStore = defineStore('host', () => { poolStore: usePoolStore(), } + const poolContext = deps.poolStore.getContext() + const { context: baseContext, ...configRest } = createXoStoreConfig('host', { sortBy: sortByNameLabel, }) - const isMasterHost = (hostId: XoHost['id']) => !!deps.poolStore.$context.records.find(pool => pool.master === hostId) + const isMasterHost = (hostId: XoHost['id']) => !!poolContext.records.value.find(pool => pool.master === hostId) const hostsByPool = computed(() => { const hostsByPoolMap = new Map()