mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-02-04 06:51:44 +00:00
feat: online Volume Resizing from the VM Page (#568)
* feat: add conditions tab * feat: ignore restart for resizing * refactor: remove unused code --------- Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
This commit is contained in:
parent
1a3822881e
commit
e6dd8d6771
@ -7,6 +7,7 @@ import ResourceTabs from '@shell/components/form/ResourceTabs';
|
|||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||||
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
|
import Conditions from '@shell/components/form/Conditions';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import { get } from '@shell/utils/object';
|
import { get } from '@shell/utils/object';
|
||||||
@ -40,6 +41,7 @@ export default {
|
|||||||
LabeledSelect,
|
LabeledSelect,
|
||||||
LabeledInput,
|
LabeledInput,
|
||||||
NameNsDescription,
|
NameNsDescription,
|
||||||
|
Conditions
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [CreateEditView],
|
mixins: [CreateEditView],
|
||||||
@ -593,6 +595,15 @@ export default {
|
|||||||
:label="t('nameNsDescription.name.label')"
|
:label="t('nameNsDescription.name.label')"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab
|
||||||
|
v-if="!isCreate"
|
||||||
|
name="conditions"
|
||||||
|
:label="t('harvester.volume.tabs.conditions')"
|
||||||
|
class="bordered-table"
|
||||||
|
:mode="mode"
|
||||||
|
>
|
||||||
|
<Conditions :value="value" />
|
||||||
|
</Tab>
|
||||||
</ResourceTabs>
|
</ResourceTabs>
|
||||||
</CruResource>
|
</CruResource>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import Tabbed from '@shell/components/Tabbed';
|
import Tabbed from '@shell/components/Tabbed';
|
||||||
import { clone, set } from '@shell/utils/object';
|
import { clone } from '@shell/utils/object';
|
||||||
import Tab from '@shell/components/Tabbed/Tab';
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
import { Checkbox } from '@components/Form/Checkbox';
|
import { Checkbox } from '@components/Form/Checkbox';
|
||||||
import CruResource from '@shell/components/CruResource';
|
import CruResource from '@shell/components/CruResource';
|
||||||
@ -434,38 +434,44 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_compareDisksIgnoreStorage(disksA = [], disksB = []) {
|
||||||
|
if (disksA.length !== disksB.length) return false;
|
||||||
|
|
||||||
|
const sortedA = [...disksA].sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));
|
||||||
|
const sortedB = [...disksB].sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));
|
||||||
|
|
||||||
|
for (let i = 0; i < sortedA.length; i++) {
|
||||||
|
const cloneA = clone(sortedA[i]);
|
||||||
|
const cloneB = clone(sortedB[i]);
|
||||||
|
|
||||||
|
// remove the storage field before comparison
|
||||||
|
delete cloneA?.spec?.resources?.requests?.storage;
|
||||||
|
delete cloneB?.spec?.resources?.requests?.storage;
|
||||||
|
|
||||||
|
if (!isEqual(cloneA, cloneB)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_shouldPromptRestart(oldVM, newVM) {
|
||||||
|
const specChanged = !isEqual(oldVM?.spec, newVM?.spec);
|
||||||
|
|
||||||
|
// check if the disk defined in annotations has changed, ignoring storage size
|
||||||
|
const oldDisks = parseVolumeClaimTemplates(oldVM);
|
||||||
|
const newDisks = parseVolumeClaimTemplates(newVM);
|
||||||
|
const diskChanged = !this._compareDisksIgnoreStorage(oldDisks, newDisks);
|
||||||
|
|
||||||
|
return specChanged || diskChanged;
|
||||||
|
},
|
||||||
|
|
||||||
restartVM() {
|
restartVM() {
|
||||||
if (this.mode !== 'edit') {
|
if (this.mode !== 'edit' || !this.value.isRunning) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.value.isRunning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const cloneDeepNewVM = clone(this.value);
|
|
||||||
|
|
||||||
// new VM
|
const needsRestart = this._shouldPromptRestart(this.cloneVM, this.value);
|
||||||
delete cloneDeepNewVM?.metadata;
|
|
||||||
delete cloneDeepNewVM?.__clone;
|
|
||||||
|
|
||||||
// old VM
|
// if no restart is needed (either no changes or only size changed)
|
||||||
delete this.cloneVM?.metadata;
|
if (!needsRestart) return;
|
||||||
delete this.cloneVM?.__clone;
|
|
||||||
|
|
||||||
// add empty hostDevices to old VM as CRD does not have it.
|
|
||||||
const devicesObj = this.cloneVM?.spec?.template?.spec?.domain?.devices;
|
|
||||||
|
|
||||||
if (devicesObj && devicesObj.hostDevices === undefined) {
|
|
||||||
set(devicesObj, 'hostDevices', []);
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldVM = JSON.parse(JSON.stringify(this.cloneVM));
|
|
||||||
const newVM = JSON.parse(JSON.stringify(cloneDeepNewVM));
|
|
||||||
|
|
||||||
// we won't show restart dialog in yaml page as we don't have a way to detect change in yaml editor.
|
|
||||||
// only check VM is changed in form page.
|
|
||||||
if (isEqual(oldVM, newVM)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.isOpen = true;
|
this.isOpen = true;
|
||||||
|
|||||||
@ -837,6 +837,7 @@ harvester:
|
|||||||
snapshots: Snapshots
|
snapshots: Snapshots
|
||||||
datasource: Data Source
|
datasource: Data Source
|
||||||
details: Details
|
details: Details
|
||||||
|
conditions: Conditions
|
||||||
size: Size
|
size: Size
|
||||||
volumeMode: Volume Mode
|
volumeMode: Volume Mode
|
||||||
source: Source
|
source: Source
|
||||||
@ -1065,21 +1066,21 @@ harvester:
|
|||||||
acl:
|
acl:
|
||||||
label: Access Control List
|
label: Access Control List
|
||||||
tooltip: The ACL to apply to this Subnet. Must be one of the ACLs in the same namespace.
|
tooltip: The ACL to apply to this Subnet. Must be one of the ACLs in the same namespace.
|
||||||
action:
|
action:
|
||||||
label: Action
|
label: Action
|
||||||
placeholder: Please select an action
|
placeholder: Please select an action
|
||||||
direction:
|
direction:
|
||||||
label: Direction
|
label: Direction
|
||||||
placeholder: Please select a direction
|
placeholder: Please select a direction
|
||||||
addRule: Add Rule
|
addRule: Add Rule
|
||||||
priority:
|
priority:
|
||||||
label: Priority
|
label: Priority
|
||||||
placeholder: Please select a priority
|
placeholder: Please select a priority
|
||||||
match:
|
match:
|
||||||
label: Match
|
label: Match
|
||||||
placeholder: e.g. ip4.dst == 10.10.0.2
|
placeholder: e.g. ip4.dst == 10.10.0.2
|
||||||
banner: The supported field in ACL match can refer to <a href="https://kubeovn.github.io/docs/v1.14.x/en/guide/subnet/#subnet-acl" target="_blank">KubeOvn Subnet ACL document</a>
|
banner: The supported field in ACL match can refer to <a href="https://kubeovn.github.io/docs/v1.14.x/en/guide/subnet/#subnet-acl" target="_blank">KubeOvn Subnet ACL document</a>
|
||||||
|
|
||||||
vpc:
|
vpc:
|
||||||
noAddonEnabled:
|
noAddonEnabled:
|
||||||
prefix: The kubeovn-operator add-on is not enabled, click
|
prefix: The kubeovn-operator add-on is not enabled, click
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user