Add USB devices page skeleton

Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
This commit is contained in:
Francesco Torchia 2024-06-27 14:28:17 +02:00
parent e8fbeccc72
commit ee963b7c1c
No known key found for this signature in database
GPG Key ID: E6D011B7415D4393
9 changed files with 281 additions and 8 deletions

View File

@ -419,6 +419,7 @@ export function init($plugin, store) {
HCI.PCI_DEVICE,
HCI.SR_IOVGPU_DEVICE,
HCI.VGPU_DEVICE,
HCI.USB_DEVICE,
HCI.ADD_ONS,
HCI.SECRET,
HCI.SETTING
@ -752,6 +753,32 @@ export function init($plugin, store) {
]
});
virtualType({
labelKey: 'harvester.usb.label',
group: 'advanced',
weight: 11,
name: HCI.USB_DEVICE,
namespaced: false,
route: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.USB_DEVICE }
},
exact: false,
});
configureType(HCI.USB_DEVICE, {
isCreatable: false,
hiddenNamespaceGroupButton: true,
listGroups: [
{
value: 'node',
field: 'groupByNode',
hideColumn: 'node',
tooltipKey: 'resourceTable.groupBy.node'
}
]
});
configureType(HCI.ADD_ONS, {
isCreatable: false,
isRemovable: false,

View File

@ -68,4 +68,5 @@ export const ADD_ONS = {
RANCHER_LOGGING: 'rancher-logging',
RANCHER_MONITORING: 'rancher-monitoring',
VM_IMPORT_CONTROLLER: 'vm-import-controller',
USB_DEVICE_CONTROLLER: 'usbdevices-controller',
};

View File

@ -0,0 +1,58 @@
<script>
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { RadioGroup } from '@components/Form/Radio';
import CreateEditView from '@shell/mixins/create-edit-view';
export default {
name: 'EditAddonUSB',
components: {
Tabbed,
Tab,
RadioGroup,
},
mixins: [CreateEditView],
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
},
};
</script>
<template>
<Tabbed :side-tabs="true">
<Tab
name="basic"
:label="t('harvester.addons.usbController.titles.basic')"
:weight="99"
>
<RadioGroup
v-model="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
</Tab>
</Tabbed>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>

View File

@ -0,0 +1,65 @@
<script>
import { _EDIT } from '@shell/config/query-params';
import { allHash } from '@shell/utils/promise';
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';
export default {
name: 'VirtualMachineUSBDevices',
components: {
Banner,
LabeledSelect,
},
props: {
mode: {
type: String,
default: _EDIT
},
value: {
type: Object,
default: () => {}
},
vm: {
type: Object,
default: () => {}
}
},
async fetch() {
const hash = {
devices: this.$store.dispatch('harvester/findAll', { type: HCI.USB_DEVICE }),
vms: this.$store.dispatch(`harvester/findAll`, { type: HCI.VM })
};
const res = await allHash(hash);
for (const key in res) {
this[key] = res[key];
}
},
data() {
return {
deviceSchema: this.$store.getters['harvester/schemaFor'](HCI.USB_DEVICE),
deviceHeaders: [
{ ...STATE },
SIMPLE_NAME,
],
devices: [],
vms: [],
selectedDevices: [],
};
},
computed: {
},
};
</script>
<template>
<div> USB Devices </div>
</template>

View File

@ -15,6 +15,11 @@ import Labels from '@shell/components/form/Labels';
import NodeScheduling from '@shell/components/form/NodeScheduling';
import PodAffinity from '@shell/components/form/PodAffinity';
import AccessCredentials from './VirtualMachineAccessCredentials';
import PciDevices from './VirtualMachinePciDevices/index';
import VGpuDevices from './VirtualMachineVGpuDevices/index';
import UsbDevices from './VirtualMachineUSBDevices/index';
import RestartVMDialog from '../../dialog/RestartVMDialog';
import KeyValue from '@shell/components/form/KeyValue';
import { clear } from '@shell/utils/array';
@ -69,10 +74,11 @@ export default {
PciDevices,
RestartVMDialog,
UnitInput,
VirtualMachineVGpuDevices,
VGpuDevices,
KeyValue,
Banner,
MessageLink
MessageLink,
UsbDevices,
},
mixins: [CreateEditView, VM_MIXIN],
@ -652,10 +658,14 @@ export default {
</Tab>
<Tab v-if="enabledSriovgpu" :label="t('harvester.tab.vGpuDevices')" name="vGpuDevices" :weight="-6">
<VirtualMachineVGpuDevices :mode="mode" :value="spec.template.spec" :vm="value" />
<VGpuDevices :mode="mode" :value="spec.template.spec" :vm="value" />
</Tab>
<Tab v-if="isEdit" :label="t('harvester.tab.accessCredentials')" name="accessCredentials" :weight="-7">
<Tab v-if="enabledUSB" :label="t('harvester.tab.usbDevices')" name="usbDevices" :weight="-7">
<UsbDevices :mode="mode" :value="spec.template.spec" :vm="value" />
</Tab>
<Tab v-if="isEdit" :label="t('harvester.tab.accessCredentials')" name="accessCredentials" :weight="-8">
<AccessCredentials v-model:value="accessCredentials" :mode="mode" :resource="value" :is-qemu-installed="isQemuInstalled" />
</Tab>

View File

@ -241,6 +241,7 @@ harvester:
accessCredentials: Access Credentials
pciDevices: PCI Devices
vGpuDevices: vGPU Devices
usbDevices: USB Devices
vmScheduling: Virtual Machine Scheduling
quotas: Quotas
snapshots: Snapshots
@ -1221,6 +1222,9 @@ harvester:
location: Driver Location
parsingSpecError:
The field 'spec.valuesContent' has invalid format.
usbController:
titles:
basic: Basic
loadBalancer:
label: Load Balancers
@ -1360,6 +1364,14 @@ harvester:
howToUseDevice: 'Use the table below to enable vGPU devices you want to use in this virtual machine.'
deviceInTheSameHost: 'You can only select devices on the same host.'
usb:
label: USB Devices
noPermission: Please contact system admin to add Harvester addons first
goSetting:
prefix: The usb addon is not enabled, click
middle: here
suffix: to enable it to manage your USB devices.
harvesterVlanConfigMigrateDialog:
targetClusterNetwork:
label: Target Cluster Network

View File

@ -0,0 +1,97 @@
<script>
import { HCI } from '../types';
import { STATE, SIMPLE_NAME } from '@shell/config/table-headers';
import { allHash } from '@shell/utils/promise';
import Banner from '@components/Banner/Banner.vue';
import Loading from '@shell/components/Loading';
import MessageLink from '@shell/components/MessageLink';
import ResourceTable from '@shell/components/ResourceTable';
import { ADD_ONS } from '../config/harvester-map';
export default {
name: 'ListUSBDevices',
components: {
Banner,
Loading,
MessageLink,
ResourceTable
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.schema = this.$store.getters[`${ inStore }/schemaFor`](HCI.USB_DEVICE);
this.hasAddonSchema = this.$store.getters[`${ inStore }/schemaFor`](HCI.ADD_ONS);
if (this.hasSchema) {
try {
const hash = await allHash({
usbDevices: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.USB_DEVICE }),
addons: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.ADD_ONS }),
});
this.hasUSBAddon = hash.addons.find(addon => addon.name === ADD_ONS.USB_DEVICE_CONTROLLER)?.spec?.enabled === true;
} catch (e) {}
}
},
data() {
return {
hasAddonSchema: false,
hasUSBAddon: false,
schema: null,
toUSBAddon: `${ HCI.ADD_ONS }/harvester-system/${ ADD_ONS.USB_DEVICE_CONTROLLER }?mode=edit`,
headers: [
{ ...STATE },
SIMPLE_NAME,
],
};
},
computed: {
hasSchema() {
return !!this.schema;
},
rows() {
const inStore = this.$store.getters['currentProduct'].inStore;
return this.$store.getters[`${ inStore }/all`](HCI.USB_DEVICE) || [];
}
},
typeDisplay() {
return this.t('harvester.usb.label');
}
};
</script>
<template>
<Loading v-if="$fetchState.pending" />
<div v-else-if="!hasAddonSchema">
<Banner color="warning">
{{ t('harvester.usb.noPermission') }}
</Banner>
</div>
<div v-else-if="!hasUSBAddon">
<Banner color="warning">
<MessageLink
:to="toUSBAddon"
prefix-label="harvester.usb.goSetting.prefix"
middle-label="harvester.usb.goSetting.middle"
suffix-label="harvester.usb.goSetting.suffix"
/>
</Banner>
</div>
<ResourceTable
v-else-if="hasSchema"
:headers="headers"
:schema="schema"
:rows="rows"
:use-query-params-for-simple-filtering="true"
:sort-generation-fn="sortGenerationFn"
:rows-per-page="10"
@submit.prevent
/>
</template>

View File

@ -119,12 +119,13 @@ export default {
const hasPCISchema = !!this.$store.getters[`${ inStore }/schemaFor`](HCI.PCI_DEVICE);
const hasSRIOVGPUSchema = !!this.$store.getters[`${ inStore }/schemaFor`](HCI.SR_IOVGPU_DEVICE);
const hasUSBSchema = !!this.$store.getters[`${ inStore }/schemaFor`](HCI.USB_DEVICE);
const hasPCIAddon = res.addons.find(addon => addon.name === ADD_ONS.PCI_DEVICE_CONTROLLER)?.spec?.enabled === true;
const hasSriovgpuAddon = res.addons.find(addon => addon.name === ADD_ONS.NVIDIA_DRIVER_TOOLKIT_CONTROLLER)?.spec?.enabled === true;
const enabledAddons = res.addons.reduce((acc, addon) => ({ ...acc, [addon.name]: addon.spec?.enabled }), {});
this.enabledPCI = hasPCIAddon && hasPCISchema;
this.enabledSriovgpu = hasSriovgpuAddon && hasPCIAddon && hasSRIOVGPUSchema;
this.enabledPCI = hasPCISchema && enabledAddons[ADD_ONS.PCI_DEVICE_CONTROLLER];
this.enabledSriovgpu = hasSRIOVGPUSchema && enabledAddons[ADD_ONS.PCI_DEVICE_CONTROLLER] && enabledAddons[ADD_ONS.NVIDIA_DRIVER_TOOLKIT_CONTROLLER];
this.enabledUSB = hasUSBSchema && enabledAddons[ADD_ONS.USB_DEVICE_CONTROLLER];
},
data() {
@ -165,6 +166,7 @@ export default {
saveNetworkDataAsClearText: false,
enabledPCI: false,
enabledSriovgpu: false,
enabledUSB: false,
immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW,
terminationGracePeriodSeconds: '',
cpuPinning: false,

View File

@ -38,6 +38,7 @@ export const HCI = {
SR_IOV: 'devices.harvesterhci.io.sriovnetworkdevice',
VGPU_DEVICE: 'devices.harvesterhci.io.vgpudevice',
SR_IOVGPU_DEVICE: 'devices.harvesterhci.io.sriovgpudevice',
USB_DEVICE: 'devices.harvesterhci.io.usbdevice',
VLAN_CONFIG: 'network.harvesterhci.io.vlanconfig',
VLAN_STATUS: 'network.harvesterhci.io.vlanstatus',
ADD_ONS: 'harvesterhci.io.addon',