mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2025-12-13 05:01:43 +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 { LabeledInput } from '@components/Form/LabeledInput';
|
||||
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||
import Conditions from '@shell/components/form/Conditions';
|
||||
import { Banner } from '@components/Banner';
|
||||
import { allHash } from '@shell/utils/promise';
|
||||
import { get } from '@shell/utils/object';
|
||||
@ -40,6 +41,7 @@ export default {
|
||||
LabeledSelect,
|
||||
LabeledInput,
|
||||
NameNsDescription,
|
||||
Conditions
|
||||
},
|
||||
|
||||
mixins: [CreateEditView],
|
||||
@ -593,6 +595,15 @@ export default {
|
||||
:label="t('nameNsDescription.name.label')"
|
||||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
v-if="!isCreate"
|
||||
name="conditions"
|
||||
:label="t('harvester.volume.tabs.conditions')"
|
||||
class="bordered-table"
|
||||
:mode="mode"
|
||||
>
|
||||
<Conditions :value="value" />
|
||||
</Tab>
|
||||
</ResourceTabs>
|
||||
</CruResource>
|
||||
</template>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import { isEqual } from 'lodash';
|
||||
import { mapGetters } from 'vuex';
|
||||
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 { Checkbox } from '@components/Form/Checkbox';
|
||||
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() {
|
||||
if (this.mode !== 'edit') {
|
||||
return;
|
||||
}
|
||||
if (!this.value.isRunning) {
|
||||
return;
|
||||
}
|
||||
const cloneDeepNewVM = clone(this.value);
|
||||
if (this.mode !== 'edit' || !this.value.isRunning) return;
|
||||
|
||||
// new VM
|
||||
delete cloneDeepNewVM?.metadata;
|
||||
delete cloneDeepNewVM?.__clone;
|
||||
const needsRestart = this._shouldPromptRestart(this.cloneVM, this.value);
|
||||
|
||||
// old VM
|
||||
delete this.cloneVM?.metadata;
|
||||
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;
|
||||
}
|
||||
// if no restart is needed (either no changes or only size changed)
|
||||
if (!needsRestart) return;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
this.isOpen = true;
|
||||
|
||||
@ -837,6 +837,7 @@ harvester:
|
||||
snapshots: Snapshots
|
||||
datasource: Data Source
|
||||
details: Details
|
||||
conditions: Conditions
|
||||
size: Size
|
||||
volumeMode: Volume Mode
|
||||
source: Source
|
||||
@ -1065,21 +1066,21 @@ harvester:
|
||||
acl:
|
||||
label: Access Control List
|
||||
tooltip: The ACL to apply to this Subnet. Must be one of the ACLs in the same namespace.
|
||||
action:
|
||||
action:
|
||||
label: Action
|
||||
placeholder: Please select an action
|
||||
direction:
|
||||
direction:
|
||||
label: Direction
|
||||
placeholder: Please select a direction
|
||||
addRule: Add Rule
|
||||
priority:
|
||||
priority:
|
||||
label: Priority
|
||||
placeholder: Please select a priority
|
||||
match:
|
||||
match:
|
||||
label: Match
|
||||
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>
|
||||
|
||||
|
||||
vpc:
|
||||
noAddonEnabled:
|
||||
prefix: The kubeovn-operator add-on is not enabled, click
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user