feat(traffic rule type by VIF): display only the traffic rules for VMs and disable VMs without a VIF. (#9977)

- Show only traffic rules of VMs. Don't include `vm-snapshot` or `vm-template`
- For VM selection, display all VMs and disable those without a VIF, with a
message displayed
This commit is contained in:
Elise-F
2026-07-22 11:01:17 +02:00
committed by GitHub
parent ebce0e508f
commit 0a5693345f
6 changed files with 41 additions and 12 deletions

View File

@@ -31,6 +31,9 @@
<span class="option-content">
<VtsIcon v-if="option.properties.icon" :name="option.properties.icon" size="medium" />
{{ option.properties.label }}
<span v-if="option.properties.disabled" class="no-vif typo-body-regular-small">{{
t('no-vif-detected')
}}</span>
</span>
</VtsOption>
</template>
@@ -109,6 +112,13 @@ async function onSubmit() {
display: inline-flex;
align-items: center;
gap: 0.8rem;
.no-vif {
&::before {
content: '\2014';
margin-inline-end: 0.8rem;
}
}
}
.new-traffic-rule-form {

View File

@@ -27,17 +27,19 @@ export function useTrafficRules(
(networkA.name_label ?? '').localeCompare(networkB.name_label ?? '')
)
const sortedVifs = [...vifs.value].sort((vifA, vifB) => {
const vmNameLabelA = getVmById(vifA.$VM)?.name_label ?? ''
const vmNameLabelB = getVmById(vifB.$VM)?.name_label ?? ''
const vmComparison = vmNameLabelA.localeCompare(vmNameLabelB)
const sortedVifs = [...vifs.value]
.filter(vif => getVmById(vif.$VM))
.sort((vifA, vifB) => {
const vmNameLabelA = getVmById(vifA.$VM)?.name_label ?? ''
const vmNameLabelB = getVmById(vifB.$VM)?.name_label ?? ''
const vmComparison = vmNameLabelA.localeCompare(vmNameLabelB)
if (vmComparison !== 0) {
return vmComparison
}
if (vmComparison !== 0) {
return vmComparison
}
return Number(vifA.device) - Number(vifB.device)
})
return Number(vifA.device) - Number(vifB.device)
})
return [
...sortedNetworks.flatMap(network =>

View File

@@ -99,11 +99,14 @@ export function useNewTrafficRuleForm(
const vmOptions = computed(() =>
poolVms.value.map(vm => {
const hasVifs = vm.VIFs.length > 0
return {
id: vm.id,
label: vm.name_label,
value: vm.id,
icon: objectIcon('vm', toLower(vm.power_state)),
disabled: !hasVifs,
}
})
)
@@ -149,7 +152,12 @@ export function useNewTrafficRuleForm(
required: () => isVifTarget.value,
searchable: true,
disabled: () => sourceVif.value !== undefined,
option: { label: 'label', value: 'value', properties: source => ({ icon: source.icon }) },
option: {
label: 'label',
value: 'value',
disabled: source => source.disabled,
properties: source => ({ icon: source.icon }),
},
})
const { id: targetSelectId } = useFormSelect('targetId', targetOptions, {

View File

@@ -21,6 +21,7 @@ import TrafficRulesSidePanel from '@/modules/traffic-rules/components/list/panel
import TrafficRulesTable from '@/modules/traffic-rules/components/TrafficRulesTable.vue'
import { useTrafficRules } from '@/modules/traffic-rules/composables/traffic-rules.composable'
import { useXoVifCollection } from '@/modules/vif/remote-resources/use-xo-vif-collection.ts'
import { useXoVmCollection } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
import VtsContentSidePanel from '@core/components/layout/VtsContentSidePanel.vue'
import VtsStateHero from '@core/components/state-hero/VtsStateHero.vue'
import UiCard from '@core/components/ui/card/UiCard.vue'
@@ -41,7 +42,9 @@ const { vifs, areVifsReady } = useXoVifCollection()
const { networks, areNetworksReady } = useXoNetworkCollection()
const isReady = logicAnd(areVifsReady, areNetworksReady)
const { areVmsReady } = useXoVmCollection()
const isReady = logicAnd(areVifsReady, areNetworksReady, areVmsReady)
const poolVifs = computed(() => vifs.value.filter(vif => vif.$pool === pool.id))

View File

@@ -1,7 +1,8 @@
<template>
<VtsContentSidePanel class="traffic-rules">
<UiCard class="container">
<TrafficRulesTable :rules="trafficRules" :pool>
<VtsStateHero v-if="!areVmsReady" format="page" type="busy" size="medium" />
<TrafficRulesTable v-else :rules="trafficRules" :pool>
<template #title-action>
<UiLink :to="{ name: '/traffic-rule/new', query: { vifid: vif.id } }" icon="fa:plus" size="medium">
{{ t('new') }}
@@ -19,7 +20,9 @@ import TrafficRulesSidePanel from '@/modules/traffic-rules/components/list/panel
import TrafficRulesTable from '@/modules/traffic-rules/components/TrafficRulesTable.vue'
import { useTrafficRules } from '@/modules/traffic-rules/composables/traffic-rules.composable.ts'
import { type FrontXoVif } from '@/modules/vif/remote-resources/use-xo-vif-collection.ts'
import { useXoVmCollection } from '@/modules/vm/remote-resources/use-xo-vm-collection.ts'
import VtsContentSidePanel from '@core/components/layout/VtsContentSidePanel.vue'
import VtsStateHero from '@core/components/state-hero/VtsStateHero.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.ts'
@@ -33,6 +36,8 @@ const { t } = useI18n()
const { useGetPoolById } = useXoPoolCollection()
const pool = useGetPoolById(() => vif.$pool)
const { areVmsReady } = useXoVmCollection()
const { trafficRules } = useTrafficRules(() => [vif], [])
const selectedRule = useRouteQuery<TrafficRule | undefined>('id', {

View File

@@ -35,6 +35,7 @@
- [RPU] Re-enable the load balancer after a configurable safe delay (30 minutes by default) when a rolling pool update ends (PR [#10111](https://github.com/vatesfr/xen-orchestra/pull/10111))
- [Tasks] Resolve objects in tasks names [Forum#100894]([https://xcp-ng.org/forum/post/100894](https://xcp-ng.org/forum/post/100894)) (PR [#9830](https://github.com/vatesfr/xen-orchestra/pull/9830))
- [REST API] Possibility to set the HA restart priority (`high_availability`) when creating a VM (PR [#10070](https://github.com/vatesfr/xen-orchestra/pull/10070))
- [XO6/Traffic rules] Show only traffic rules of VMs. Don't include vm-snapshots or vm-templates. In the VM selector, disable VMs that don't have any VIF (PR [#9977](https://github.com/vatesfr/xen-orchestra/pull/9977))
### Bug fixes