mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-01 03:36:04 +08:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d996c42dc | ||
|
|
88cd302ce4 | ||
|
|
e6dd8d6771 | ||
|
|
1a3822881e | ||
|
|
ee8bb21a10 | ||
|
|
e3d30a0eec | ||
|
|
532b6c4d50 | ||
|
|
f22644e058 | ||
|
|
cd128d0444 | ||
|
|
0278f51260 | ||
|
|
98efd63110 | ||
|
|
78e78ac8dd |
@@ -22,6 +22,10 @@ module.exports = {
|
||||
'security', // Security fixes
|
||||
]
|
||||
],
|
||||
// Allows the scope to be empty. [0] means the rule is disabled.
|
||||
'scope-empty': [0, 'always'],
|
||||
// Allows any string for the scope.
|
||||
'scope-case': [2, 'always', 'kebab-case'],
|
||||
'type-case': [2, 'always', 'lower-case'],
|
||||
'type-empty': [2, 'never'],
|
||||
'subject-case': [0, 'never'],
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
"nth-check": "2.1.1",
|
||||
"qs": "6.14.0",
|
||||
"roarr": "7.21.1",
|
||||
"semver": "7.7.2",
|
||||
"semver": "7.7.3",
|
||||
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -391,4 +391,7 @@ export default {
|
||||
.vm-console .v-popper__popper{
|
||||
margin-top: 8px;
|
||||
}
|
||||
.vm-console .v-popper__inner{
|
||||
overflow-y: visible;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -50,7 +50,9 @@ const FEATURE_FLAGS = {
|
||||
],
|
||||
'v1.6.1': [],
|
||||
'v1.7.0': [
|
||||
'vmMachineTypeAuto'
|
||||
'vmMachineTypeAuto',
|
||||
'lhV2VolExpansion',
|
||||
'l2VlanTrunkMode'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ export const BACKUP_TYPE = {
|
||||
};
|
||||
|
||||
export const NETWORK_TYPE = {
|
||||
L2VLAN: 'L2VlanNetwork',
|
||||
UNTAGGED: 'UntaggedNetwork',
|
||||
OVERLAY: 'OverlayNetwork',
|
||||
L2VLAN: 'L2VlanNetwork',
|
||||
UNTAGGED: 'UntaggedNetwork',
|
||||
OVERLAY: 'OverlayNetwork',
|
||||
L2TRUNK_VLAN: 'L2VlanTrunkNetwork',
|
||||
};
|
||||
|
||||
export const VOLUME_MODE = {
|
||||
@@ -23,3 +24,8 @@ export const INTERNAL_STORAGE_CLASS = {
|
||||
VMSTATE_PERSISTENCE: 'vmstate-persistence',
|
||||
LONGHORN_STATIC: 'longhorn-static',
|
||||
};
|
||||
|
||||
export const L2VLAN_MODE = {
|
||||
ACCESS: 'access',
|
||||
TRUNK: 'trunk',
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import LabelValue from '@shell/components/LabelValue';
|
||||
import { BadgeState } from '@components/BadgeState';
|
||||
import { Banner } from '@components/Banner';
|
||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||
import { RadioGroup, RadioButton } from '@components/Form/Radio';
|
||||
import { RadioGroup } from '@components/Form/Radio';
|
||||
import HarvesterDisk from '../../mixins/harvester-disk';
|
||||
import Tags from '../../components/DiskTags';
|
||||
import { HCI } from '../../types';
|
||||
@@ -30,7 +30,6 @@ export default {
|
||||
BadgeState,
|
||||
Banner,
|
||||
RadioGroup,
|
||||
RadioButton,
|
||||
ModalWithCard,
|
||||
Tags,
|
||||
},
|
||||
@@ -184,12 +183,11 @@ export default {
|
||||
},
|
||||
|
||||
forceFormattedDisabled() {
|
||||
const lastFormattedAt = this.blockDevice?.status?.deviceStatus?.fileSystem?.LastFormattedAt;
|
||||
const fileSystem = this.blockDevice?.status?.deviceStatus?.fileSystem.type;
|
||||
|
||||
const systems = ['ext4', 'XFS'];
|
||||
|
||||
if (lastFormattedAt || this.blockDevice?.childParts?.length > 0) {
|
||||
if (this.blockDevice?.childParts?.length > 0) {
|
||||
return true;
|
||||
} else if (systems.includes(fileSystem)) {
|
||||
return false;
|
||||
@@ -446,7 +444,7 @@ export default {
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="(value.isNew && isLonghornV1 && !isFormatted) || isCorrupted"
|
||||
v-if="(value.isNew && isLonghornV1) || isCorrupted"
|
||||
class="col span-6"
|
||||
>
|
||||
<RadioGroup
|
||||
@@ -459,15 +457,6 @@ export default {
|
||||
:disabled="forceFormattedDisabled"
|
||||
tooltip-key="harvester.host.disk.forceFormatted.toolTip"
|
||||
>
|
||||
<template #1="{option, listeners}">
|
||||
<RadioButton
|
||||
:label="option.label"
|
||||
:val="option.value"
|
||||
:value="value.forceFormatted"
|
||||
:disabled="forceFormattedDisabled && !value.forceFormatted"
|
||||
v-on="listeners"
|
||||
/>
|
||||
</template>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -10,9 +10,13 @@ import { HCI as HCI_LABELS_ANNOTATIONS } from '@pkg/harvester/config/labels-anno
|
||||
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||
import { allHash } from '@shell/utils/promise';
|
||||
import { HCI } from '../types';
|
||||
import { NETWORK_TYPE } from '../config/types';
|
||||
import { NETWORK_TYPE, L2VLAN_MODE } from '../config/types';
|
||||
import { removeObject } from '@shell/utils/array';
|
||||
|
||||
const { L2VLAN, UNTAGGED, OVERLAY } = NETWORK_TYPE;
|
||||
const {
|
||||
L2VLAN, UNTAGGED, OVERLAY, L2TRUNK_VLAN
|
||||
} = NETWORK_TYPE;
|
||||
const { ACCESS, TRUNK } = L2VLAN_MODE;
|
||||
|
||||
const AUTO = 'auto';
|
||||
const MANUAL = 'manual';
|
||||
@@ -52,6 +56,8 @@ export default {
|
||||
return {
|
||||
config,
|
||||
type,
|
||||
l2VlanMode: this.value.vlanType === L2TRUNK_VLAN ? TRUNK : ACCESS,
|
||||
vlanTrunk: this.parseVlanTrunk(config),
|
||||
layer3Network: {
|
||||
mode: layer3Network.mode || AUTO,
|
||||
serverIPAddr: layer3Network.serverIPAddr || '',
|
||||
@@ -108,6 +114,10 @@ export default {
|
||||
}];
|
||||
},
|
||||
|
||||
l2VlanTrunkModeFeatureEnabled() {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('l2VlanTrunkMode');
|
||||
},
|
||||
|
||||
kubeovnVpcSubnetSupport() {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('kubeovnVpcSubnet');
|
||||
},
|
||||
@@ -131,6 +141,16 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
l2VlanModeOptions() {
|
||||
return [{
|
||||
label: this.t('harvester.vlanStatus.vlanConfig.l2VlanMode.access'),
|
||||
value: ACCESS,
|
||||
}, {
|
||||
label: this.t('harvester.vlanStatus.vlanConfig.l2VlanMode.trunk'),
|
||||
value: TRUNK,
|
||||
}];
|
||||
},
|
||||
|
||||
networkTypes() {
|
||||
const types = [L2VLAN, UNTAGGED];
|
||||
|
||||
@@ -149,6 +169,22 @@ export default {
|
||||
return this.type === L2VLAN;
|
||||
},
|
||||
|
||||
isL2VlanTrunkMode() {
|
||||
if (this.isView) {
|
||||
return this.value.vlanType === L2TRUNK_VLAN && this.l2VlanMode === TRUNK;
|
||||
}
|
||||
|
||||
return this.type === L2TRUNK_VLAN || (this.l2VlanMode === TRUNK && this.type === L2VLAN);
|
||||
},
|
||||
|
||||
isL2VlanAccessMode() {
|
||||
if (this.isView) {
|
||||
return this.value.vlanType === L2VLAN && this.l2VlanMode === ACCESS;
|
||||
}
|
||||
|
||||
return this.type === L2VLAN && this.l2VlanMode === ACCESS;
|
||||
},
|
||||
|
||||
isOverlayNetwork() {
|
||||
if (this.isView) {
|
||||
return this.value.vlanType === OVERLAY;
|
||||
@@ -168,11 +204,11 @@ export default {
|
||||
|
||||
watch: {
|
||||
type(newType) {
|
||||
if (newType === OVERLAY) {
|
||||
if (newType === OVERLAY) { // overlay network configuration
|
||||
this.config.type = 'kube-ovn';
|
||||
this.config.provider = `${ this.value.metadata.name }.${ this.value.metadata.namespace }.ovn`;
|
||||
this.config.server_socket = '/run/openvswitch/kube-ovn-daemon.sock';
|
||||
} else {
|
||||
} else { // l2vlan or untagged network configuration
|
||||
this.config.type = 'bridge';
|
||||
this.config.promiscMode = true;
|
||||
this.config.ipam = {};
|
||||
@@ -180,7 +216,20 @@ export default {
|
||||
delete this.config.provider;
|
||||
delete this.config.server_socket;
|
||||
}
|
||||
}
|
||||
},
|
||||
l2VlanMode(newAccessMode) {
|
||||
if (this.type !== L2VLAN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newAccessMode === TRUNK) { // trunk mode
|
||||
this.config.vlan = 0;
|
||||
this.config.vlanTrunk = this.vlanTrunk;
|
||||
} else { // access mode
|
||||
delete this.config.vlanTrunk;
|
||||
this.config.vlan = '';
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -188,7 +237,15 @@ export default {
|
||||
const errors = [];
|
||||
|
||||
if (this.isL2VlanNetwork || this.isUntaggedNetwork) {
|
||||
if (!this.config.vlan && !this.isUntaggedNetwork) {
|
||||
if (this.isL2VlanTrunkMode && this.vlanTrunk.some((trunk) => trunk.minID === '')) {
|
||||
errors.push(this.$store.getters['i18n/t']('validation.required', { key: this.t('harvester.vlanStatus.vlanConfig.vlanTrunk.minId') }));
|
||||
}
|
||||
|
||||
if (this.isL2VlanTrunkMode && this.vlanTrunk.some((trunk) => trunk.maxID === '')) {
|
||||
errors.push(this.$store.getters['i18n/t']('validation.required', { key: this.t('harvester.vlanStatus.vlanConfig.vlanTrunk.maxId') }));
|
||||
}
|
||||
|
||||
if (this.isL2VlanAccessMode && !this.config.vlan && !this.isUntaggedNetwork) {
|
||||
errors.push(this.$store.getters['i18n/t']('validation.required', { key: this.t('tableHeaders.networkVlan') }));
|
||||
}
|
||||
|
||||
@@ -217,6 +274,30 @@ export default {
|
||||
await this.save(buttonCb);
|
||||
},
|
||||
|
||||
parseVlanTrunk(config) {
|
||||
if (config?.vlanTrunk && config?.vlanTrunk?.length > 0) {
|
||||
return config.vlanTrunk;
|
||||
}
|
||||
|
||||
return [{ minID: '', maxID: '' }];
|
||||
},
|
||||
|
||||
removeVlanTrunk(trunk) {
|
||||
removeObject(this.vlanTrunk, trunk);
|
||||
},
|
||||
|
||||
addVlanTrunk() {
|
||||
if (!this.config.vlanTrunk) {
|
||||
this.config.vlanTrunk = [];
|
||||
}
|
||||
this.vlanTrunk.push({ minID: this.minId, maxID: this.maxId });
|
||||
this.config.vlanTrunk = this.vlanTrunk;
|
||||
},
|
||||
|
||||
vlanTrunkChange() {
|
||||
this.config.vlanTrunk = this.vlanTrunk;
|
||||
},
|
||||
|
||||
input(neu) {
|
||||
if (neu === '') {
|
||||
this.config.vlan = '';
|
||||
@@ -292,8 +373,76 @@ export default {
|
||||
required
|
||||
/>
|
||||
|
||||
<LabeledSelect
|
||||
v-if="isL2VlanNetwork && l2VlanTrunkModeFeatureEnabled"
|
||||
v-model:value="l2VlanMode"
|
||||
class="mb-20"
|
||||
:options="l2VlanModeOptions"
|
||||
:mode="mode"
|
||||
:disabled="isEdit"
|
||||
:label="t('harvester.vlanStatus.vlanConfig.l2VlanMode.label')"
|
||||
required
|
||||
/>
|
||||
<div v-if="isL2VlanTrunkMode && l2VlanTrunkModeFeatureEnabled">
|
||||
<div
|
||||
v-for="(trunk, i) in vlanTrunk"
|
||||
:key="i"
|
||||
>
|
||||
<div class="row mt-10">
|
||||
<div class="col trunk-span">
|
||||
<LabeledInput
|
||||
v-model:value.number="trunk.minID"
|
||||
class="mb-20"
|
||||
required
|
||||
placeholder="e.g. 1-4094"
|
||||
:min="1"
|
||||
:max="4094"
|
||||
type="number"
|
||||
:label="t('harvester.vlanStatus.vlanConfig.vlanTrunk.minId')"
|
||||
:mode="mode"
|
||||
@update:value="vlanTrunkChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="col trunk-span">
|
||||
<LabeledInput
|
||||
v-model:value.number="trunk.maxID"
|
||||
class="mb-20"
|
||||
:max="4094"
|
||||
:min="1"
|
||||
placeholder="e.g. 1-4094"
|
||||
required
|
||||
type="number"
|
||||
:label="t('harvester.vlanStatus.vlanConfig.vlanTrunk.maxId')"
|
||||
:mode="mode"
|
||||
@update:value="vlanTrunkChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="col remove-btn mb-20">
|
||||
<button
|
||||
type="button"
|
||||
:disabled="isView || vlanTrunk.length <= 1"
|
||||
:aria-label="t('generic.ariaLabel.remove', {index: i+1})"
|
||||
role="button"
|
||||
class="btn role-link"
|
||||
@click="removeVlanTrunk(trunk)"
|
||||
>
|
||||
{{ t('harvester.fields.remove') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
v-if="isL2VlanTrunkMode"
|
||||
type="button"
|
||||
class="btn btn-sm bg-primary mb-20"
|
||||
:disabled="isView"
|
||||
@click="addVlanTrunk"
|
||||
>
|
||||
{{ t('harvester.vlanStatus.vlanConfig.vlanTrunk.add') }}
|
||||
</button>
|
||||
</div>
|
||||
<LabeledInput
|
||||
v-if="isL2VlanNetwork"
|
||||
v-if="isL2VlanAccessMode"
|
||||
v-model:value.number="config.vlan"
|
||||
class="mb-20"
|
||||
required
|
||||
@@ -316,7 +465,7 @@ export default {
|
||||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
v-if="isL2VlanNetwork"
|
||||
v-if="isL2VlanAccessMode"
|
||||
name="layer3Network"
|
||||
:label="t('harvester.network.tabs.layer3Network')"
|
||||
:weight="98"
|
||||
@@ -375,3 +524,12 @@ export default {
|
||||
</Tabbed>
|
||||
</CruResource>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.remove-btn {
|
||||
align-self: center;
|
||||
}
|
||||
.trunk-span{
|
||||
flex: 5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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],
|
||||
@@ -267,7 +269,19 @@ export default {
|
||||
|
||||
rebuildStatus() {
|
||||
return this.value.longhornEngine?.status?.rebuildStatus;
|
||||
}
|
||||
},
|
||||
|
||||
isLHV2VolExpansionFeatureEnabled() {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('lhV2VolExpansion');
|
||||
},
|
||||
|
||||
isResizeDisabled() {
|
||||
return (
|
||||
!this.isLHV2VolExpansionFeatureEnabled &&
|
||||
this.value?.isLonghornV2 &&
|
||||
this.isEdit
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -463,7 +477,7 @@ export default {
|
||||
:output-modifier="true"
|
||||
:increment="1024"
|
||||
:mode="mode"
|
||||
:disabled="value?.isLonghornV2 && isEdit"
|
||||
:disabled="isResizeDisabled"
|
||||
required
|
||||
class="mb-20"
|
||||
:suffix="GIBIBYTE"
|
||||
@@ -471,7 +485,7 @@ export default {
|
||||
/>
|
||||
|
||||
<Banner
|
||||
v-if="value?.isLonghornV2 && isEdit"
|
||||
v-if="isResizeDisabled"
|
||||
color="warning"
|
||||
>
|
||||
<span>{{ t('harvester.volume.longhorn.disableResize') }}</span>
|
||||
@@ -581,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>
|
||||
|
||||
@@ -136,6 +136,10 @@ export default {
|
||||
pvcs() {
|
||||
return this.$store.getters['harvester/all'](PVC) || [];
|
||||
},
|
||||
|
||||
isLHV2VolExpansionFeatureEnabled() {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('lhV2VolExpansion');
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -270,8 +274,8 @@ export default {
|
||||
},
|
||||
|
||||
isResizeDisabled(volume) {
|
||||
if (this.isCreate) return false;
|
||||
if (volume.newCreateId) return false;
|
||||
if (this.isLHV2VolExpansionFeatureEnabled) return false;
|
||||
if (this.isCreate || volume.newCreateId) return false;
|
||||
|
||||
const isStopped = this.vm.stateDisplay === OFF;
|
||||
const isLonghornV2 = this.isLonghornV2(volume);
|
||||
@@ -406,7 +410,7 @@ export default {
|
||||
:label="ucFirst(value.volumeBackups.error.message)"
|
||||
/>
|
||||
<Banner
|
||||
v-if="isLonghornV2(volume) && !isView"
|
||||
v-if="!isLHV2VolExpansionFeatureEnabled && isLonghornV2(volume) && !isView"
|
||||
color="warning"
|
||||
:label="t('harvester.volume.longhorn.disableResize')"
|
||||
/>
|
||||
|
||||
@@ -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
|
||||
@@ -1482,6 +1483,14 @@ harvester:
|
||||
vlanStatus:
|
||||
vlanConfig:
|
||||
label: Network Configuration
|
||||
l2VlanMode:
|
||||
access: Access
|
||||
trunk: Trunk
|
||||
label: Mode
|
||||
vlanTrunk:
|
||||
minId: Minimum VLAN ID
|
||||
maxId: Maximum VLAN ID
|
||||
add: Add VLAN Trunk
|
||||
|
||||
clusterNetwork:
|
||||
title: Cluster Network Configuration
|
||||
|
||||
@@ -2,7 +2,7 @@ import SteveModel from '@shell/plugins/steve/steve-class';
|
||||
import { HCI } from '@shell/config/labels-annotations';
|
||||
import { NETWORK_TYPE } from '../config/types';
|
||||
|
||||
const { UNTAGGED, OVERLAY } = NETWORK_TYPE;
|
||||
const { UNTAGGED, OVERLAY, L2TRUNK_VLAN } = NETWORK_TYPE;
|
||||
|
||||
export default class NetworkAttachmentDef extends SteveModel {
|
||||
applyDefaults() {
|
||||
@@ -45,7 +45,7 @@ export default class NetworkAttachmentDef extends SteveModel {
|
||||
}
|
||||
|
||||
get vlanId() {
|
||||
return this.vlanType === UNTAGGED || this.vlanType === OVERLAY ? 'N/A' : this.parseConfig.vlan;
|
||||
return this.vlanType === UNTAGGED || this.vlanType === OVERLAY || this.vlanType === L2TRUNK_VLAN ? 'N/A' : this.parseConfig.vlan;
|
||||
}
|
||||
|
||||
get customValidationRules() {
|
||||
@@ -68,7 +68,7 @@ export default class NetworkAttachmentDef extends SteveModel {
|
||||
const route = annotations[HCI.NETWORK_ROUTE];
|
||||
let config = {};
|
||||
|
||||
if (this.vlanType === UNTAGGED || this.vlanType === OVERLAY) {
|
||||
if (this.vlanType === UNTAGGED || this.vlanType === OVERLAY || this.vlanType === L2TRUNK_VLAN) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"annotations": {
|
||||
"catalog.cattle.io/display-name": "Harvester",
|
||||
"catalog.cattle.io/kube-version": ">= 1.16.0-0",
|
||||
"catalog.cattle.io/rancher-version": ">= 2.12.0-0",
|
||||
"catalog.cattle.io/rancher-version": ">= 2.13.0-0",
|
||||
"catalog.cattle.io/ui-extensions-version": ">= 3.0.0 < 4.0.0"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -215,19 +215,18 @@ export default {
|
||||
if (this.imageSource === IMAGE_METHOD.NEW) {
|
||||
this.imageValue.metadata.annotations[HCI_ANNOTATIONS.OS_UPGRADE_IMAGE] = 'True';
|
||||
|
||||
if (this.sourceType === UPLOAD && this.uploadImageId !== '') {
|
||||
if (this.sourceType === UPLOAD && this.uploadImageId !== '') { // upload new image
|
||||
this.value.spec.image = this.uploadImageId;
|
||||
} else if (this.sourceType === DOWNLOAD) {
|
||||
} else if (this.sourceType === DOWNLOAD) { // give URL to download new image
|
||||
this.imageValue.spec.sourceType = DOWNLOAD;
|
||||
|
||||
if (!this.imageValue.spec.url) {
|
||||
this.errors.push(this.$store.getters['i18n/t']('harvester.setting.upgrade.imageUrl'));
|
||||
buttonCb(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
res = await this.imageValue.save();
|
||||
|
||||
this.value.spec.image = res.id;
|
||||
}
|
||||
} else if (this.imageSource === IMAGE_METHOD.EXIST) {
|
||||
@@ -247,7 +246,6 @@ export default {
|
||||
if (this.skipSingleReplicaDetachedVolFeatureEnabled) {
|
||||
this.value.metadata.annotations = { [HCI_ANNOTATIONS.SKIP_SINGLE_REPLICA_DETACHED_VOL]: JSON.stringify(this.skipSingleReplicaDetachedVol) };
|
||||
}
|
||||
|
||||
await this.value.save();
|
||||
this.done();
|
||||
buttonCb(true);
|
||||
@@ -334,6 +332,22 @@ export default {
|
||||
},
|
||||
|
||||
watch: {
|
||||
async sourceType(neu) {
|
||||
if (neu === DOWNLOAD && this.imageValue && this.uploadController) {
|
||||
if (this.uploadController) {
|
||||
this.uploadController.abort();
|
||||
}
|
||||
|
||||
try {
|
||||
await this.imageValue.remove();
|
||||
await this.initImageValue();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error occurred while removing imageValue:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
imageSource(neu) {
|
||||
if (neu !== IMAGE_METHOD.DELETE) {
|
||||
this.deleteImageId = '';
|
||||
|
||||
@@ -3114,9 +3114,9 @@
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*", "@types/node@20.10.8", "@types/node@^14.14.31", "@types/node@~20.19.0":
|
||||
version "20.19.19"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.19.tgz#18c8982becd5728f92e5f1939f2f3dc85604abcd"
|
||||
integrity sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg==
|
||||
version "20.19.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.22.tgz#f17e80ee1d1fdd10d50bef449abe23bfc0216780"
|
||||
integrity sha512-hRnu+5qggKDSyWHlnmThnUqg62l29Aj/6vcYgUaSFL9oc7DVjeWEQN3PRgdSc6F8d9QRMWkf36CLMch1Do/+RQ==
|
||||
dependencies:
|
||||
undici-types "~6.21.0"
|
||||
|
||||
@@ -11525,10 +11525,10 @@ semver-compare@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||
integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@7.7.2, semver@^5.5.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.6.0, semver@^7.6.3, semver@~7.0.0:
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
|
||||
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
|
||||
"semver@2 || 3 || 4 || 5", semver@7.7.3, semver@^5.5.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.6.0, semver@^7.6.3, semver@~7.0.0:
|
||||
version "7.7.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
|
||||
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
||||
|
||||
send@0.17.1:
|
||||
version "0.17.1"
|
||||
|
||||
Reference in New Issue
Block a user