mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2025-12-13 13:11:43 +00:00
feat: add vGPU MIG Configuration page (#576)
* feat: add vGPU MIGConfiguration page Signed-off-by: Andy Lee <andy.lee@suse.com> * feat: add detail page Signed-off-by: Andy Lee <andy.lee@suse.com> * feat: add banner Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: allow editConfig when status is empty Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: remove unneeded code Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: only show disable action if MIGConfig is enabled Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: some UI flow changes Signed-off-by: Andy Lee <andy.lee@suse.com> * feat: show configured profile in table Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: show configured profiles with requested count Signed-off-by: Andy Lee <andy.lee@suse.com> * refactor: based on review Signed-off-by: Andy Lee <andy.lee@suse.com> --------- Signed-off-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
parent
b4980a51e7
commit
6f90cae482
@ -457,6 +457,7 @@ export function init($plugin, store) {
|
|||||||
HCI.PCI_DEVICE,
|
HCI.PCI_DEVICE,
|
||||||
HCI.SR_IOVGPU_DEVICE,
|
HCI.SR_IOVGPU_DEVICE,
|
||||||
HCI.VGPU_DEVICE,
|
HCI.VGPU_DEVICE,
|
||||||
|
HCI.MIG_CONFIGURATION,
|
||||||
HCI.USB_DEVICE,
|
HCI.USB_DEVICE,
|
||||||
HCI.ADD_ONS,
|
HCI.ADD_ONS,
|
||||||
HCI.SECRET,
|
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({
|
virtualType({
|
||||||
labelKey: 'harvester.usb.label',
|
labelKey: 'harvester.usb.label',
|
||||||
group: 'advanced',
|
group: 'advanced',
|
||||||
|
|||||||
@ -0,0 +1,124 @@
|
|||||||
|
<script>
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
||||||
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
|
import SortableTable from '@shell/components/SortableTable';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
components: {
|
||||||
|
ResourceTabs,
|
||||||
|
Tab,
|
||||||
|
SortableTable,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
headers() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'profileName',
|
||||||
|
labelKey: 'harvester.migconfiguration.tableHeaders.profileName',
|
||||||
|
value: 'name',
|
||||||
|
width: 75,
|
||||||
|
sort: 'name',
|
||||||
|
dashIfEmpty: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'vGPUID',
|
||||||
|
labelKey: 'harvester.migconfiguration.tableHeaders.vGPUID',
|
||||||
|
value: 'vGPUID',
|
||||||
|
width: 75,
|
||||||
|
sort: 'vGPUID',
|
||||||
|
dashIfEmpty: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'available',
|
||||||
|
labelKey: 'harvester.migconfiguration.tableHeaders.available',
|
||||||
|
value: 'available',
|
||||||
|
width: 75,
|
||||||
|
sort: 'available',
|
||||||
|
align: 'center',
|
||||||
|
dashIfEmpty: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'requested',
|
||||||
|
labelKey: 'harvester.migconfiguration.tableHeaders.requested',
|
||||||
|
value: 'requested',
|
||||||
|
width: 75,
|
||||||
|
sort: 'requested',
|
||||||
|
align: 'center',
|
||||||
|
dashIfEmpty: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'total',
|
||||||
|
labelKey: 'harvester.migconfiguration.tableHeaders.total',
|
||||||
|
value: 'total',
|
||||||
|
width: 75,
|
||||||
|
sort: 'total',
|
||||||
|
align: 'center',
|
||||||
|
dashIfEmpty: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
rows() {
|
||||||
|
let out = (this.value?.status?.profileStatus || []).map((profile) => {
|
||||||
|
const {
|
||||||
|
id, name, total, available
|
||||||
|
} = profile;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
total,
|
||||||
|
available,
|
||||||
|
vGPUID: profile.vGPUID?.join(', ') || '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
out = out.map((row) => {
|
||||||
|
const requested = this.value?.spec?.profileSpec.find((p) => p.id === row.id)?.requested || 0;
|
||||||
|
|
||||||
|
return { ...row, requested };
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ResourceTabs
|
||||||
|
:value="value"
|
||||||
|
:need-events="false"
|
||||||
|
:need-related="false"
|
||||||
|
:mode="mode"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
name="Profile Status"
|
||||||
|
:label="t('harvester.migconfiguration.profileStatus')"
|
||||||
|
>
|
||||||
|
<SortableTable
|
||||||
|
:headers="headers"
|
||||||
|
:rows="rows"
|
||||||
|
key-field="condition"
|
||||||
|
default-sort-by="condition"
|
||||||
|
:table-actions="false"
|
||||||
|
:row-actions="false"
|
||||||
|
:search="false"
|
||||||
|
/>
|
||||||
|
</Tab>
|
||||||
|
</ResourceTabs>
|
||||||
|
</template>
|
||||||
133
pkg/harvester/edit/devices.harvesterhci.io.migconfiguration.vue
Normal file
133
pkg/harvester/edit/devices.harvesterhci.io.migconfiguration.vue
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<script>
|
||||||
|
import Tabbed from '@shell/components/Tabbed';
|
||||||
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
|
import CruResource from '@shell/components/CruResource';
|
||||||
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||||
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
|
import LabelValue from '@shell/components/LabelValue';
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HarvesterEditMIGConfiguration',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Tab,
|
||||||
|
Tabbed,
|
||||||
|
CruResource,
|
||||||
|
LabeledInput,
|
||||||
|
NameNsDescription,
|
||||||
|
LabelValue
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
const { profileSpec } = this.value.spec;
|
||||||
|
|
||||||
|
return { profileSpec: profileSpec || [] };
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.registerBeforeHook) {
|
||||||
|
this.registerBeforeHook(this.updateBeforeSave);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isView() {
|
||||||
|
return this.mode === 'view';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateBeforeSave() {
|
||||||
|
// MIGConfiguration CRD don't have any namespace field,
|
||||||
|
// so we need to remove the namespace field before saving
|
||||||
|
delete this.value.metadata.namespace;
|
||||||
|
// enable the MIGConfiguration when saving
|
||||||
|
this.value.spec.enabled = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
labelTitle(profile) {
|
||||||
|
return `${ profile.name } (available : ${ this.available(profile) })`;
|
||||||
|
},
|
||||||
|
|
||||||
|
available(profile) {
|
||||||
|
const count = this.value.status?.profileStatus?.find((p) => p.id === profile.id)?.available;
|
||||||
|
|
||||||
|
return count || 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRequested(neu, profile) {
|
||||||
|
if (neu === null || neu === '') return;
|
||||||
|
const newValue = Number(neu);
|
||||||
|
const availableCount = this.available(profile);
|
||||||
|
|
||||||
|
if (newValue < 0) {
|
||||||
|
profile.requested = 0;
|
||||||
|
} else if ( newValue > availableCount ) {
|
||||||
|
profile.requested = availableCount;
|
||||||
|
} else {
|
||||||
|
profile.requested = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CruResource
|
||||||
|
:done-route="doneRoute"
|
||||||
|
:resource="value"
|
||||||
|
:mode="mode"
|
||||||
|
:errors="errors"
|
||||||
|
:apply-hooks="applyHooks"
|
||||||
|
finish-button-mode="enable"
|
||||||
|
@finish="save"
|
||||||
|
@error="e=>errors=e"
|
||||||
|
>
|
||||||
|
<NameNsDescription
|
||||||
|
:value="value"
|
||||||
|
:mode="mode"
|
||||||
|
/>
|
||||||
|
<Tabbed
|
||||||
|
v-bind="$attrs"
|
||||||
|
class="mt-15"
|
||||||
|
:side-tabs="true"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
name="profileSpec"
|
||||||
|
:label="t('harvester.migconfiguration.profileSpec')"
|
||||||
|
:weight="1"
|
||||||
|
class="bordered-table"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(profile, index) in profileSpec"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<LabelValue
|
||||||
|
:value="labelTitle(profile)"
|
||||||
|
class="mb-10"
|
||||||
|
/>
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="profile.requested"
|
||||||
|
:min="0"
|
||||||
|
:disabled="isView"
|
||||||
|
type="number"
|
||||||
|
class="mb-20"
|
||||||
|
:label="`${t('harvester.migconfiguration.requested')}`"
|
||||||
|
@update:value="updateRequested($event, profile)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</Tabbed>
|
||||||
|
</CruResource>
|
||||||
|
</template>
|
||||||
@ -272,6 +272,7 @@ harvester:
|
|||||||
cronExpression: Cron Expression
|
cronExpression: Cron Expression
|
||||||
retain: Retain
|
retain: Retain
|
||||||
scheduleType: Type
|
scheduleType: Type
|
||||||
|
configuredProfiles: Configured Profiles
|
||||||
maxFailure: Max Failure
|
maxFailure: Max Failure
|
||||||
sourceVm: Source Virtual Machine
|
sourceVm: Source Virtual Machine
|
||||||
vmSchedule: Virtual Machine Schedule
|
vmSchedule: Virtual Machine Schedule
|
||||||
@ -1679,6 +1680,26 @@ harvester:
|
|||||||
middle: here
|
middle: here
|
||||||
suffix: to enable it to manage your SR-IOV GPU devices.
|
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:
|
vgpu:
|
||||||
label: vGPU Devices
|
label: vGPU Devices
|
||||||
noPermission: Please contact system administrator to add Harvester add-ons first.
|
noPermission: Please contact system administrator to add Harvester add-ons first.
|
||||||
@ -1953,6 +1974,12 @@ typeLabel:
|
|||||||
other { vGPU Devices }
|
other { vGPU Devices }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
devices.harvesterhci.io.migconfiguration: |-
|
||||||
|
{count, plural,
|
||||||
|
one { vGPU MIG Configuration }
|
||||||
|
other { vGPU MIG Configurations }
|
||||||
|
}
|
||||||
|
|
||||||
harvesterhci.io.secret: |-
|
harvesterhci.io.secret: |-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
one { Secret }
|
one { Secret }
|
||||||
|
|||||||
177
pkg/harvester/list/devices.harvesterhci.io.migconfiguration.vue
Normal file
177
pkg/harvester/list/devices.harvesterhci.io.migconfiguration.vue
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
<script>
|
||||||
|
import { STATE, 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 ResourceTable from '@shell/components/ResourceTable';
|
||||||
|
import { HCI } from '../types';
|
||||||
|
import { ADD_ONS } from '../config/harvester-map';
|
||||||
|
import MessageLink from '@shell/components/MessageLink';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ListMIGConfigurations',
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Banner,
|
||||||
|
Loading,
|
||||||
|
ResourceTable,
|
||||||
|
MessageLink,
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
this.schema = this.$store.getters[`${ inStore }/schemaFor`](HCI.MIG_CONFIGURATION);
|
||||||
|
this.hasAddonSchema = this.$store.getters[`${ inStore }/schemaFor`](HCI.ADD_ONS);
|
||||||
|
|
||||||
|
if (this.hasSchema) {
|
||||||
|
try {
|
||||||
|
const hash = await allHash({
|
||||||
|
migconfigs: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.MIG_CONFIGURATION }),
|
||||||
|
vGpuDevices: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VGPU_DEVICE }),
|
||||||
|
addons: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.ADD_ONS })
|
||||||
|
});
|
||||||
|
|
||||||
|
this.hasPCIAddon = hash.addons.find((addon) => addon.name === ADD_ONS.PCI_DEVICE_CONTROLLER)?.spec?.enabled === true;
|
||||||
|
this.hasSriovgpuAddon = hash.addons.find((addon) => addon.name === ADD_ONS.NVIDIA_DRIVER_TOOLKIT_CONTROLLER)?.spec?.enabled === true;
|
||||||
|
this.hasSRIOVGPUSchema = !!this.$store.getters[`${ inStore }/schemaFor`](HCI.SR_IOVGPU_DEVICE);
|
||||||
|
|
||||||
|
if (this.hasSRIOVGPUSchema) {
|
||||||
|
await this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.SR_IOVGPU_DEVICE });
|
||||||
|
}
|
||||||
|
this.rows = hash.migconfigs;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
rows: [],
|
||||||
|
schema: null,
|
||||||
|
hasAddonSchema: false,
|
||||||
|
hasPCIAddon: false,
|
||||||
|
hasSriovgpuAddon: false,
|
||||||
|
hasSRIOVGPUSchema: false,
|
||||||
|
toVGpuAddon: `${ HCI.ADD_ONS }/harvester-system/${ ADD_ONS.NVIDIA_DRIVER_TOOLKIT_CONTROLLER }?mode=edit`,
|
||||||
|
toPciAddon: `${ HCI.ADD_ONS }/harvester-system/${ ADD_ONS.PCI_DEVICE_CONTROLLER }?mode=edit`,
|
||||||
|
SRIOVGPUPage: `${ HCI.ADD_ONS }/harvester-system/${ ADD_ONS.NVIDIA_DRIVER_TOOLKIT_CONTROLLER }?mode=edit`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
hasSchema() {
|
||||||
|
return !!this.schema;
|
||||||
|
},
|
||||||
|
|
||||||
|
rowsData() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const rows = this.$store.getters[`${ inStore }/all`](HCI.MIG_CONFIGURATION) || [];
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
},
|
||||||
|
|
||||||
|
sriovGPUPage() {
|
||||||
|
return {
|
||||||
|
name: 'harvester-c-cluster-resource',
|
||||||
|
params: { cluster: this.$store.getters['clusterId'], resource: HCI.SR_IOVGPU_DEVICE },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
showEnableSRIOVGPUMessage() {
|
||||||
|
return this.rowsData.length === 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
headers() {
|
||||||
|
const cols = [
|
||||||
|
STATE,
|
||||||
|
NAME,
|
||||||
|
{
|
||||||
|
name: 'address',
|
||||||
|
label: 'Address',
|
||||||
|
value: 'spec.gpuAddress',
|
||||||
|
sort: ['spec.gpuAddress']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Configured Profile',
|
||||||
|
label: 'Configured Count',
|
||||||
|
labelKey: 'harvester.tableHeaders.configuredProfiles',
|
||||||
|
value: 'configuredProfiles',
|
||||||
|
sort: ['configuredProfiles'],
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'status',
|
||||||
|
label: 'Status',
|
||||||
|
labelKey: 'tableHeaders.status',
|
||||||
|
sort: ['status.status'],
|
||||||
|
value: 'status.status',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return cols;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Loading v-if="$fetchState.pending" />
|
||||||
|
<div v-else-if="!hasAddonSchema">
|
||||||
|
<Banner color="warning">
|
||||||
|
{{ t('harvester.vgpu.noPermission') }}
|
||||||
|
</Banner>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!hasSriovgpuAddon || !hasPCIAddon">
|
||||||
|
<Banner
|
||||||
|
v-if="!hasSriovgpuAddon"
|
||||||
|
color="warning"
|
||||||
|
>
|
||||||
|
<MessageLink
|
||||||
|
:to="toVGpuAddon"
|
||||||
|
prefix-label="harvester.vgpu.goSetting.prefix"
|
||||||
|
middle-label="harvester.vgpu.goSetting.middle"
|
||||||
|
suffix-label="harvester.vgpu.goSetting.suffix"
|
||||||
|
/>
|
||||||
|
</Banner>
|
||||||
|
<Banner
|
||||||
|
v-if="!hasPCIAddon"
|
||||||
|
color="warning"
|
||||||
|
>
|
||||||
|
<MessageLink
|
||||||
|
:to="toPciAddon"
|
||||||
|
prefix-label="harvester.pci.goSetting.prefix"
|
||||||
|
middle-label="harvester.pci.goSetting.middle"
|
||||||
|
suffix-label="harvester.pci.goSetting.suffix"
|
||||||
|
/>
|
||||||
|
</Banner>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="hasSchema">
|
||||||
|
<Banner
|
||||||
|
v-if="showEnableSRIOVGPUMessage"
|
||||||
|
color="warning"
|
||||||
|
>
|
||||||
|
<MessageLink
|
||||||
|
:to="sriovGPUPage"
|
||||||
|
prefix-label="harvester.migconfiguration.goSriovGPU.prefix"
|
||||||
|
middle-label="harvester.migconfiguration.goSriovGPU.middle"
|
||||||
|
suffix-label="harvester.migconfiguration.goSriovGPU.suffix"
|
||||||
|
/>
|
||||||
|
</Banner>
|
||||||
|
<Banner
|
||||||
|
v-if="!showEnableSRIOVGPUMessage"
|
||||||
|
color="warning"
|
||||||
|
:label="t('harvester.migconfiguration.infoBanner')"
|
||||||
|
/>
|
||||||
|
<ResourceTable
|
||||||
|
v-bind="$attrs"
|
||||||
|
:groupable="false"
|
||||||
|
:namespaced="false"
|
||||||
|
:headers="headers"
|
||||||
|
:schema="schema"
|
||||||
|
:rows="rowsData"
|
||||||
|
key-field="_key"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
116
pkg/harvester/models/devices.harvesterhci.io.migconfiguration.js
Normal file
116
pkg/harvester/models/devices.harvesterhci.io.migconfiguration.js
Normal file
@ -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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -44,6 +44,7 @@ export const HCI = {
|
|||||||
SR_IOVGPU_DEVICE: 'devices.harvesterhci.io.sriovgpudevice',
|
SR_IOVGPU_DEVICE: 'devices.harvesterhci.io.sriovgpudevice',
|
||||||
USB_DEVICE: 'devices.harvesterhci.io.usbdevice',
|
USB_DEVICE: 'devices.harvesterhci.io.usbdevice',
|
||||||
USB_CLAIM: 'devices.harvesterhci.io.usbdeviceclaim',
|
USB_CLAIM: 'devices.harvesterhci.io.usbdeviceclaim',
|
||||||
|
MIG_CONFIGURATION: 'devices.harvesterhci.io.migconfiguration',
|
||||||
VLAN_CONFIG: 'network.harvesterhci.io.vlanconfig',
|
VLAN_CONFIG: 'network.harvesterhci.io.vlanconfig',
|
||||||
VLAN_STATUS: 'network.harvesterhci.io.vlanstatus',
|
VLAN_STATUS: 'network.harvesterhci.io.vlanstatus',
|
||||||
ADD_ONS: 'harvesterhci.io.addon',
|
ADD_ONS: 'harvesterhci.io.addon',
|
||||||
@ -53,7 +54,7 @@ export const HCI = {
|
|||||||
LB: 'loadbalancer.harvesterhci.io.loadbalancer',
|
LB: 'loadbalancer.harvesterhci.io.loadbalancer',
|
||||||
IP_POOL: 'loadbalancer.harvesterhci.io.ippool',
|
IP_POOL: 'loadbalancer.harvesterhci.io.ippool',
|
||||||
HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig',
|
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';
|
export const VOLUME_SNAPSHOT = 'snapshot.storage.k8s.io.volumesnapshot';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user