From d077de20420e16a5206fcb38496c41549df15f96 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Fri, 19 Jul 2024 17:19:24 +0200 Subject: [PATCH 01/25] Update storage class provisioner labels in view of new provisioners; Code rafactoring. Signed-off-by: Francesco Torchia --- pkg/harvester/components/settings/csi-driver-config.vue | 3 +-- pkg/harvester/edit/harvesterhci.io.storage/index.vue | 5 ++--- pkg/harvester/l10n/en-us.yaml | 6 ++++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/harvester/components/settings/csi-driver-config.vue b/pkg/harvester/components/settings/csi-driver-config.vue index 02638fdd3bb..2f77c8a9dcb 100644 --- a/pkg/harvester/components/settings/csi-driver-config.vue +++ b/pkg/harvester/components/settings/csi-driver-config.vue @@ -4,8 +4,7 @@ import LabeledSelect from '@shell/components/form/LabeledSelect'; import InfoBox from '@shell/components/InfoBox'; import { CSI_DRIVER, VOLUME_SNAPSHOT_CLASS } from '../../types'; import { allHash } from '@shell/utils/promise'; - -const LONGHORN_DRIVER = 'driver.longhorn.io'; +import { LONGHORN_DRIVER } from '@shell/models/persistentvolume'; export default { name: 'HarvesterCsiDriver', diff --git a/pkg/harvester/edit/harvesterhci.io.storage/index.vue b/pkg/harvester/edit/harvesterhci.io.storage/index.vue index 34dbc90bf17..22d8d6832f4 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/index.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/index.vue @@ -18,8 +18,7 @@ import { STORAGE_CLASS, LONGHORN } from '@shell/config/types'; import { CSI_DRIVER } from '../../types'; import { allHash } from '@shell/utils/promise'; import { clone } from '@shell/utils/object'; - -const LONGHORN_DRIVER = 'driver.longhorn.io'; +import { LONGHORN_DRIVER } from '@shell/models/persistentvolume'; export default { name: 'HarvesterStorage', @@ -122,7 +121,7 @@ export default { provisioners() { const csiDrivers = this.$store.getters[`${ this.inStore }/all`](CSI_DRIVER) || []; - const format = { [LONGHORN_DRIVER]: 'storageClass.longhorn.title' }; + const format = { [LONGHORN_DRIVER]: 'harvester.storage.storageClass.longhornV1.label' }; return csiDrivers.map((provisioner) => { return { diff --git a/pkg/harvester/l10n/en-us.yaml b/pkg/harvester/l10n/en-us.yaml index 5e1fe8a1b59..011bdcb6cb3 100644 --- a/pkg/harvester/l10n/en-us.yaml +++ b/pkg/harvester/l10n/en-us.yaml @@ -1136,6 +1136,12 @@ harvester: label: Disk Selector storageClass: label: Storage Class + longhornV1: + label: LonghornV1 (CSI) + longhornV2: + label: LonghornV2 (CSI) + lvm: + label: LVM title: Storage Classes customize: volumeBindingMode: From 887d8b843c1d0e7e4a109823089eda115550cc69 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Fri, 19 Jul 2024 17:20:36 +0200 Subject: [PATCH 02/25] Add provisioner selector in Host edit page/Disk Signed-off-by: Francesco Torchia --- pkg/harvester/config/harvester-map.js | 1 + .../harvesterhci.io.host/HarvesterDisk.vue | 51 ++++++++++++++++++- pkg/harvester/l10n/en-us.yaml | 1 + 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/pkg/harvester/config/harvester-map.js b/pkg/harvester/config/harvester-map.js index 39e01ad49ac..7cb5e10e3bf 100644 --- a/pkg/harvester/config/harvester-map.js +++ b/pkg/harvester/config/harvester-map.js @@ -68,6 +68,7 @@ export const ADD_ONS = { RANCHER_LOGGING: 'rancher-logging', RANCHER_MONITORING: 'rancher-monitoring', VM_IMPORT_CONTROLLER: 'vm-import-controller', + HARVESTER_CSI_DRIVER_LVM: 'harvester-csi-driver-lvm' }; export const CSI_SECRETS = { diff --git a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue index 3391ff17c54..ea81f75f7cc 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue @@ -1,17 +1,21 @@ + From 59e2991d2583e535564c3c559536e4b94836e060 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Fri, 19 Jul 2024 18:30:59 +0200 Subject: [PATCH 04/25] Add longhorn v2 check Signed-off-by: Francesco Torchia --- .../edit/harvesterhci.io.host/HarvesterDisk.vue | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue index ea81f75f7cc..2fc1d9df033 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue @@ -1,4 +1,5 @@ + + + + diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.vue deleted file mode 100644 index e9c7c1703df..00000000000 --- a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.vue +++ /dev/null @@ -1,32 +0,0 @@ - - diff --git a/pkg/harvester/l10n/en-us.yaml b/pkg/harvester/l10n/en-us.yaml index 2be63ec4497..298d8b87af4 100644 --- a/pkg/harvester/l10n/en-us.yaml +++ b/pkg/harvester/l10n/en-us.yaml @@ -1162,6 +1162,10 @@ harvester: no-options: No available tags, please add in the `Host > Storage` page migratable: label: Migratable + lvmVolumeGroupType: + label: Volume Group Type + lvmVolumeGroup: + label: Volume Group Name allowedTopologies: title: Allowed Topologies tooltip: Allowed Topologies helps scheduling virtual machines on hosts which match all of below expressions. From 0372c915664f4412aa4c49b9e94e2ef73c6b2343 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Tue, 17 Sep 2024 19:51:10 +0200 Subject: [PATCH 12/25] Add lvm node selector Signed-off-by: Francesco Torchia --- .../HarvesterHostDisk.vue | 2 +- .../harvesterhci.io.host/HarvesterDisk.vue | 4 +- .../edit/harvesterhci.io.storage/index.vue | 31 +++++++-- .../lvm.driver.harvesterhci.io.vue | 68 +++++++++++++++---- pkg/harvester/l10n/en-us.yaml | 2 + 5 files changed, 85 insertions(+), 22 deletions(-) diff --git a/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue b/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue index 4fed063a261..0a441a6045f 100644 --- a/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue +++ b/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue @@ -87,7 +87,7 @@ export default { } }, - provisioner() { + provisioner() { let labelKey = `harvester.storage.storageClass.longhorn.${ LONGHORN_VERSION_V1 }.label`; if (this.value?.blockDevice?.spec?.provisioner.longhorn) { diff --git a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue index f388641a0be..ac66a65b559 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue @@ -242,7 +242,7 @@ export default { :label="t('harvester.host.disk.fileSystem.formatting')" /> @@ -333,6 +333,7 @@ export default { :localized-label="true" :searchable="true" :options="provisioners" + :disabled="isProvisioned" @keydown.native.enter.prevent="()=>{}" /> @@ -367,6 +368,7 @@ export default { :searchable="true" :options="lvmVolumeGroups" :required="true" + :disabled="isProvisioned" @keydown.native.enter.prevent="()=>{}" /> diff --git a/pkg/harvester/edit/harvesterhci.io.storage/index.vue b/pkg/harvester/edit/harvesterhci.io.storage/index.vue index 01addcdd31f..e3629fe30ff 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/index.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/index.vue @@ -21,7 +21,9 @@ import { clone } from '@shell/utils/object'; import { LONGHORN_DRIVER, LONGHORN_VERSION_V1, LONGHORN_VERSION_V2 } from '@shell/models/persistentvolume'; import { LVM_DRIVER } from '@shell/models/storage.k8s.io.storageclass'; -const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine'; +const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine'; + +export const LVM_TOPOLOGY_LABEL = 'topology.lvm.csi/node'; export default { name: 'HarvesterStorage', @@ -72,7 +74,7 @@ export default { } ]; - const allowedTopologies = clone(this.value.allowedTopologies?.[0]?.matchLabelExpressions || []); + const allowedTopologies = clone(this.value.allowedTopologies?.[0]?.matchLabelExpressions || []).filter(t => t.key !== LVM_TOPOLOGY_LABEL); this.$set(this.value, 'parameters', this.value.parameters || {}); this.$set(this.value, 'provisioner', this.value.provisioner || LONGHORN_DRIVER); @@ -155,6 +157,10 @@ export default { isLonghornV2() { return this.value.provisioner === LONGHORN_DRIVER && this.longhornVersion === LONGHORN_VERSION_V2; }, + + isLvm() { + return this.value.provisioner === LVM_DRIVER; + }, }, watch: { @@ -165,6 +171,16 @@ export default { parameters.migratable = false; } + if (!this.isLvm) { + const matchLabelExpressions = (this.value.allowedTopologies?.[0]?.matchLabelExpressions || []).filter(t => t.key !== LVM_TOPOLOGY_LABEL); + + if (matchLabelExpressions.length > 0) { + this.$set(this.value, 'allowedTopologies', [{ matchLabelExpressions }]); + } else { + delete this.value.allowedTopologies; + } + } + this.$set(this.value, 'parameters', parameters); } }, @@ -198,10 +214,15 @@ export default { }, formatAllowedTopoloties() { - const neu = this.allowedTopologies; + const neu = this.allowedTopologies.filter(t => t.key !== LVM_TOPOLOGY_LABEL); + const lvmMatchExpression = (this.value.allowedTopologies?.[0]?.matchLabelExpressions || []).filter(t => t.key === LVM_TOPOLOGY_LABEL); if (!neu || neu.length === 0) { - delete this.value.allowedTopologies; + if (lvmMatchExpression.length > 0) { + this.value.allowedTopologies = [{ matchLabelExpressions: lvmMatchExpression }]; + } else { + delete this.value.allowedTopologies; + } return; } @@ -209,7 +230,7 @@ export default { const matchLabelExpressions = neu.filter(R => !!R.key.trim() && (R.values.length > 0 && !R.values.find(V => !V.trim()))); if (matchLabelExpressions.length > 0) { - this.value.allowedTopologies = [{ matchLabelExpressions }]; + this.value.allowedTopologies = [{ matchLabelExpressions: [...matchLabelExpressions, ...lvmMatchExpression] }]; } } } diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue index 85c8dd42d27..f20a2a23497 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue @@ -2,28 +2,29 @@ import KeyValue from '@shell/components/form/KeyValue'; import LabeledSelect from '@shell/components/form/LabeledSelect'; -import { LabeledInput } from '@components/Form/LabeledInput'; -import RadioGroup from '@components/Form/Radio/RadioGroup'; import { allHash } from '@shell/utils/promise'; -import { _CREATE, _VIEW } from '@shell/config/query-params'; -import { LONGHORN } from '@shell/config/types'; import { clone } from '@shell/utils/object'; -import { uniq } from '@shell/utils/array'; -import { LONGHORN_VERSION_V1, LONGHORN_VERSION_V2 } from '@shell/models/persistentvolume'; import { HCI } from '../../../types'; +import { NODE } from '@shell/config/types'; +import { LVM_TOPOLOGY_LABEL } from '../index.vue'; const DEFAULT_PARAMETERS = [ 'type', 'vgName' ]; +const DEFAUL_TOPOLOGIES = [{ + matchLabelExpressions: [{ + key: LVM_TOPOLOGY_LABEL, + values: [] + }] +}]; + export default { components: { KeyValue, LabeledSelect, - LabeledInput, - RadioGroup, }, props: { @@ -45,28 +46,49 @@ export default { const inStore = this.$store.getters['currentProduct'].inStore; await allHash({ + nodes: this.$store.dispatch(`${ inStore }/findAll`, { type: NODE }), lvmVolumeGroups: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.LVM_VOLUME_GROUP }), }); }, data() { + const node = (this.value.allowedTopologies?.[0]?.matchLabelExpressions || []).find(t => t.key === LVM_TOPOLOGY_LABEL)?.values[0]; + return { volumeGroupTypes: ['striped', 'dm-thin'], - volumeGroupType: null, - volumeGroup: null, - nodeName: 'harvester-node-0' + node, }; }, + watch: { + node(value) { + delete (this.value.parameters.vgName); + + const allowedTopologies = [...DEFAUL_TOPOLOGIES]; + + allowedTopologies[0].matchLabelExpressions[0].values = [value]; + + this.value.allowedTopologies = allowedTopologies; + } + }, + computed: { + nodes() { + const inStore = this.$store.getters['currentProduct'].inStore; + const nodes = this.$store.getters[`${ inStore }/all`](NODE) || []; + + return nodes.map(n => n.name); + }, + volumeGroups() { const inStore = this.$store.getters['currentProduct'].inStore; const lvmVolumeGroups = this.$store.getters[`${ inStore }/all`](HCI.LVM_VOLUME_GROUP) || []; return lvmVolumeGroups - .filter(group => group.spec.nodeName === this.nodeName) + .filter(group => group.spec.nodeName === this.node) .map(g => g.spec.vgName); }, + parameters: { get() { const parameters = clone(this.value?.parameters) || {}; @@ -90,7 +112,24 @@ export default {
+ + +
+
+
+
+
- diff --git a/pkg/harvester/l10n/en-us.yaml b/pkg/harvester/l10n/en-us.yaml index 298d8b87af4..27eafc3d49b 100644 --- a/pkg/harvester/l10n/en-us.yaml +++ b/pkg/harvester/l10n/en-us.yaml @@ -1166,6 +1166,8 @@ harvester: label: Volume Group Type lvmVolumeGroup: label: Volume Group Name + node: + label: Node allowedTopologies: title: Allowed Topologies tooltip: Allowed Topologies helps scheduling virtual machines on hosts which match all of below expressions. From 87ad61ea8735506cd19afc1d0c8daea15c47c09b Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Wed, 18 Sep 2024 15:25:09 +0200 Subject: [PATCH 13/25] Remove unsupported LVM storages from Image creation page. Signed-off-by: Francesco Torchia --- .../harvesterhci.io.virtualmachineimage.vue | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pkg/harvester/edit/harvesterhci.io.virtualmachineimage.vue b/pkg/harvester/edit/harvesterhci.io.virtualmachineimage.vue index f44cae4d9ca..e590c9ec943 100644 --- a/pkg/harvester/edit/harvesterhci.io.virtualmachineimage.vue +++ b/pkg/harvester/edit/harvesterhci.io.virtualmachineimage.vue @@ -16,6 +16,7 @@ import { exceptionToErrorsArray } from '@shell/utils/error'; import { allHash } from '@shell/utils/promise'; import { STORAGE_CLASS } from '@shell/config/types'; import { HCI } from '../types'; +import { LVM_DRIVER } from '@shell/models/storage.k8s.io.storageclass'; const ENCRYPT = 'encrypt'; const DECRYPT = 'decrypt'; @@ -132,16 +133,16 @@ export default { const inStore = this.$store.getters['currentProduct'].inStore; const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS); - const out = storages.filter(s => !s.parameters?.backingImage).map((s) => { - const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name; + return storages + .filter(s => !s.parameters?.backingImage && s.provisioner !== LVM_DRIVER) // Lvm storage is not supported. + .map((s) => { + const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name; - return { - label, - value: s.name, - }; - }) || []; - - return out; + return { + label, + value: s.name, + }; + }) || []; }, storageClassName: { From 9ff98f50f23816daf6f1485bd25e53261f31bff3 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Sun, 22 Sep 2024 16:11:04 +0200 Subject: [PATCH 14/25] Various enhancements - Add lvm driver addon - Add longhorn v2 engine in provisioner dropdowns - Save storage class parameters for longhorn - Decouple Storage class longhron params - Add create new option for LVM groups Signed-off-by: Francesco Torchia --- pkg/harvester/config/harvester-map.js | 3 +- pkg/harvester/config/labels-annotations.js | 1 + .../HarvesterHostDisk.vue | 6 +- .../harvesterhci.io.host/HarvesterDisk.vue | 169 ++++++++++-- .../edit/harvesterhci.io.host/index.vue | 29 +- .../edit/harvesterhci.io.storage/index.vue | 110 ++++---- ...ghorn.io.vue => driver.longhorn.io_v1.vue} | 15 +- .../provisioners/driver.longhorn.io_v2.vue | 253 ++++++++++++++++++ .../lvm.driver.harvesterhci.io.vue | 12 +- .../harvesterhci.io.virtualmachineimage.vue | 2 +- pkg/harvester/l10n/en-us.yaml | 22 +- .../harvester/storage.k8s.io.storageclass.js | 26 ++ shell/models/storage.k8s.io.storageclass.js | 2 - 13 files changed, 540 insertions(+), 110 deletions(-) rename pkg/harvester/edit/harvesterhci.io.storage/provisioners/{driver.longhorn.io.vue => driver.longhorn.io_v1.vue} (90%) create mode 100644 pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue diff --git a/pkg/harvester/config/harvester-map.js b/pkg/harvester/config/harvester-map.js index e1c4d321b39..808ef0b2f33 100644 --- a/pkg/harvester/config/harvester-map.js +++ b/pkg/harvester/config/harvester-map.js @@ -67,7 +67,8 @@ export const ADD_ONS = { NVIDIA_DRIVER_TOOLKIT_CONTROLLER: 'nvidia-driver-toolkit', RANCHER_LOGGING: 'rancher-logging', RANCHER_MONITORING: 'rancher-monitoring', - VM_IMPORT_CONTROLLER: 'vm-import-controller' + VM_IMPORT_CONTROLLER: 'vm-import-controller', + LVM_DRIVER: 'lvm.driver.harvesterhci.io' }; export const CSI_SECRETS = { diff --git a/pkg/harvester/config/labels-annotations.js b/pkg/harvester/config/labels-annotations.js index 64a27c5316e..65472ddcf1d 100644 --- a/pkg/harvester/config/labels-annotations.js +++ b/pkg/harvester/config/labels-annotations.js @@ -54,4 +54,5 @@ export const HCI = { CPU_MANAGER: 'cpumanager', VM_DEVICE_ALLOCATION_DETAILS: 'harvesterhci.io/deviceAllocationDetails', SVM_BACKUP_ID: 'harvesterhci.io/svmbackupId', + DISABLE_LONGHORN_V2_ENGINE: 'node.longhorn.io/disable-v2-data-engine' }; diff --git a/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue b/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue index 0a441a6045f..c715b441493 100644 --- a/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue +++ b/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue @@ -88,14 +88,14 @@ export default { }, provisioner() { - let labelKey = `harvester.storage.storageClass.longhorn.${ LONGHORN_VERSION_V1 }.label`; + let labelKey = `harvester.host.disk.storage.longhorn.${ LONGHORN_VERSION_V1 }.label`; if (this.value?.blockDevice?.spec?.provisioner.longhorn) { - labelKey = `harvester.storage.storageClass.longhorn.${ this.value.blockDevice.spec.provisioner.engineVersion }.label`; + labelKey = `harvester.host.disk.storage.longhorn.${ this.value.blockDevice.spec.provisioner.longhorn.engineVersion }.label`; } if (this.value?.blockDevice?.spec?.provisioner.lvm) { - labelKey = 'harvester.storage.storageClass.lvm.label'; + labelKey = 'harvester.host.disk.storage.lvm.label'; } return this.t(labelKey); diff --git a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue index ac66a65b559..51d7510fc74 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue @@ -10,11 +10,16 @@ import { RadioGroup, RadioButton } from '@components/Form/Radio'; import HarvesterDisk from '../../mixins/harvester-disk'; import Tags from '../../components/DiskTags'; import { HCI } from '../../types'; +import { HCI as HCI_LABELS_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations'; import { LONGHORN_SYSTEM } from './index'; import { LONGHORN_DRIVER, LONGHORN_VERSION_V1, LONGHORN_VERSION_V2 } from '@shell/models/persistentvolume'; -import { LVM_DRIVER } from '@shell/models/storage.k8s.io.storageclass'; +import { LVM_DRIVER } from '../../models/harvester/storage.k8s.io.storageclass'; +import ModalWithCard from '@shell/components/ModalWithCard'; +import { randomStr } from '@shell/utils/string'; +import { LONGHORN_V2_DATA_ENGINE } from './index.vue'; +import { _EDIT } from '@shell/config/query-params'; -const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine'; +const _NEW = '_NEW'; export default { components: { @@ -25,6 +30,7 @@ export default { Banner, RadioGroup, RadioButton, + ModalWithCard, Tags, }, @@ -59,30 +65,72 @@ export default { const inStore = this.$store.getters['currentProduct'].inStore; await allHash({ - csiDrivers: this.$store.dispatch(`${ inStore }/findAll`, { type: CSI_DRIVER }), - longhornV2DataEngine: this.$store.dispatch(`${ inStore }/find`, { type: LONGHORN.SETTINGS, id: LONGHORN_V2_DATA_ENGINE }), - lvmVolumeGroups: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.LVM_VOLUME_GROUP }), + csiDrivers: this.$store.dispatch(`${ inStore }/findAll`, { type: CSI_DRIVER }), + lvmVolumeGroups: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.LVM_VOLUME_GROUP }), }); }, + data() { + let provisioner = `${ this.value.provisioner || LONGHORN_DRIVER }`; + + if (provisioner === LONGHORN_DRIVER) { + provisioner = `${ provisioner }_${ this.value.provisionerVersion || LONGHORN_VERSION_V1 }`; + } + + return { + provisioner, + volumeGroupDialog: null, + randomStr: randomStr(10).toLowerCase(), + }; + }, + computed: { provisioners() { + const out = []; + const inStore = this.$store.getters['currentProduct'].inStore; const csiDrivers = this.$store.getters[`${ inStore }/all`](CSI_DRIVER) || []; - return csiDrivers.map((provisioner) => { - return { - label: this.provisionersLabelKeys[provisioner.name], - value: provisioner.name, - }; + csiDrivers.forEach(({ name }) => { + switch (name) { + case LONGHORN_DRIVER: + out.push({ + label: `harvester.host.disk.storage.longhorn.${ LONGHORN_VERSION_V1 }.label`, + value: `${ name }_${ LONGHORN_VERSION_V1 }`, + }); + + if (this.longhornSystemVersion === LONGHORN_VERSION_V2 || this.value.provisionerVersion === LONGHORN_VERSION_V2) { + out.push({ + label: `harvester.host.disk.storage.longhorn.${ LONGHORN_VERSION_V2 }.label`, + value: `${ name }_${ LONGHORN_VERSION_V2 }`, + disabled: this.forceLonghornV1 + }); + } + break; + case LVM_DRIVER: + out.push({ + label: 'harvester.host.disk.storage.lvm.label', + value: name, + }); + break; + } }); + + return out; }, lvmVolumeGroups() { const inStore = this.$store.getters['currentProduct'].inStore; const lvmVolumeGroups = this.$store.getters[`${ inStore }/all`](HCI.LVM_VOLUME_GROUP) || []; - return lvmVolumeGroups.filter(group => group.spec.nodeName === this.node.name).map(g => g.spec.vgName); + const out = lvmVolumeGroups.filter(group => group.spec.nodeName === this.node.name).map(g => g.spec.vgName); + + out.unshift({ + label: this.t('harvester.host.disk.lvmVolumeGroup.create'), + value: _NEW, + }); + + return out; }, targetDisk() { @@ -130,7 +178,7 @@ export default { }, isProvisioned() { - return this.blockDevice?.spec.fileSystem.provisioned; + return this.blockDevice?.spec?.fileSystem?.provisioned; }, forceFormattedDisabled() { @@ -197,30 +245,82 @@ export default { return this.blockDevice.isFormatting; }, - longhornVersion() { + longhornSystemVersion() { const inStore = this.$store.getters['currentProduct'].inStore; const v2DataEngine = this.$store.getters[`${ inStore }/byId`](LONGHORN.SETTINGS, LONGHORN_V2_DATA_ENGINE) || {}; return v2DataEngine.value === 'true' ? LONGHORN_VERSION_V2 : LONGHORN_VERSION_V1; }, + forceLonghornV1() { + return this.node?.labels[HCI_LABELS_ANNOTATIONS.DISABLE_LONGHORN_V2_ENGINE] === 'true'; + }, + isLvm() { return this.value.provisioner === LVM_DRIVER; }, + isLonghorn() { + return this.value.provisioner === LONGHORN_DRIVER; + }, + isLonghornV1() { - return this.value.provisioner === LONGHORN_DRIVER && this.longhornVersion === LONGHORN_VERSION_V1; + return this.isLonghorn && (this.longhornSystemVersion === LONGHORN_VERSION_V1 || this.forceLonghornV1); }, - provisionersLabelKeys() { - return { - [LONGHORN_DRIVER]: `harvester.storage.storageClass.longhorn.${ this.longhornVersion }.label`, - [LVM_DRIVER]: 'harvester.storage.storageClass.lvm.label' - }; + provisionerTooltip() { + if ( + this.mode === _EDIT && + this.isLonghorn && + this.longhornSystemVersion === LONGHORN_VERSION_V2 && + this.forceLonghornV1 + ) { + return this.t('harvester.storage.storageClass.longhorn.versionTooltip'); + } + + return null; + } + }, + + watch: { + provisioner(value) { + this.randomStr = randomStr(10).toLowerCase(); + + const [provisioner, provisionerVersion] = value?.split('_'); + + this.value.provisioner = provisioner; + + if (provisioner === LONGHORN_DRIVER) { + this.value.provisionerVersion = provisionerVersion || LONGHORN_VERSION_V1; + } else { + delete this.value.provisionerVersion; + } }, + + 'value.lvmVolumeGroup'(neu) { + if (neu === _NEW) { + this.value.lvmVolumeGroup = null; + this.showCreateVolumeGroup(); + } + } }, methods: { + showCreateVolumeGroup() { + this.volumeGroupDialog = null; + this.$modal.show(this.randomStr); + }, + + hideCreateVolumeGroup() { + this.$modal.hide(this.randomStr); + }, + + saveCreateVolumeGroup(buttonCb) { + buttonCb(true); + this.value.lvmVolumeGroup = this.volumeGroupDialog; + this.hideCreateVolumeGroup(); + }, + update() { this.$emit('input', this.value); }, @@ -327,13 +427,14 @@ export default {
@@ -363,16 +464,38 @@ export default {
+ + + + +
diff --git a/pkg/harvester/edit/harvesterhci.io.host/index.vue b/pkg/harvester/edit/harvesterhci.io.host/index.vue index 951b8395abf..50c79d8973b 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/index.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/index.vue @@ -29,12 +29,14 @@ import HarvesterDisk from './HarvesterDisk'; import HarvesterKsmtuned from './HarvesterKsmtuned'; import HarvesterSeeder from './HarvesterSeeder'; import Tags from '../../components/DiskTags'; -import { LONGHORN_DRIVER, LONGHORN_VERSION_V1 } from '@shell/models/persistentvolume'; -import { LVM_DRIVER } from '@shell/models/storage.k8s.io.storageclass'; +import { LONGHORN_DRIVER, LONGHORN_VERSION_V1, LONGHORN_VERSION_V2 } from '@shell/models/persistentvolume'; +import { LVM_DRIVER } from '../../models/harvester/storage.k8s.io.storageclass'; import isEqual from 'lodash/isEqual'; export const LONGHORN_SYSTEM = 'longhorn-system'; +export const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine'; + export default { name: 'HarvesterEditNode', components: { @@ -65,10 +67,11 @@ export default { const inStore = this.$store.getters['currentProduct'].inStore; const hash = { - longhornNodes: this.$store.dispatch(`${ inStore }/findAll`, { type: LONGHORN.NODES }), - blockDevices: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.BLOCK_DEVICE }), - addons: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.ADD_ONS }), - secrets: this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET }), + longhornNodes: this.$store.dispatch(`${ inStore }/findAll`, { type: LONGHORN.NODES }), + blockDevices: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.BLOCK_DEVICE }), + addons: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.ADD_ONS }), + secrets: this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET }), + longhornV2DataEngine: this.$store.dispatch(`${ inStore }/find`, { type: LONGHORN.SETTINGS, id: LONGHORN_V2_DATA_ENGINE }), }; if (this.$store.getters[`${ inStore }/schemaFor`](HCI.INVENTORY)) { @@ -97,7 +100,7 @@ export default { displayName: d?.displayName, forceFormatted: corrupted ? true : d?.spec?.fileSystem?.forceFormatted || false, provisioner: d?.spec?.provisioner?.lvm ? LVM_DRIVER : LONGHORN_DRIVER, - provisionerVersion: d?.spec?.provisioner?.longhorn?.engineVersion || LONGHORN_VERSION_V1, // todo get default from system version + provisionerVersion: d?.spec?.provisioner?.longhorn?.engineVersion || LONGHORN_VERSION_V1, lvmVolumeGroup: d?.spec?.provisioner?.lvm?.vgName, }; }); @@ -162,6 +165,13 @@ export default { return out; }, + longhornSystemVersion() { + const inStore = this.$store.getters['currentProduct'].inStore; + const v2DataEngine = this.$store.getters[`${ inStore }/byId`](LONGHORN.SETTINGS, LONGHORN_V2_DATA_ENGINE) || {}; + + return v2DataEngine.value === 'true' ? LONGHORN_VERSION_V2 : LONGHORN_VERSION_V1; + }, + longhornDisks() { const inStore = this.$store.getters['currentProduct'].inStore; const longhornNode = this.$store.getters[`${ inStore }/byId`](LONGHORN.NODES, `${ LONGHORN_SYSTEM }/${ this.value.id }`); @@ -193,7 +203,7 @@ export default { forceFormatted: blockDevice?.spec?.fileSystem?.forceFormatted || false, tags: diskSpec?.[key]?.tags || [], provisioner: blockDevice?.spec?.provisioner?.lvm ? LVM_DRIVER : LONGHORN_DRIVER, - provisionerVersion: blockDevice?.spec?.provisioner?.longhorn?.engineVersion || LONGHORN_VERSION_V1, // todo get default from system version + provisionerVersion: blockDevice?.spec?.provisioner?.longhorn?.engineVersion || LONGHORN_VERSION_V1, lvmVolumeGroup: blockDevice?.spec?.provisioner?.lvm?.vgName, }; }); @@ -331,7 +341,7 @@ export default { displayName: disk?.displayName, forceFormatted, provisioner: LONGHORN_DRIVER, - provisionerVersion: LONGHORN_VERSION_V1, // todo get default from system version + provisionerVersion: LONGHORN_VERSION_V1, lvmVolumeGroup: null, }); }, @@ -370,6 +380,7 @@ export default { break; case LVM_DRIVER: blockDevice.spec.provisioner = { lvm: { vgName: d.lvmVolumeGroup } }; + blockDevice.spec.provision = true; break; } diff --git a/pkg/harvester/edit/harvesterhci.io.storage/index.vue b/pkg/harvester/edit/harvesterhci.io.storage/index.vue index e3629fe30ff..bfbdb7a5b87 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/index.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/index.vue @@ -18,10 +18,13 @@ import { STORAGE_CLASS, LONGHORN } from '@shell/config/types'; import { CSI_DRIVER } from '../../types'; import { allHash } from '@shell/utils/promise'; import { clone } from '@shell/utils/object'; -import { LONGHORN_DRIVER, LONGHORN_VERSION_V1, LONGHORN_VERSION_V2 } from '@shell/models/persistentvolume'; -import { LVM_DRIVER } from '@shell/models/storage.k8s.io.storageclass'; +import { LONGHORN_DRIVER } from '@shell/models/persistentvolume'; +import { LVM_DRIVER } from '../../models/harvester/storage.k8s.io.storageclass'; -const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine'; +const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine'; + +export const ENGINE_VERSION_V1 = 'v1'; +export const ENGINE_VERSION_V2 = 'v2'; export const LVM_TOPOLOGY_LABEL = 'topology.lvm.csi/node'; @@ -78,17 +81,28 @@ export default { this.$set(this.value, 'parameters', this.value.parameters || {}); this.$set(this.value, 'provisioner', this.value.provisioner || LONGHORN_DRIVER); + + if (this.value.provisioner === LONGHORN_DRIVER) { + this.$set(this.value.parameters, 'engineVersion', this.value.longhornVersion); + } + this.$set(this.value, 'allowVolumeExpansion', this.value.allowVolumeExpansion || allowVolumeExpansionOptions[0].value); this.$set(this.value, 'reclaimPolicy', this.value.reclaimPolicy || reclaimPolicyOptions[0].value); this.$set(this.value, 'volumeBindingMode', this.value.volumeBindingMode || volumeBindingModeOptions[0].value); + let provisioner = `${ this.value.provisioner || LONGHORN_DRIVER }`; + + if (provisioner === LONGHORN_DRIVER) { + provisioner = `${ provisioner }_${ this.value.longhornVersion }`; + } + return { reclaimPolicyOptions, allowVolumeExpansionOptions, volumeBindingModeOptions, mountOptions: [], - provisioner: LONGHORN_DRIVER, STORAGE_CLASS, + provisioner, allowedTopologies, defaultAddValue: { key: '', @@ -119,19 +133,37 @@ export default { return this.isCreate ? _CREATE : _VIEW; }, - provisionerWatch() { - return this.value.provisioner; - }, - provisioners() { - const csiDrivers = this.$store.getters[`${ this.inStore }/all`](CSI_DRIVER) || []; + const out = []; - return csiDrivers.map((provisioner) => { - return { - label: this.provisionersLabelKeys[provisioner.name], - value: provisioner.name, - }; + const inStore = this.$store.getters['currentProduct'].inStore; + const csiDrivers = this.$store.getters[`${ inStore }/all`](CSI_DRIVER) || []; + + csiDrivers.forEach(({ name }) => { + switch (name) { + case LONGHORN_DRIVER: + out.push({ + label: `harvester.storage.storageClass.longhorn.${ ENGINE_VERSION_V1 }.label`, + value: `${ name }_${ ENGINE_VERSION_V1 }`, + }); + + if (this.longhornSystemVersion === ENGINE_VERSION_V2 || this.value.longhornVersion === ENGINE_VERSION_V2) { + out.push({ + label: `harvester.storage.storageClass.longhorn.${ ENGINE_VERSION_V2 }.label`, + value: `${ name }_${ ENGINE_VERSION_V2 }`, + }); + } + break; + case LVM_DRIVER: + out.push({ + label: 'harvester.storage.storageClass.lvm.label', + value: name, + }); + break; + } }); + + return out; }, schema() { @@ -140,38 +172,21 @@ export default { return this.$store.getters[`${ inStore }/schemaFor`](STORAGE_CLASS); }, - provisionersLabelKeys() { - return { - [LONGHORN_DRIVER]: `harvester.storage.storageClass.longhorn.${ this.longhornVersion }.label`, - [LVM_DRIVER]: 'harvester.storage.storageClass.lvm.label' - }; - }, - - longhornVersion() { + longhornSystemVersion() { const inStore = this.$store.getters['currentProduct'].inStore; const v2DataEngine = this.$store.getters[`${ inStore }/byId`](LONGHORN.SETTINGS, LONGHORN_V2_DATA_ENGINE) || {}; - return v2DataEngine.value === 'true' ? LONGHORN_VERSION_V2 : LONGHORN_VERSION_V1; - }, - - isLonghornV2() { - return this.value.provisioner === LONGHORN_DRIVER && this.longhornVersion === LONGHORN_VERSION_V2; - }, - - isLvm() { - return this.value.provisioner === LVM_DRIVER; + return v2DataEngine.value === 'true' ? ENGINE_VERSION_V2 : ENGINE_VERSION_V1; }, }, watch: { - provisionerWatch() { - const parameters = {}; + provisioner(neu) { + const [provisioner, engineVersion] = neu?.split('_'); - if (this.isLonghornV2) { - parameters.migratable = false; - } + let parameters = {}; - if (!this.isLvm) { + if (provisioner === LVM_DRIVER) { const matchLabelExpressions = (this.value.allowedTopologies?.[0]?.matchLabelExpressions || []).filter(t => t.key !== LVM_TOPOLOGY_LABEL); if (matchLabelExpressions.length > 0) { @@ -181,11 +196,18 @@ export default { } } + this.$set(this.value, 'provisioner', provisioner); + + if (provisioner === LONGHORN_DRIVER) { + parameters = { engineVersion }; + } + + this.$set(this.value, 'allowVolumeExpansion', this.value.provisioner === LONGHORN_DRIVER); this.$set(this.value, 'parameters', parameters); } }, - created() { + created(neu) { this.registerBeforeHook(this.willSave, 'willSave'); }, @@ -198,11 +220,6 @@ export default { } }, - updateProvisioner(provisioner) { - this.$set(this.value, 'provisioner', provisioner); - this.$set(this.value, 'allowVolumeExpansion', provisioner === LONGHORN_DRIVER); - }, - willSave() { Object.keys(this.value.parameters).forEach((key) => { if (this.value.parameters[key] === null || this.value.parameters[key] === '') { @@ -259,7 +276,7 @@ export default { :register-before-hook="registerBeforeHook" /> secret.isSystem === false); }, data() { - const inStore = this.$store.getters['currentProduct'].inStore; - const v2DataEngine = this.$store.getters[`${ inStore }/byId`](LONGHORN.SETTINGS, LONGHORN_V2_DATA_ENGINE) || {}; - - const longhornVersion = v2DataEngine.value === 'true' ? LONGHORN_VERSION_V2 : LONGHORN_VERSION_V1; - if (this.realMode === _CREATE) { this.$set(this.value, 'parameters', { numberOfReplicas: '3', staleReplicaTimeout: '30', diskSelector: null, nodeSelector: null, - encrypted: 'false', migratable: 'true', + engineVersion: ENGINE_VERSION_V1 }); } - return { secrets: [] }; + return {}; }, computed: { longhornNodes() { @@ -333,6 +327,7 @@ export default { :label="t('harvester.storage.secret')" :options="secretOptions" :mode="mode" + :options="migratableOptions" /> diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue new file mode 100644 index 00000000000..6b0f991c6fc --- /dev/null +++ b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue @@ -0,0 +1,253 @@ + + + + diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue index f20a2a23497..2f00234062a 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue @@ -77,7 +77,7 @@ export default { const inStore = this.$store.getters['currentProduct'].inStore; const nodes = this.$store.getters[`${ inStore }/all`](NODE) || []; - return nodes.map(n => n.name); + return nodes.filter(n => n.labels[LVM_TOPOLOGY_LABEL] === n.name).map(n => n.name); }, volumeGroups() { @@ -137,7 +137,7 @@ export default { > @@ -149,13 +149,7 @@ export default { :options="volumeGroupTypes" :mode="mode" :required="true" - > - - + /> Storage` page node: label: Node allowedTopologies: diff --git a/pkg/harvester/models/harvester/storage.k8s.io.storageclass.js b/pkg/harvester/models/harvester/storage.k8s.io.storageclass.js index 80dffc49df6..f84676850a5 100644 --- a/pkg/harvester/models/harvester/storage.k8s.io.storageclass.js +++ b/pkg/harvester/models/harvester/storage.k8s.io.storageclass.js @@ -2,6 +2,10 @@ import { clone } from '@shell/utils/object'; import { HCI } from '../../types'; import StorageClass from '@shell/models/storage.k8s.io.storageclass'; import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../config/harvester'; +import { LONGHORN_DRIVER } from '@shell/models/persistentvolume'; +import { ENGINE_VERSION_V1 } from '../../edit/harvesterhci.io.storage/index.vue'; + +export const LVM_DRIVER = 'lvm.driver.harvesterhci.io'; export default class HciStorageClass extends StorageClass { get detailLocation() { @@ -31,4 +35,26 @@ export default class HciStorageClass extends StorageClass { get parentNameOverride() { return this.$rootGetters['i18n/t'](`typeLabel."${ HCI.STORAGE }"`, { count: 1 })?.trim(); } + + get longhornVersion() { + if (this.provisioner === LONGHORN_DRIVER) { + return (this.parameters || {}).engineVersion || ENGINE_VERSION_V1; + } + + return null; + } + + get provisionerDisplay() { + let key = ''; + + if (this.provisioner === LONGHORN_DRIVER) { + key = `harvester.storage.storageClass.longhorn.${ this.longhornVersion }.label`; + } + + if (this.provisioner === LVM_DRIVER) { + key = `harvester.storage.storageClass.lvm.label`; + } + + return this.$rootGetters['i18n/t'](key); + } } diff --git a/shell/models/storage.k8s.io.storageclass.js b/shell/models/storage.k8s.io.storageclass.js index df3e72696b0..7b38d2fc5a4 100644 --- a/shell/models/storage.k8s.io.storageclass.js +++ b/shell/models/storage.k8s.io.storageclass.js @@ -81,8 +81,6 @@ export const PROVISIONER_OPTIONS = [ } ]; -export const LVM_DRIVER = 'lvm.driver.harvesterhci.io'; - export default class extends SteveModel { get provisionerDisplay() { const option = PROVISIONER_OPTIONS.find(o => o.value === this.provisioner); From 4e6f9d8abbeda90d1ad504e15738662af573b8a4 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Mon, 23 Sep 2024 13:48:43 +0200 Subject: [PATCH 15/25] Handle spec.fileSystem.provisioned deprecation in favor of spec.provision in blockDevice CR Signed-off-by: Francesco Torchia --- .../detail/harvesterhci.io.host/index.vue | 3 +-- .../edit/harvesterhci.io.host/HarvesterDisk.vue | 2 +- pkg/harvester/edit/harvesterhci.io.host/index.vue | 14 ++++++-------- pkg/harvester/models/harvester/node.js | 2 +- .../models/harvesterhci.io.blockdevice.js | 7 ++++++- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/pkg/harvester/detail/harvesterhci.io.host/index.vue b/pkg/harvester/detail/harvesterhci.io.host/index.vue index 8bd8c34467c..2daa1497835 100644 --- a/pkg/harvester/detail/harvesterhci.io.host/index.vue +++ b/pkg/harvester/detail/harvesterhci.io.host/index.vue @@ -103,11 +103,10 @@ export default { const blockDevices = this.$store.getters[`${ inStore }/all`](HCI.BLOCK_DEVICE); const provisionedBlockDevices = blockDevices.filter((d) => { - const provisioned = d?.spec?.fileSystem?.provisioned; const isCurrentNode = d?.spec?.nodeName === this.value.id; const isLonghornMounted = findBy(this.longhornDisks, 'name', d.metadata.name); - return provisioned && isCurrentNode && !isLonghornMounted; + return d?.isProvisioned && isCurrentNode && !isLonghornMounted; }) .map((d) => { return { diff --git a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue index 51d7510fc74..abc4aec1114 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue @@ -178,7 +178,7 @@ export default { }, isProvisioned() { - return this.blockDevice?.spec?.fileSystem?.provisioned; + return this.blockDevice?.isProvisioned; }, forceFormattedDisabled() { diff --git a/pkg/harvester/edit/harvesterhci.io.host/index.vue b/pkg/harvester/edit/harvesterhci.io.host/index.vue index 50c79d8973b..e526ad08000 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/index.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/index.vue @@ -82,11 +82,10 @@ export default { const blockDevices = this.$store.getters[`${ inStore }/all`](HCI.BLOCK_DEVICE); const provisionedBlockDevices = blockDevices.filter((d) => { - const provisioned = d?.spec?.fileSystem?.provisioned; const isCurrentNode = d?.spec?.nodeName === this.value.id; const isLonghornMounted = findBy(this.longhornDisks, 'name', d.metadata.name); - return provisioned && isCurrentNode && !isLonghornMounted; + return d?.isProvisioned && isCurrentNode && !isLonghornMounted; }) .map((d) => { const corrupted = d?.status?.deviceStatus?.fileSystem?.corrupted; @@ -356,10 +355,10 @@ export default { } else if (addDisks.length !== 0 && removeDisks.length === 0) { const updatedDisks = addDisks.filter((d) => { const blockDevice = this.$store.getters[`${ inStore }/byId`](HCI.BLOCK_DEVICE, `${ LONGHORN_SYSTEM }/${ d.name }`); - const { provisioned, forceFormatted } = blockDevice.spec.fileSystem; + const { forceFormatted } = blockDevice.spec.fileSystem; const { provisioner } = blockDevice.spec; - return !(provisioned && forceFormatted === d.forceFormatted && isEqual(provisioner, d.provisioner)); + return !(blockDevice.isProvisioned && forceFormatted === d.forceFormatted && isEqual(provisioner, d.provisioner)); }); if (updatedDisks.length === 0) { @@ -371,7 +370,7 @@ export default { await Promise.all(addDisks.map((d) => { const blockDevice = this.$store.getters[`${ inStore }/byId`](HCI.BLOCK_DEVICE, `${ LONGHORN_SYSTEM }/${ d.name }`); - blockDevice.spec.fileSystem.provisioned = true; + blockDevice.spec.provision = true; blockDevice.spec.fileSystem.forceFormatted = d.forceFormatted; switch (d.provisioner) { @@ -380,7 +379,6 @@ export default { break; case LVM_DRIVER: blockDevice.spec.provisioner = { lvm: { vgName: d.lvmVolumeGroup } }; - blockDevice.spec.provision = true; break; } @@ -390,7 +388,7 @@ export default { await Promise.all(removeDisks.map((d) => { const blockDevice = this.$store.getters[`${ inStore }/byId`](HCI.BLOCK_DEVICE, `${ LONGHORN_SYSTEM }/${ d.name }`); - blockDevice.spec.fileSystem.provisioned = false; + blockDevice.spec.provision = false; return blockDevice.save(); })); @@ -443,7 +441,7 @@ export default { if ((!findBy(this.disks || [], 'name', d.metadata.name) && d?.spec?.nodeName === this.value.id && (!addedToNodeCondition || addedToNodeCondition?.status === 'False') && - !d.spec?.fileSystem?.provisioned && + !d?.isProvisioned && !isAdded) || isRemoved ) { diff --git a/pkg/harvester/models/harvester/node.js b/pkg/harvester/models/harvester/node.js index 49f388c0d29..793d67a7d89 100644 --- a/pkg/harvester/models/harvester/node.js +++ b/pkg/harvester/models/harvester/node.js @@ -489,7 +489,7 @@ export default class HciNode extends HarvesterResource { get unProvisionedDisks() { const blockDevices = this.blockDevices || []; - return blockDevices.filter(d => d?.spec?.fileSystem?.provisioned && d?.status?.provisionPhase !== 'Provisioned'); + return blockDevices.filter(d => d?.isProvisioned && d?.status?.provisionPhase !== 'Provisioned'); } get diskStatusCount() { diff --git a/pkg/harvester/models/harvesterhci.io.blockdevice.js b/pkg/harvester/models/harvesterhci.io.blockdevice.js index 06078faf692..7e968781235 100644 --- a/pkg/harvester/models/harvesterhci.io.blockdevice.js +++ b/pkg/harvester/models/harvesterhci.io.blockdevice.js @@ -15,7 +15,7 @@ export default class HciBlockDevice extends HarvesterResource { } get isChildPartProvisioned() { - const parts = this.childParts.filter(p => p.spec?.fileSystem?.provisioned) || []; + const parts = this.childParts.filter(p => p.isProvisioned) || []; return parts.length > 0; } @@ -59,4 +59,9 @@ export default class HciBlockDevice extends HarvesterResource { return formatting.status === 'True'; } + + get isProvisioned() { + // spec.fileSystem.provisioned is deprecated + return this.spec?.fileSystem?.provisioned || this.spec?.provision; + } } From d23db5e446ef25be0d3b5aa690ec4cc8ccd9dec8 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Tue, 24 Sep 2024 10:28:31 +0200 Subject: [PATCH 16/25] Rebase driver.longhorn.io Signed-off-by: Francesco Torchia --- .../provisioners/driver.longhorn.io_v1.vue | 8 +- .../provisioners/driver.longhorn.io_v2.vue | 122 ++++++++++++++++-- 2 files changed, 115 insertions(+), 15 deletions(-) diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v1.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v1.vue index 984535f023d..5393307025c 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v1.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v1.vue @@ -10,12 +10,14 @@ import { clone } from '@shell/utils/object'; import { uniq } from '@shell/utils/array'; import { ENGINE_VERSION_V1 } from '../index.vue'; +// UI components for Longhorn storage class parameters const DEFAULT_PARAMETERS = [ 'numberOfReplicas', 'staleReplicaTimeout', 'diskSelector', 'nodeSelector', 'migratable', + 'encrypted', 'engineVersion', ]; @@ -29,7 +31,7 @@ const { } = CSI_SECRETS; export default { - name: 'DriverLonghornIO', + name: 'DriverLonghornIOV1', components: { KeyValue, @@ -70,12 +72,13 @@ export default { staleReplicaTimeout: '30', diskSelector: null, nodeSelector: null, + encrypted: 'false', migratable: 'true', engineVersion: ENGINE_VERSION_V1 }); } - return {}; + return { secrets: [] }; }, computed: { longhornNodes() { @@ -327,7 +330,6 @@ export default { :label="t('harvester.storage.secret')" :options="secretOptions" :mode="mode" - :options="migratableOptions" /> diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue index 6b0f991c6fc..fb0b21024a7 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue @@ -3,23 +3,36 @@ import KeyValue from '@shell/components/form/KeyValue'; import LabeledSelect from '@shell/components/form/LabeledSelect'; import { LabeledInput } from '@components/Form/LabeledInput'; import RadioGroup from '@components/Form/Radio/RadioGroup'; - +import { SECRET, NAMESPACE, LONGHORN } from '@shell/config/types'; import { _CREATE, _VIEW } from '@shell/config/query-params'; -import { LONGHORN } from '@shell/config/types'; +import { CSI_SECRETS } from '@pkg/harvester/config/harvester-map'; import { clone } from '@shell/utils/object'; import { uniq } from '@shell/utils/array'; import { ENGINE_VERSION_V2 } from '../index.vue'; +// UI components for Longhorn storage class parameters const DEFAULT_PARAMETERS = [ 'numberOfReplicas', 'staleReplicaTimeout', 'diskSelector', 'nodeSelector', 'migratable', + 'encrypted', 'engineVersion', ]; +const { + CSI_PROVISIONER_SECRET_NAME, + CSI_PROVISIONER_SECRET_NAMESPACE, + CSI_NODE_PUBLISH_SECRET_NAME, + CSI_NODE_PUBLISH_SECRET_NAMESPACE, + CSI_NODE_STAGE_SECRET_NAME, + CSI_NODE_STAGE_SECRET_NAMESPACE +} = CSI_SECRETS; + export default { + name: 'DriverLonghornIOV2', + components: { KeyValue, LabeledSelect, @@ -42,6 +55,16 @@ export default { }, }, + async fetch() { + const inStore = this.$store.getters['currentProduct'].inStore; + + await this.$store.dispatch(`${ inStore }/findAll`, { type: NAMESPACE }); + + const allSecrets = await this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET }); + + // only show non-system secret to user to select + this.secrets = allSecrets.filter(secret => secret.isSystem === false); + }, data() { if (this.realMode === _CREATE) { this.$set(this.value, 'parameters', { @@ -49,14 +72,14 @@ export default { staleReplicaTimeout: '30', diskSelector: null, nodeSelector: null, + encrypted: 'false', migratable: 'false', engineVersion: ENGINE_VERSION_V2 }); } - return {}; + return { secrets: [] }; }, - computed: { longhornNodes() { const inStore = this.$store.getters['currentProduct'].inStore; @@ -100,11 +123,29 @@ export default { }]; }, + secretOptions() { + return this.secrets.map(secret => secret.id); + }, + + volumeEncryptionOptions() { + return [{ + label: this.t('generic.yes'), + value: 'true' + }, { + label: this.t('generic.no'), + value: 'false' + }]; + }, + parameters: { get() { const parameters = clone(this.value?.parameters) || {}; - DEFAULT_PARAMETERS.map((key) => { + DEFAULT_PARAMETERS.forEach((key) => { + delete parameters[key]; + }); + + Object.values(CSI_SECRETS).forEach((key) => { delete parameters[key]; }); @@ -116,6 +157,46 @@ export default { } }, + volumeEncryption: { + set(neu) { + this.$set(this.value, 'parameters', { + ...this.value.parameters, + encrypted: neu + }); + }, + + get() { + return this.value?.parameters?.encrypted || 'false'; + } + }, + + secret: { + get() { + const selectedNs = this.value.parameters[CSI_PROVISIONER_SECRET_NAMESPACE]; + const selectedName = this.value.parameters[CSI_PROVISIONER_SECRET_NAME]; + + if (selectedNs && selectedName) { + return `${ selectedNs }/${ selectedName }`; + } + + return ''; + }, + + set(selectedSecret) { + const [namespace, name] = selectedSecret.split('/'); + + this.$set(this.value, 'parameters', { + ...this.value.parameters, + [CSI_PROVISIONER_SECRET_NAME]: name, + [CSI_NODE_PUBLISH_SECRET_NAME]: name, + [CSI_NODE_STAGE_SECRET_NAME]: name, + [CSI_PROVISIONER_SECRET_NAMESPACE]: namespace, + [CSI_NODE_PUBLISH_SECRET_NAMESPACE]: namespace, + [CSI_NODE_STAGE_SECRET_NAMESPACE]: namespace + }); + } + }, + nodeSelector: { get() { const nodeSelector = this.value?.parameters?.nodeSelector; @@ -224,15 +305,32 @@ export default { -
+
+ +
+
+ +
+
-
From d1d4fbfb820418fe6094392369127cce1dd8a102 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Tue, 24 Sep 2024 12:04:26 +0200 Subject: [PATCH 17/25] Rename longhorn version param to dataEngine Signed-off-by: Francesco Torchia --- .../edit/harvesterhci.io.storage/index.vue | 22 +++++++++---------- .../provisioners/driver.longhorn.io_v1.vue | 6 ++--- .../provisioners/driver.longhorn.io_v2.vue | 6 ++--- .../harvester/storage.k8s.io.storageclass.js | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pkg/harvester/edit/harvesterhci.io.storage/index.vue b/pkg/harvester/edit/harvesterhci.io.storage/index.vue index bfbdb7a5b87..879a46a059f 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/index.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/index.vue @@ -23,8 +23,8 @@ import { LVM_DRIVER } from '../../models/harvester/storage.k8s.io.storageclass'; const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine'; -export const ENGINE_VERSION_V1 = 'v1'; -export const ENGINE_VERSION_V2 = 'v2'; +export const DATA_ENGINE_V1 = 'v1'; +export const DATA_ENGINE_V2 = 'v2'; export const LVM_TOPOLOGY_LABEL = 'topology.lvm.csi/node'; @@ -83,7 +83,7 @@ export default { this.$set(this.value, 'provisioner', this.value.provisioner || LONGHORN_DRIVER); if (this.value.provisioner === LONGHORN_DRIVER) { - this.$set(this.value.parameters, 'engineVersion', this.value.longhornVersion); + this.$set(this.value.parameters, 'dataEngine', this.value.longhornVersion); } this.$set(this.value, 'allowVolumeExpansion', this.value.allowVolumeExpansion || allowVolumeExpansionOptions[0].value); @@ -143,14 +143,14 @@ export default { switch (name) { case LONGHORN_DRIVER: out.push({ - label: `harvester.storage.storageClass.longhorn.${ ENGINE_VERSION_V1 }.label`, - value: `${ name }_${ ENGINE_VERSION_V1 }`, + label: `harvester.storage.storageClass.longhorn.${ DATA_ENGINE_V1 }.label`, + value: `${ name }_${ DATA_ENGINE_V1 }`, }); - if (this.longhornSystemVersion === ENGINE_VERSION_V2 || this.value.longhornVersion === ENGINE_VERSION_V2) { + if (this.longhornSystemVersion === DATA_ENGINE_V2 || this.value.longhornVersion === DATA_ENGINE_V2) { out.push({ - label: `harvester.storage.storageClass.longhorn.${ ENGINE_VERSION_V2 }.label`, - value: `${ name }_${ ENGINE_VERSION_V2 }`, + label: `harvester.storage.storageClass.longhorn.${ DATA_ENGINE_V2 }.label`, + value: `${ name }_${ DATA_ENGINE_V2 }`, }); } break; @@ -176,13 +176,13 @@ export default { const inStore = this.$store.getters['currentProduct'].inStore; const v2DataEngine = this.$store.getters[`${ inStore }/byId`](LONGHORN.SETTINGS, LONGHORN_V2_DATA_ENGINE) || {}; - return v2DataEngine.value === 'true' ? ENGINE_VERSION_V2 : ENGINE_VERSION_V1; + return v2DataEngine.value === 'true' ? DATA_ENGINE_V2 : DATA_ENGINE_V1; }, }, watch: { provisioner(neu) { - const [provisioner, engineVersion] = neu?.split('_'); + const [provisioner, dataEngine] = neu?.split('_'); let parameters = {}; @@ -199,7 +199,7 @@ export default { this.$set(this.value, 'provisioner', provisioner); if (provisioner === LONGHORN_DRIVER) { - parameters = { engineVersion }; + parameters = { dataEngine }; } this.$set(this.value, 'allowVolumeExpansion', this.value.provisioner === LONGHORN_DRIVER); diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v1.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v1.vue index 5393307025c..70d8bc73307 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v1.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v1.vue @@ -8,7 +8,7 @@ import { _CREATE, _VIEW } from '@shell/config/query-params'; import { CSI_SECRETS } from '@pkg/harvester/config/harvester-map'; import { clone } from '@shell/utils/object'; import { uniq } from '@shell/utils/array'; -import { ENGINE_VERSION_V1 } from '../index.vue'; +import { DATA_ENGINE_V1 } from '../index.vue'; // UI components for Longhorn storage class parameters const DEFAULT_PARAMETERS = [ @@ -18,7 +18,7 @@ const DEFAULT_PARAMETERS = [ 'nodeSelector', 'migratable', 'encrypted', - 'engineVersion', + 'dataEngine', ]; const { @@ -74,7 +74,7 @@ export default { nodeSelector: null, encrypted: 'false', migratable: 'true', - engineVersion: ENGINE_VERSION_V1 + dataEngine: DATA_ENGINE_V1 }); } diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue index fb0b21024a7..9c528523a35 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/driver.longhorn.io_v2.vue @@ -8,7 +8,7 @@ import { _CREATE, _VIEW } from '@shell/config/query-params'; import { CSI_SECRETS } from '@pkg/harvester/config/harvester-map'; import { clone } from '@shell/utils/object'; import { uniq } from '@shell/utils/array'; -import { ENGINE_VERSION_V2 } from '../index.vue'; +import { DATA_ENGINE_V2 } from '../index.vue'; // UI components for Longhorn storage class parameters const DEFAULT_PARAMETERS = [ @@ -18,7 +18,7 @@ const DEFAULT_PARAMETERS = [ 'nodeSelector', 'migratable', 'encrypted', - 'engineVersion', + 'dataEngine', ]; const { @@ -74,7 +74,7 @@ export default { nodeSelector: null, encrypted: 'false', migratable: 'false', - engineVersion: ENGINE_VERSION_V2 + dataEngine: DATA_ENGINE_V2 }); } diff --git a/pkg/harvester/models/harvester/storage.k8s.io.storageclass.js b/pkg/harvester/models/harvester/storage.k8s.io.storageclass.js index f84676850a5..983e2af9413 100644 --- a/pkg/harvester/models/harvester/storage.k8s.io.storageclass.js +++ b/pkg/harvester/models/harvester/storage.k8s.io.storageclass.js @@ -3,7 +3,7 @@ import { HCI } from '../../types'; import StorageClass from '@shell/models/storage.k8s.io.storageclass'; import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../config/harvester'; import { LONGHORN_DRIVER } from '@shell/models/persistentvolume'; -import { ENGINE_VERSION_V1 } from '../../edit/harvesterhci.io.storage/index.vue'; +import { DATA_ENGINE_V1 } from '../../edit/harvesterhci.io.storage/index.vue'; export const LVM_DRIVER = 'lvm.driver.harvesterhci.io'; @@ -38,7 +38,7 @@ export default class HciStorageClass extends StorageClass { get longhornVersion() { if (this.provisioner === LONGHORN_DRIVER) { - return (this.parameters || {}).engineVersion || ENGINE_VERSION_V1; + return (this.parameters || {}).dataEngine || DATA_ENGINE_V1; } return null; From cb0b5b490473263b06e2fc5c13209fc59cb03214 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Tue, 24 Sep 2024 12:20:17 +0200 Subject: [PATCH 18/25] Hide format tooltip for longhorn v2 version Signed-off-by: Francesco Torchia --- pkg/harvester/edit/harvesterhci.io.host/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/harvester/edit/harvesterhci.io.host/index.vue b/pkg/harvester/edit/harvesterhci.io.host/index.vue index e526ad08000..a9680904041 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/index.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/index.vue @@ -211,7 +211,7 @@ export default { }, showFormattedWarning() { - const out = this.newDisks.filter(d => d.forceFormatted && d.isNew) || []; + const out = this.newDisks.filter(d => d.forceFormatted && d.isNew && d.provisionerVersion === LONGHORN_VERSION_V1) || []; return out.length > 0; }, From 31e47f13d5d4be955df3309146878715eb5dd220 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Wed, 25 Sep 2024 13:04:36 +0200 Subject: [PATCH 19/25] Remove Path for non longhorn disks and fix typos Signed-off-by: Francesco Torchia --- pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue | 2 +- pkg/harvester/l10n/en-us.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue b/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue index c715b441493..8fc030e762d 100644 --- a/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue +++ b/pkg/harvester/detail/harvesterhci.io.host/HarvesterHostDisk.vue @@ -212,7 +212,7 @@ export default { :value="value.displayName" />
-
+
Storage` page + no-options: No available Volume Groups, please add in the `Host > Storage` page node: label: Node allowedTopologies: From 8bcf17737208dd2482c17929e56b8b3366b3f27c Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Wed, 25 Sep 2024 13:21:02 +0200 Subject: [PATCH 20/25] Fix Provisioner/Force-formatted fields visibilty Signed-off-by: Francesco Torchia --- .../edit/harvesterhci.io.host/HarvesterDisk.vue | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue index abc4aec1114..92c4947f5a6 100644 --- a/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue +++ b/pkg/harvester/edit/harvesterhci.io.host/HarvesterDisk.vue @@ -265,7 +265,7 @@ export default { }, isLonghornV1() { - return this.isLonghorn && (this.longhornSystemVersion === LONGHORN_VERSION_V1 || this.forceLonghornV1); + return this.isLonghorn && this.value.provisionerVersion === LONGHORN_VERSION_V1; }, provisionerTooltip() { @@ -293,7 +293,7 @@ export default { if (provisioner === LONGHORN_DRIVER) { this.value.provisionerVersion = provisionerVersion || LONGHORN_VERSION_V1; } else { - delete this.value.provisionerVersion; + this.value.provisionerVersion = undefined; } }, @@ -424,16 +424,15 @@ export default {
-
+
From 1794c1a0e9db12fa174eaccf304bf1bc53f1f15d Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Thu, 26 Sep 2024 11:30:48 +0200 Subject: [PATCH 21/25] Set ReadWriteOnce accessMode for Longhorn V2 volumes Signed-off-by: Francesco Torchia --- pkg/harvester/edit/harvesterhci.io.volume.vue | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pkg/harvester/edit/harvesterhci.io.volume.vue b/pkg/harvester/edit/harvesterhci.io.volume.vue index fb3289b4fc1..c4be0be57ad 100644 --- a/pkg/harvester/edit/harvesterhci.io.volume.vue +++ b/pkg/harvester/edit/harvesterhci.io.volume.vue @@ -19,6 +19,7 @@ import { _CREATE } from '@shell/config/query-params'; import CreateEditView from '@shell/mixins/create-edit-view'; import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations'; import { STATE, NAME, AGE, NAMESPACE } from '@shell/config/table-headers'; +import { DATA_ENGINE_V2 } from './harvesterhci.io.storage/index.vue'; export default { name: 'HarvesterVolume', @@ -156,11 +157,13 @@ export default { return VOLUME_DATA_SOURCE_KIND[this.value.spec?.dataSource?.kind]; }, - storageClassOptions() { + storageClasses() { const inStore = this.$store.getters['currentProduct'].inStore; - const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS); + return this.$store.getters[`${ inStore }/all`](STORAGE_CLASS); + }, - const out = storages.filter(s => !s.parameters?.backingImage).map((s) => { + storageClassOptions() { + return this.storageClasses.filter(s => !s.parameters?.backingImage).map((s) => { const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name; return { @@ -168,8 +171,6 @@ export default { value: s.name, }; }) || []; - - return out; }, frontend() { @@ -226,6 +227,7 @@ export default { update() { let imageAnnotations = ''; let storageClassName = this.value.spec.storageClassName; + const storageClassDataEngine = this.storageClasses.find((sc) => sc.name === storageClassName)?.parameters?.dataEngine; if (this.isVMImage && this.imageId) { const images = this.$store.getters['harvester/all'](HCI.IMAGE); @@ -242,7 +244,8 @@ export default { const spec = { ...this.value.spec, resources: { requests: { storage: this.storage } }, - storageClassName + storageClassName, + accessModes: storageClassDataEngine === DATA_ENGINE_V2 ? ['ReadWriteOnce'] : ['ReadWriteMany'], }; this.value.setAnnotations(imageAnnotations); From 00a1d182a99b4f0d3f3b7af8ea27e9e6e4688519 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Thu, 26 Sep 2024 11:32:10 +0200 Subject: [PATCH 22/25] Limit VolumeBindingMode to WaitForFirstConsumer for LVM StorageClasses Signed-off-by: Francesco Torchia --- .../edit/harvesterhci.io.storage/index.vue | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/pkg/harvester/edit/harvesterhci.io.storage/index.vue b/pkg/harvester/edit/harvesterhci.io.storage/index.vue index 879a46a059f..486a75e88a1 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/index.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/index.vue @@ -28,6 +28,9 @@ export const DATA_ENGINE_V2 = 'v2'; export const LVM_TOPOLOGY_LABEL = 'topology.lvm.csi/node'; +const VOLUME_BINDING_MODE_IMMEDIATE = 'Immediate'; +const VOLUME_BINDING_MODE_WAIT = 'WaitForFirstConsumer'; + export default { name: 'HarvesterStorage', @@ -69,11 +72,11 @@ export default { const volumeBindingModeOptions = [ { label: this.t('storageClass.customize.volumeBindingMode.now'), - value: 'Immediate' + value: VOLUME_BINDING_MODE_IMMEDIATE }, { label: this.t('harvester.storage.customize.volumeBindingMode.later'), - value: 'WaitForFirstConsumer' + value: VOLUME_BINDING_MODE_WAIT } ]; @@ -81,14 +84,17 @@ export default { this.$set(this.value, 'parameters', this.value.parameters || {}); this.$set(this.value, 'provisioner', this.value.provisioner || LONGHORN_DRIVER); + this.$set(this.value, 'allowVolumeExpansion', this.value.allowVolumeExpansion || allowVolumeExpansionOptions[0].value); + this.$set(this.value, 'reclaimPolicy', this.value.reclaimPolicy || reclaimPolicyOptions[0].value); if (this.value.provisioner === LONGHORN_DRIVER) { this.$set(this.value.parameters, 'dataEngine', this.value.longhornVersion); + this.$set(this.value, 'volumeBindingMode', this.value.volumeBindingMode || VOLUME_BINDING_MODE_IMMEDIATE); } - this.$set(this.value, 'allowVolumeExpansion', this.value.allowVolumeExpansion || allowVolumeExpansionOptions[0].value); - this.$set(this.value, 'reclaimPolicy', this.value.reclaimPolicy || reclaimPolicyOptions[0].value); - this.$set(this.value, 'volumeBindingMode', this.value.volumeBindingMode || volumeBindingModeOptions[0].value); + if (this.value.provisioner === LVM_DRIVER) { + this.$set(this.value, 'volumeBindingMode', this.value.volumeBindingMode || VOLUME_BINDING_MODE_WAIT); + } let provisioner = `${ this.value.provisioner || LONGHORN_DRIVER }`; @@ -97,6 +103,7 @@ export default { } return { + LVM_DRIVER, reclaimPolicyOptions, allowVolumeExpansionOptions, volumeBindingModeOptions, @@ -194,14 +201,16 @@ export default { } else { delete this.value.allowedTopologies; } - } - this.$set(this.value, 'provisioner', provisioner); + this.$set(this.value, 'volumeBindingMode', VOLUME_BINDING_MODE_WAIT); + } if (provisioner === LONGHORN_DRIVER) { parameters = { dataEngine }; + this.$set(this.value, 'volumeBindingMode', VOLUME_BINDING_MODE_IMMEDIATE); } + this.$set(this.value, 'provisioner', provisioner); this.$set(this.value, 'allowVolumeExpansion', this.value.provisioner === LONGHORN_DRIVER); this.$set(this.value, 'parameters', parameters); } @@ -324,6 +333,7 @@ export default { :label="t('storageClass.customize.volumeBindingMode.label')" :mode="modeOverride" :options="volumeBindingModeOptions" + :disabled="provisioner === LVM_DRIVER" />
From f91ba19eb8f10746bbf06eec4365fbeaaf072420 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Thu, 26 Sep 2024 11:34:23 +0200 Subject: [PATCH 23/25] Fix typos Signed-off-by: Francesco Torchia --- .../provisioners/lvm.driver.harvesterhci.io.vue | 4 ++-- pkg/harvester/edit/harvesterhci.io.volume.vue | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue index 2f00234062a..dd714bca8e1 100644 --- a/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue +++ b/pkg/harvester/edit/harvesterhci.io.storage/provisioners/lvm.driver.harvesterhci.io.vue @@ -14,7 +14,7 @@ const DEFAULT_PARAMETERS = [ 'vgName' ]; -const DEFAUL_TOPOLOGIES = [{ +const DEFAULT_TOPOLOGIES = [{ matchLabelExpressions: [{ key: LVM_TOPOLOGY_LABEL, values: [] @@ -64,7 +64,7 @@ export default { node(value) { delete (this.value.parameters.vgName); - const allowedTopologies = [...DEFAUL_TOPOLOGIES]; + const allowedTopologies = [...DEFAULT_TOPOLOGIES]; allowedTopologies[0].matchLabelExpressions[0].values = [value]; diff --git a/pkg/harvester/edit/harvesterhci.io.volume.vue b/pkg/harvester/edit/harvesterhci.io.volume.vue index c4be0be57ad..0db78ce54c3 100644 --- a/pkg/harvester/edit/harvesterhci.io.volume.vue +++ b/pkg/harvester/edit/harvesterhci.io.volume.vue @@ -159,6 +159,7 @@ export default { storageClasses() { const inStore = this.$store.getters['currentProduct'].inStore; + return this.$store.getters[`${ inStore }/all`](STORAGE_CLASS); }, @@ -227,7 +228,7 @@ export default { update() { let imageAnnotations = ''; let storageClassName = this.value.spec.storageClassName; - const storageClassDataEngine = this.storageClasses.find((sc) => sc.name === storageClassName)?.parameters?.dataEngine; + const storageClassDataEngine = this.storageClasses.find(sc => sc.name === storageClassName)?.parameters?.dataEngine; if (this.isVMImage && this.imageId) { const images = this.$store.getters['harvester/all'](HCI.IMAGE); @@ -243,7 +244,7 @@ export default { const spec = { ...this.value.spec, - resources: { requests: { storage: this.storage } }, + resources: { requests: { storage: this.storage } }, storageClassName, accessModes: storageClassDataEngine === DATA_ENGINE_V2 ? ['ReadWriteOnce'] : ['ReadWriteMany'], }; From a38a0b917fa2c9fb5d2c7f3fcc27633d0c172a67 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Fri, 27 Sep 2024 12:34:08 +0200 Subject: [PATCH 24/25] Fix new Volumes access mode rules Signed-off-by: Francesco Torchia --- pkg/harvester/edit/harvesterhci.io.volume.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/harvester/edit/harvesterhci.io.volume.vue b/pkg/harvester/edit/harvesterhci.io.volume.vue index 0db78ce54c3..90ea19a5dc0 100644 --- a/pkg/harvester/edit/harvesterhci.io.volume.vue +++ b/pkg/harvester/edit/harvesterhci.io.volume.vue @@ -19,6 +19,7 @@ import { _CREATE } from '@shell/config/query-params'; import CreateEditView from '@shell/mixins/create-edit-view'; import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations'; import { STATE, NAME, AGE, NAMESPACE } from '@shell/config/table-headers'; +import { LVM_DRIVER } from '../models/harvester/storage.k8s.io.storageclass'; import { DATA_ENGINE_V2 } from './harvesterhci.io.storage/index.vue'; export default { @@ -228,7 +229,10 @@ export default { update() { let imageAnnotations = ''; let storageClassName = this.value.spec.storageClassName; - const storageClassDataEngine = this.storageClasses.find(sc => sc.name === storageClassName)?.parameters?.dataEngine; + + const storageClass = this.storageClasses.find(sc => sc.name === storageClassName); + const storageClassProvisioner = storageClass?.provisioner; + const storageClassDataEngine = storageClass?.parameters?.dataEngine; if (this.isVMImage && this.imageId) { const images = this.$store.getters['harvester/all'](HCI.IMAGE); @@ -246,7 +250,7 @@ export default { ...this.value.spec, resources: { requests: { storage: this.storage } }, storageClassName, - accessModes: storageClassDataEngine === DATA_ENGINE_V2 ? ['ReadWriteOnce'] : ['ReadWriteMany'], + accessModes: storageClassProvisioner === LVM_DRIVER || storageClassDataEngine === DATA_ENGINE_V2 ? ['ReadWriteOnce'] : ['ReadWriteMany'], }; this.value.setAnnotations(imageAnnotations); From 21cbfa607741dafbb6f00101fde8d4166b82be89 Mon Sep 17 00:00:00 2001 From: Francesco Torchia Date: Sun, 29 Sep 2024 19:19:50 +0200 Subject: [PATCH 25/25] Fix Virtual Machines Volumes access mode rules Signed-off-by: Francesco Torchia --- .../VirtualMachineVolume/index.vue | 7 ++---- .../VirtualMachineVolume/type/volume.vue | 23 +++++++++++++++---- pkg/harvester/mixins/harvester-vm/index.js | 14 +++++++++-- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/index.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/index.vue index 22be4c10ef3..b507c8c1231 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/index.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/index.vue @@ -8,7 +8,7 @@ import { LabeledInput } from '@components/Form/LabeledInput'; import LabeledSelect from '@shell/components/form/LabeledSelect'; import ModalWithCard from '@shell/components/ModalWithCard'; -import { PVC, STORAGE_CLASS } from '@shell/config/types'; +import { PVC } from '@shell/config/types'; import { HCI } from '../../../types'; import { clone } from '@shell/utils/object'; import { removeObject } from '@shell/utils/array'; @@ -172,10 +172,7 @@ export default { }; if (type === SOURCE_TYPE.NEW) { - const inStore = this.$store.getters['currentProduct'].inStore; - const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find( O => O.isDefault); - - neu.storageClassName = defaultStorage?.metadata?.name || 'longhorn'; + neu.storageClassName = this.defaultStorageClass?.metadata?.name || 'longhorn'; } this.rows.push(neu); diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/volume.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/volume.vue index 68a1404dace..5e86127a910 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/volume.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineVolume/type/volume.vue @@ -11,6 +11,8 @@ import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map'; import { _VIEW } from '@shell/config/query-params'; import LabelValue from '@shell/components/LabelValue'; import { ucFirst } from '@shell/utils/string'; +import { LVM_DRIVER } from '../../../../models/harvester/storage.k8s.io.storageclass'; +import { DATA_ENGINE_V2 } from '../../../../edit/harvesterhci.io.storage/index.vue'; export default { name: 'HarvesterEditVolume', @@ -85,10 +87,12 @@ export default { return !this.value.newCreateId && this.isEdit && this.isVirtualType; }, - storageClassOptions() { - const storages = this.$store.getters[`harvester/all`](STORAGE_CLASS) || []; + storageClasses() { + return this.$store.getters[`harvester/all`](STORAGE_CLASS) || []; + }, - const out = storages.filter(s => !s.parameters?.backingImage).map((s) => { + storageClassOptions() { + return this.storageClasses.filter(s => !s.parameters?.backingImage).map((s) => { const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name; return { @@ -96,12 +100,21 @@ export default { value: s.name, }; }) || []; - - return out; }, }, watch: { + 'value.storageClassName': { + immediate: true, + handler(neu) { + const storageClass = this.storageClasses.find(sc => sc.name === neu); + const provisioner = storageClass?.provisioner; + const engine = storageClass?.parameters?.dataEngine; + + this.value.accessMode = provisioner === LVM_DRIVER || engine === DATA_ENGINE_V2 ? 'ReadWriteOnce' : 'ReadWriteMany'; + } + }, + 'value.type'(neu) { if (neu === 'cd-rom') { this.$set(this.value, 'bus', 'sata'); diff --git a/pkg/harvester/mixins/harvester-vm/index.js b/pkg/harvester/mixins/harvester-vm/index.js index bc49bf410a1..7793258ac0b 100644 --- a/pkg/harvester/mixins/harvester-vm/index.js +++ b/pkg/harvester/mixins/harvester-vm/index.js @@ -25,6 +25,9 @@ import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations import impl, { QGA_JSON, USB_TABLET } from './impl'; import { uniq } from '@shell/utils/array'; import { parseVolumeClaimTemplates } from '../../utils/vm'; +import { LONGHORN_VERSION_V1, LONGHORN_VERSION_V2 } from '@shell/models/persistentvolume'; + +const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine'; export const MANAGEMENT_NETWORK = 'management Network'; @@ -100,6 +103,7 @@ export default { vms: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VM }), secrets: this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET }), addons: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.ADD_ONS }), + longhornV2Engine: this.$store.dispatch(`${ inStore }/find`, { type: LONGHORN.SETTINGS, id: LONGHORN_V2_DATA_ENGINE }), }; if (this.$store.getters[`${ inStore }/schemaFor`](NODE)) { @@ -241,7 +245,7 @@ export default { defaultStorageClass() { const defaultStorage = this.$store.getters[`${ this.inStore }/all`](STORAGE_CLASS).find( O => O.isDefault); - return defaultStorage?.metadata?.name || 'longhorn'; + return defaultStorage; }, storageClassSetting() { @@ -254,6 +258,12 @@ export default { } }, + longhornSystemVersion() { + const v2DataEngine = this.$store.getters[`${ this.inStore }/byId`](LONGHORN.SETTINGS, LONGHORN_V2_DATA_ENGINE) || {}; + + return v2DataEngine.value === 'true' ? LONGHORN_VERSION_V2 : LONGHORN_VERSION_V1; + }, + customVolumeMode() { return this.storageClassSetting.volumeMode || 'Block'; }, @@ -445,7 +455,7 @@ export default { id: randomStr(5), source: SOURCE_TYPE.IMAGE, name: 'disk-0', - accessMode: 'ReadWriteMany', + accessMode: this.longhornSystemVersion === LONGHORN_VERSION_V2 ? 'ReadWriteOnce' : 'ReadWriteMany', bus, volumeName: '', size,