diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/DeviceList.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/DeviceList.vue index 8139f281..e3ffc34e 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/DeviceList.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/DeviceList.vue @@ -246,4 +246,27 @@ export default { .group-actions { display: inline; } + +// Match the enable/disable passthrough bulk-action buttons height to the .btn (40px) on the right. +:deep(.bulk .rc-button.btn-medium.bulk-action:not(.btn-sm)) { + min-height: 40px; +} + +// Make the collapsed "Actions" dropdown button 40px too. +:deep(.bulk .rc-button.btn-medium.bulk-actions-dropdown:not(.btn-sm)) { + min-height: 40px; +} + +// Lay the bulk row out with flex so the "N selected" label can wrap below the buttons. +:deep(.bulk) { + display: flex; + flex-wrap: wrap; + align-items: center; +} + +:deep(.bulk .action-availability) { + flex-basis: 100%; + margin-left: 0; + margin-top: 6px; +} diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/index.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/index.vue index e48acfd2..7454ccf3 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/index.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/index.vue @@ -5,6 +5,7 @@ import LabeledSelect from '@shell/components/form/LabeledSelect'; import Banner from '@components/Banner/Banner.vue'; import remove from 'lodash/remove'; import { set } from '@shell/utils/object'; +import { uniq } from '@shell/utils/array'; import { HCI } from '../../../types'; import DeviceList from './DeviceList'; import CompatibilityMatrix from '../CompatibilityMatrix'; @@ -60,7 +61,15 @@ export default { row.allowDisable = !vmDeviceNames.includes(row.metadata.name); }); - vmDevices.forEach(({ name }) => { + // When off, read from spec; otherwise the spec name is a placeholder ('provisioned'), + // so read the real allocated device names from the deviceAllocationDetails annotation. + const hostDeviceNames = this.vm.isOff ? [ + ...vmDevices.map(({ name }) => name), + ] : [ + ...Object.values(this.vm?.provisionedHostDevices || {}).reduce((acc, devices) => [...acc, ...devices], []), + ]; + + uniq(hostDeviceNames).forEach((name) => { if (this.enabledDevices.find((device) => device?.metadata?.name === name)) { selectedDevices.push(name); } diff --git a/pkg/harvester/models/kubevirt.io.virtualmachine.js b/pkg/harvester/models/kubevirt.io.virtualmachine.js index cb67d214..c5bc34fb 100644 --- a/pkg/harvester/models/kubevirt.io.virtualmachine.js +++ b/pkg/harvester/models/kubevirt.io.virtualmachine.js @@ -1315,6 +1315,18 @@ export default class VirtVm extends HarvesterResource { } } + get provisionedHostDevices() { + // Rancher-provisioned VMs store hostDevices.name as a placeholder ('provisioned'); the + // real allocated PCI device names live in this annotation, keyed by resourceName. + try { + const deviceAllocationDetails = JSON.parse(this.metadata?.annotations[HCI_ANNOTATIONS.VM_DEVICE_ALLOCATION_DETAILS] || '{}'); + + return deviceAllocationDetails?.hostdevices || {}; + } catch (error) { + return {}; + } + } + get schedulingVMBackupFeatureEnabled() { return this.$rootGetters['harvester-common/getFeatureEnabled']('schedulingVMBackup'); }