diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/CompatibilityMatrix.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/CompatibilityMatrix.vue similarity index 92% rename from pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/CompatibilityMatrix.vue rename to pkg/harvester/edit/kubevirt.io.virtualmachine/CompatibilityMatrix.vue index dc3eb209..fa98f8cb 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/CompatibilityMatrix.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/CompatibilityMatrix.vue @@ -3,11 +3,11 @@ import { mapGetters } from 'vuex'; export default { props: { /** - * deviceId/vendorId is unique per type of device - there may be multiple pciDevice CRD objects for a given device + * deviceId/vendorId is unique per type of device - there may be multiple CRD objects for a given device * { * [deviceId/vendorId]: { * nodes: array of devicecrd.status.nodeName's for given device, - * deviceCRDs: array of all instances (pciDevice CRD) of given device + * deviceCRDs: array of all instances of given device * } * } */ @@ -78,9 +78,9 @@ export default {
- {{ t('harvester.pci.matrixDeviceClaimName') }} + {{ t('harvester.devices.matrixDeviceClaimName') }}
-
{{ t('harvester.pci.matrixHostName') }}
+
{{ t('harvester.devices.matrixHostName') }}
{{ nodeName }} diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/index.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/index.vue index ebe963bf..d7f74e2e 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/index.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachinePciDevices/index.vue @@ -3,6 +3,8 @@ import { _EDIT } from '@shell/config/query-params'; import { allHash } from '@shell/utils/promise'; import LabeledSelect from '@shell/components/form/LabeledSelect'; import Banner from '@components/Banner/Banner.vue'; +import CompatibilityMatrix from '../CompatibilityMatrix'; +import DeviceList from './DeviceList'; import remove from 'lodash/remove'; import { get, set } from '@shell/utils/object'; diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineUSBDevices/index.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineUSBDevices/index.vue index 2c3c172c..75a45f6e 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineUSBDevices/index.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineUSBDevices/index.vue @@ -5,11 +5,13 @@ import { HCI } from '../../../types'; import { STATE, SIMPLE_NAME } from '@shell/config/table-headers'; import LabeledSelect from '@shell/components/form/LabeledSelect'; import Banner from '@components/Banner/Banner.vue'; +import CompatibilityMatrix from '../CompatibilityMatrix'; export default { name: 'VirtualMachineUSBDevices', components: { Banner, + CompatibilityMatrix, LabeledSelect, }, props: { @@ -52,14 +54,140 @@ export default { devices: [], vms: [], selectedDevices: [], + showMatrix: false, }; }, computed: { + deviceOpts() { + const filteredOptions = this.enabledDevices.filter((deviceCRD) => { + if (this.selectedDevices.length > 0) { + const selectedDevice = this.enabledDevices.find(device => device.metadata.name === this.selectedDevices[0]); + + return !this.devicesInUse[deviceCRD?.metadata.name] && deviceCRD.status.nodeName === selectedDevice.status.nodeName; + } + + return !this.devicesInUse[deviceCRD?.metadata.name]; + }); + + return filteredOptions.map((deviceCRD) => { + return { + value: deviceCRD?.metadata.name, + label: deviceCRD?.metadata.name, + displayLabel: deviceCRD?.status?.resourceName, + }; + }); + }, + + enabledDevices() { + return this.devices.filter((device) => { + return device.isEnabled; + }) || []; + }, + + devicesInUse() { + const inUse = this.vms.reduce((inUse, vm) => { + if (vm.metadata.name === this.vm?.metadata?.name) { + return inUse; + } + const devices = get(vm, 'spec.template.spec.domain.devices.hostDevices') || []; + + devices.forEach((device) => { + inUse[device.name] = { usedBy: [vm.metadata.name] }; + }); + + return inUse; + }, {}); + + return inUse; + }, + + devicesByNode() { + const out = {}; + + this.enabledDevices.forEach((deviceCRD) => { + const nodeName = deviceCRD.spec?.nodeName; + + if (!out[nodeName]) { + out[nodeName] = [deviceCRD]; + } else { + out[nodeName].push(deviceCRD); + } + }); + + return out; + }, + + compatibleNodes() { + const out = [...Object.keys(this.devicesByNode)]; + + this.selectedDevices.forEach((deviceUid) => { + remove(out, (nodeName) => { + const device = this.enabledDevices.find(deviceCRD => deviceCRD.metadata.name === deviceUid); + + return device.spec.nodeName !== nodeName; + }); + }); + + return out; + }, }, }; diff --git a/pkg/harvester/l10n/en-us.yaml b/pkg/harvester/l10n/en-us.yaml index 4a1a8156..d7581b69 100644 --- a/pkg/harvester/l10n/en-us.yaml +++ b/pkg/harvester/l10n/en-us.yaml @@ -303,8 +303,11 @@ harvester: suffix: to enable it to manage your PCI devices. noPCIPermission: Please contact your system administrator to enable the PCI devices first. enablePassthroughWarning: Please be careful not to use host-owned PCI devices (e.g., management and VLAN NICs). Incorrect device allocation may cause damage to your cluster, including node failure. + + devices: matrixHostName: Host Name matrixDeviceClaimName: Device Claim Name + generic: close: Close open: Open @@ -1371,6 +1374,16 @@ harvester: prefix: The usb addon is not enabled, click middle: here suffix: to enable it to manage your USB devices. + available: Available USB Devices + compatibleNodes: Compatible Nodes + impossibleSelection: 'There are no hosts with all of the selected devices.' + howToUseDevice: 'Use the table below to enable USB passthrough on each device you want to use in this VM.' + deviceInTheSameHost: 'You can only select devices on the same host.' + showCompatibility: Show device compatibility matrix + hideCompatibility: Hide device compatibility matrix + claimError: Error enabling passthrough on {name} + unclaimError: Error disabling passthrough on {name} + cantUnclaim: You cannot disable passthrough on a device claimed by another user. harvesterVlanConfigMigrateDialog: targetClusterNetwork: