diff --git a/pkg/harvester/config/harvester-cluster.js b/pkg/harvester/config/harvester-cluster.js index 3830fc50..5931c28b 100644 --- a/pkg/harvester/config/harvester-cluster.js +++ b/pkg/harvester/config/harvester-cluster.js @@ -457,6 +457,7 @@ export function init($plugin, store) { HCI.PCI_DEVICE, HCI.SR_IOVGPU_DEVICE, HCI.VGPU_DEVICE, + HCI.MIG_CONFIGURATION, HCI.USB_DEVICE, HCI.ADD_ONS, HCI.SECRET, @@ -849,6 +850,26 @@ export function init($plugin, store) { ] }); + virtualType({ + labelKey: 'harvester.migconfiguration.label', + group: 'advanced', + weight: 12, + name: HCI.MIG_CONFIGURATION, + namespaced: false, + route: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.MIG_CONFIGURATION } + }, + exact: false, + ifHaveType: HCI.MIG_CONFIGURATION, + }); + + configureType(HCI.MIG_CONFIGURATION, { + isCreatable: false, + hiddenNamespaceGroupButton: true, + canYaml: false, + }); + virtualType({ labelKey: 'harvester.usb.label', group: 'advanced', diff --git a/pkg/harvester/detail/devices.harvesterhci.io.migconfiguration.vue b/pkg/harvester/detail/devices.harvesterhci.io.migconfiguration.vue new file mode 100644 index 00000000..6adbf4e3 --- /dev/null +++ b/pkg/harvester/detail/devices.harvesterhci.io.migconfiguration.vue @@ -0,0 +1,124 @@ + + + diff --git a/pkg/harvester/edit/devices.harvesterhci.io.migconfiguration.vue b/pkg/harvester/edit/devices.harvesterhci.io.migconfiguration.vue new file mode 100644 index 00000000..dcb5ed50 --- /dev/null +++ b/pkg/harvester/edit/devices.harvesterhci.io.migconfiguration.vue @@ -0,0 +1,133 @@ + + + diff --git a/pkg/harvester/l10n/en-us.yaml b/pkg/harvester/l10n/en-us.yaml index cd7ce789..a1f26eae 100644 --- a/pkg/harvester/l10n/en-us.yaml +++ b/pkg/harvester/l10n/en-us.yaml @@ -272,6 +272,7 @@ harvester: cronExpression: Cron Expression retain: Retain scheduleType: Type + configuredProfiles: Configured Profiles maxFailure: Max Failure sourceVm: Source Virtual Machine vmSchedule: Virtual Machine Schedule @@ -1679,6 +1680,26 @@ harvester: middle: here suffix: to enable it to manage your SR-IOV GPU devices. + migconfiguration: + label: vGPU MIG Configurations + infoBanner: To configure the MIG configuration, please disable it first and re-enable after editing the configuration. + profileSpec: Profile Specs + profileStatus: Profile Status + tableHeaders: + profileName: Profile Name + total: Total + vGPUID: vGPU ID + available: Available + requested: Requested + requested: Requested + available: Available + total: Total + vGPUID: vGPU ID + goSriovGPU: + prefix: Please enable the supported GPU devices in + middle: SR-IOV GPU Devices + suffix: page to manage the vGPU MIG configurations. + vgpu: label: vGPU Devices noPermission: Please contact system administrator to add Harvester add-ons first. @@ -1953,6 +1974,12 @@ typeLabel: other { vGPU Devices } } + devices.harvesterhci.io.migconfiguration: |- + {count, plural, + one { vGPU MIG Configuration } + other { vGPU MIG Configurations } + } + harvesterhci.io.secret: |- {count, plural, one { Secret } diff --git a/pkg/harvester/list/devices.harvesterhci.io.migconfiguration.vue b/pkg/harvester/list/devices.harvesterhci.io.migconfiguration.vue new file mode 100644 index 00000000..7ddbd1b1 --- /dev/null +++ b/pkg/harvester/list/devices.harvesterhci.io.migconfiguration.vue @@ -0,0 +1,177 @@ + + + diff --git a/pkg/harvester/models/devices.harvesterhci.io.migconfiguration.js b/pkg/harvester/models/devices.harvesterhci.io.migconfiguration.js new file mode 100644 index 00000000..411f30ec --- /dev/null +++ b/pkg/harvester/models/devices.harvesterhci.io.migconfiguration.js @@ -0,0 +1,116 @@ +import SteveModel from '@shell/plugins/steve/steve-class'; +import { escapeHtml } from '@shell/utils/string'; +import { colorForState } from '@shell/plugins/dashboard-store/resource-class'; + +/** + * Class representing vGPU MIGConfiguration resource. + * @extends SteveModal + */ +export default class MIGCONFIGURATION extends SteveModel { + get _availableActions() { + let out = super._availableActions; + + out = out.map((action) => { + if (action.action === 'showConfiguration') { + return { ...action, enabled: !this.spec.enabled }; + } else if (action.action === 'goToEditYaml') { + return { ...action, enabled: !this.spec.enabled }; + } else if (action.action === 'goToEdit') { + // need to wait for status to be disabled or empty value, then allow user to editConfig + return { ...action, enabled: !this.spec.enabled && ['disabled', ''].includes(this.configStatus) }; + } else { + return action; + } + }); + + out.push( + { + action: 'enableConfig', + enabled: !this.isEnabled, + icon: 'icon icon-fw icon-dot', + label: 'Enable', + }, + { + action: 'disableConfig', + enabled: this.isEnabled, + icon: 'icon icon-fw icon-dot-open', + label: 'Disable', + }, + ); + + return out; + } + + get canYaml() { + return false; + } + + get disableResourceDetailDrawer() { + return true; + } + + get canDelete() { + return false; + } + + get configStatus() { + return this.status.status; + } + + get actualState() { + return this.isEnabled ? 'Enabled' : 'Disabled'; + } + + get stateDisplay() { + return this.actualState; + } + + get stateColor() { + const state = this.actualState; + + return colorForState(state); + } + + get isEnabled() { + return this.spec.enabled; + } + + get configuredProfiles() { + const configuredProfiles = this.spec?.profileSpec?.filter((p) => p.requested > 0) || []; + + if (configuredProfiles.length === 0) { + return ''; + } + + return configuredProfiles + .map((profile) => `${ profile.name } * ${ profile.requested }`) + .join(', '); + } + + async enableConfig() { + try { + this.spec.enabled = true; + await this.save(); + } catch (err) { + this.$dispatch('growl/fromError', { + title: this.t('generic.notification.title.error', { name: escapeHtml(this.name) }), + err, + }, { root: true }); + } + } + + async disableConfig() { + const { enabled: currentEnabled } = this.spec; + + try { + this.spec.enabled = false; + await this.save(); + } catch (err) { + this.spec.enabled = currentEnabled; + this.$dispatch('growl/fromError', { + title: this.t('generic.notification.title.error', { name: escapeHtml(this.name) }), + err, + }, { root: true }); + } + } +} diff --git a/pkg/harvester/types.ts b/pkg/harvester/types.ts index b8f8908a..b4f7c6ea 100644 --- a/pkg/harvester/types.ts +++ b/pkg/harvester/types.ts @@ -44,6 +44,7 @@ export const HCI = { SR_IOVGPU_DEVICE: 'devices.harvesterhci.io.sriovgpudevice', USB_DEVICE: 'devices.harvesterhci.io.usbdevice', USB_CLAIM: 'devices.harvesterhci.io.usbdeviceclaim', + MIG_CONFIGURATION: 'devices.harvesterhci.io.migconfiguration', VLAN_CONFIG: 'network.harvesterhci.io.vlanconfig', VLAN_STATUS: 'network.harvesterhci.io.vlanstatus', ADD_ONS: 'harvesterhci.io.addon', @@ -53,7 +54,7 @@ export const HCI = { LB: 'loadbalancer.harvesterhci.io.loadbalancer', IP_POOL: 'loadbalancer.harvesterhci.io.ippool', HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig', - LVM_VOLUME_GROUP: 'harvesterhci.io.lvmvolumegroup' + LVM_VOLUME_GROUP: 'harvesterhci.io.lvmvolumegroup', }; export const VOLUME_SNAPSHOT = 'snapshot.storage.k8s.io.volumesnapshot';