mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2025-12-13 13:11:43 +00:00
* Add OS upgrade features (#311) * Add failed and success banner after image uploaded Signed-off-by: Andy Lee <andy.lee@suse.com> * add delete image feature Signed-off-by: Andy Lee <andy.lee@suse.com> * add skip checking single-replica detached volumes checkbox Signed-off-by: Andy Lee <andy.lee@suse.com> * change delete image flow Signed-off-by: Andy Lee <andy.lee@suse.com> * Reuse ConfirmRelatedToRemoveDialog Signed-off-by: Andy Lee <andy.lee@suse.com> --------- Signed-off-by: Andy Lee <andy.lee@suse.com> (cherry picked from commit dbbad01b0f1f491bc64a54ae0d23ffe1774b357a) # Conflicts: # pkg/harvester/dialog/ConfirmRelatedToRemoveDialog.vue * resolve conflict Signed-off-by: Andy Lee <andy.lee@suse.com> --------- Signed-off-by: Andy Lee <andy.lee@suse.com> Co-authored-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
parent
01e3867da1
commit
965c7d9b72
@ -43,7 +43,7 @@ export default {
|
|||||||
{{ t('harvester.upgradePage.upgradeInfo.tip') }}
|
{{ t('harvester.upgradePage.upgradeInfo.tip') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="mb-5">
|
<p>
|
||||||
{{ t('harvester.upgradePage.upgradeInfo.moreNotes') }} <a
|
{{ t('harvester.upgradePage.upgradeInfo.moreNotes') }} <a
|
||||||
:href="releaseVersion"
|
:href="releaseVersion"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@ -42,8 +42,25 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapState('action-menu', ['modalData']),
|
...mapState('action-menu', ['modalData']),
|
||||||
|
|
||||||
warningMessageKey() {
|
title() {
|
||||||
return this.modalData.warningMessageKey;
|
return this.modalData.title || 'dialog.promptRemove.title';
|
||||||
|
},
|
||||||
|
|
||||||
|
formattedType() {
|
||||||
|
return this.type.toLowerCase();
|
||||||
|
},
|
||||||
|
|
||||||
|
warningMessage() {
|
||||||
|
if (this.modalData.warningMessage) return this.modalData.warningMessage;
|
||||||
|
|
||||||
|
const isPlural = this.type.endsWith('s');
|
||||||
|
const thisOrThese = isPlural ? 'these' : 'this';
|
||||||
|
const defaultMessage = this.t('dialog.promptRemove.warningMessage', {
|
||||||
|
type: this.formattedType,
|
||||||
|
thisOrThese,
|
||||||
|
});
|
||||||
|
|
||||||
|
return defaultMessage;
|
||||||
},
|
},
|
||||||
|
|
||||||
names() {
|
names() {
|
||||||
@ -70,6 +87,12 @@ export default {
|
|||||||
return this.resources[0].nameDisplay;
|
return this.resources[0].nameDisplay;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
needConfirmation() {
|
||||||
|
const { needConfirmation = true } = this.modalData ;
|
||||||
|
|
||||||
|
return needConfirmation === true;
|
||||||
|
},
|
||||||
|
|
||||||
plusMore() {
|
plusMore() {
|
||||||
const remaining = this.resources.length - this.names.length;
|
const remaining = this.resources.length - this.names.length;
|
||||||
|
|
||||||
@ -97,6 +120,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deleteDisabled() {
|
deleteDisabled() {
|
||||||
|
if (!this.needConfirmation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return this.confirmName !== this.nameToMatch;
|
return this.confirmName !== this.nameToMatch;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -147,21 +174,29 @@ export default {
|
|||||||
v-clean-html="t(warningMessageKey, { type, names: resourceNames }, true)"
|
v-clean-html="t(warningMessageKey, { type, names: resourceNames }, true)"
|
||||||
></span>
|
></span>
|
||||||
|
|
||||||
<div class="mt-10 mb-10">
|
<div
|
||||||
<span
|
v-if="needConfirmation"
|
||||||
v-clean-html="t('promptRemove.confirmName', { nameToMatch: escapeHtml(nameToMatch) }, true)"
|
class="mt-20"
|
||||||
></span>
|
>
|
||||||
</div>
|
<div class="mt-10 mb-10">
|
||||||
<div class="mb-10">
|
<span
|
||||||
<CopyToClipboardText :text="nameToMatch" />
|
v-clean-html="t('dialog.promptRemove.confirmName', {
|
||||||
</div>
|
type: formattedType,
|
||||||
<input
|
nameToMatch: escapeHtml(nameToMatch)
|
||||||
id="confirm"
|
}, true)"
|
||||||
v-model="confirmName"
|
></span>
|
||||||
type="text"
|
</div>
|
||||||
/>
|
<div class="mb-10">
|
||||||
<div class="text-info mt-20">
|
<CopyToClipboardText :text="nameToMatch" />
|
||||||
{{ protip }}
|
</div>
|
||||||
|
<input
|
||||||
|
id="confirm"
|
||||||
|
v-model="confirmName"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<div class="text-info mt-20">
|
||||||
|
{{ protip }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Banner
|
<Banner
|
||||||
v-for="(error, i) in errors"
|
v-for="(error, i) in errors"
|
||||||
|
|||||||
@ -290,7 +290,7 @@ export default {
|
|||||||
|
|
||||||
const res = await this.value.save();
|
const res = await this.value.save();
|
||||||
|
|
||||||
res.uploadImage(file);
|
await res.uploadImage(file);
|
||||||
|
|
||||||
buttonCb(true);
|
buttonCb(true);
|
||||||
this.done();
|
this.done();
|
||||||
|
|||||||
@ -61,6 +61,7 @@ harvester:
|
|||||||
tip: 'Upload an icon to replace the Harvester favicon in the browser tab. Max file size is 20KB'
|
tip: 'Upload an icon to replace the Harvester favicon in the browser tab. Max file size is 20KB'
|
||||||
productLabel: 'Harvester'
|
productLabel: 'Harvester'
|
||||||
modal:
|
modal:
|
||||||
|
|
||||||
backup:
|
backup:
|
||||||
success: 'Backup { backUpName } has been initiated.'
|
success: 'Backup { backUpName } has been initiated.'
|
||||||
addBackup: Add Backup
|
addBackup: Add Backup
|
||||||
@ -99,6 +100,9 @@ harvester:
|
|||||||
tip: Please enter a virtual machine name!
|
tip: Please enter a virtual machine name!
|
||||||
success: 'Virtual machine { name } cloned successfully.'
|
success: 'Virtual machine { name } cloned successfully.'
|
||||||
failed: 'Failed clone virtual machine!'
|
failed: 'Failed clone virtual machine!'
|
||||||
|
osImage:
|
||||||
|
title: Delete Image
|
||||||
|
message: Are you sure you want to delete the image { name } ?
|
||||||
downloadImage:
|
downloadImage:
|
||||||
title: Download Image
|
title: Download Image
|
||||||
banner: 'This action takes a while depending on the image size ({ size }). Please be patient.'
|
banner: 'This action takes a while depending on the image size ({ size }). Please be patient.'
|
||||||
@ -872,14 +876,18 @@ harvester:
|
|||||||
upgradeNode: Upgrading Node
|
upgradeNode: Upgrading Node
|
||||||
upgradeSysService: Upgrading System Service
|
upgradeSysService: Upgrading System Service
|
||||||
upgradeImage: Download Upgrade Image
|
upgradeImage: Download Upgrade Image
|
||||||
osUpgrade: OS Upgrade
|
osUpgrade: Cluster Upgrade
|
||||||
uploadNew: Upload New Image
|
uploadNew: Upload New Image
|
||||||
|
deleteHeader: Please select an image to delete.
|
||||||
|
deleteExisting: Delete Existing Image
|
||||||
selectExisting: Select Existing Image
|
selectExisting: Select Existing Image
|
||||||
createRepository: Creating Upgrade Repository
|
createRepository: Creating Upgrade Repository
|
||||||
succeeded: Succeeded
|
succeeded: Succeeded
|
||||||
releaseTip: Please read the upgrade documentation carefully. You can view details on the <a href="{url}" target="_blank">Harvester Release Notes</a>.
|
releaseTip: Please read the upgrade documentation carefully. You can view details on the <a href="{url}" target="_blank">Harvester Release Notes</a>.
|
||||||
checkReady: I have read and understood the upgrade instructions related to this Harvester version.
|
checkReady: I have read and understood the upgrade instructions related to this Harvester version.
|
||||||
pending: Pending
|
pending: Pending
|
||||||
|
upload:
|
||||||
|
duplicatedFile: The file you are trying to upload already exists.
|
||||||
repoInfo:
|
repoInfo:
|
||||||
upgradeStatus: Upgrade Status
|
upgradeStatus: Upgrade Status
|
||||||
os: OS
|
os: OS
|
||||||
@ -1086,6 +1094,11 @@ harvester:
|
|||||||
imageUrl: Please input a valid image URL.
|
imageUrl: Please input a valid image URL.
|
||||||
chooseFile: Please select to upload an image.
|
chooseFile: Please select to upload an image.
|
||||||
checksum: Checksum
|
checksum: Checksum
|
||||||
|
networkError: Unable to upload the image. Resolve network issues that may have occurred and try again.
|
||||||
|
cancelUpload: Cancelled the image upload.
|
||||||
|
uploadSuccess: "{name} uploaded successfully. Press Upgrade button to start the cluster upgrade process."
|
||||||
|
deleteImage: Please select an image to delete.
|
||||||
|
deleteSuccess: "{name} deleted successfully."
|
||||||
harvesterMonitoring:
|
harvesterMonitoring:
|
||||||
label: Harvester Monitoring
|
label: Harvester Monitoring
|
||||||
section:
|
section:
|
||||||
|
|||||||
@ -313,7 +313,7 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
try {
|
try {
|
||||||
this.$ctx.commit('harvester-common/uploadStart', this.metadata.name, { root: true });
|
this.$ctx.commit('harvester-common/uploadStart', this.metadata.name, { root: true });
|
||||||
|
|
||||||
await this.doAction('upload', formData, {
|
const result = await this.doAction('upload', formData, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
'File-Size': file.size,
|
'File-Size': file.size,
|
||||||
@ -321,15 +321,15 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
params: { size: file.size },
|
params: { size: file.size },
|
||||||
signal: opt.signal,
|
signal: opt.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$ctx.commit('harvester-common/uploadError', { name: this.name, message: err.message }, { root: true });
|
this.$ctx.commit('harvester-common/uploadError', { name: this.name, message: err.message }, { root: true });
|
||||||
|
|
||||||
this.$ctx.commit('harvester-common/uploadEnd', this.metadata.name, { root: true });
|
this.$ctx.commit('harvester-common/uploadEnd', this.metadata.name, { root: true });
|
||||||
|
throw err;
|
||||||
return Promise.reject(err);
|
} finally {
|
||||||
|
this.$ctx.commit('harvester-common/uploadEnd', this.metadata.name, { root: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$ctx.commit('harvester-common/uploadEnd', this.metadata.name, { root: true });
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,12 @@ import { HCI } from '../../../../types';
|
|||||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../../../config/harvester';
|
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../../../config/harvester';
|
||||||
import ImagePercentageBar from '@shell/components/formatter/ImagePercentageBar';
|
import ImagePercentageBar from '@shell/components/formatter/ImagePercentageBar';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
const IMAGE_METHOD = {
|
const IMAGE_METHOD = {
|
||||||
NEW: 'new',
|
NEW: 'new',
|
||||||
EXIST: 'exist'
|
EXIST: 'exist',
|
||||||
|
DELETE: 'delete'
|
||||||
};
|
};
|
||||||
|
|
||||||
const DOWNLOAD = 'download';
|
const DOWNLOAD = 'download';
|
||||||
@ -40,23 +42,8 @@ export default {
|
|||||||
spec: { image: '' }
|
spec: { image: '' }
|
||||||
});
|
});
|
||||||
|
|
||||||
const imageValue = await this.$store.dispatch('harvester/create', {
|
await this.initImageValue();
|
||||||
type: HCI.IMAGE,
|
|
||||||
metadata: {
|
|
||||||
name: '',
|
|
||||||
namespace: 'harvester-system',
|
|
||||||
generateName: 'image-',
|
|
||||||
annotations: {}
|
|
||||||
},
|
|
||||||
spec: {
|
|
||||||
sourceType: UPLOAD,
|
|
||||||
displayName: '',
|
|
||||||
checksum: ''
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.imageValue = imageValue;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
@ -67,17 +54,20 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
value: null,
|
value: null,
|
||||||
file: {},
|
file: {},
|
||||||
uploadImageId: '',
|
uploadImageId: '',
|
||||||
imageId: '',
|
imageId: '',
|
||||||
imageSource: IMAGE_METHOD.NEW,
|
deleteImageId: '',
|
||||||
sourceType: UPLOAD,
|
imageSource: IMAGE_METHOD.NEW,
|
||||||
uploadController: null,
|
sourceType: UPLOAD,
|
||||||
imageValue: null,
|
uploadController: null,
|
||||||
errors: [],
|
uploadResult: null,
|
||||||
enableLogging: true,
|
imageValue: null,
|
||||||
IMAGE_METHOD
|
enableLogging: true,
|
||||||
|
IMAGE_METHOD,
|
||||||
|
skipSingleReplicaDetachedVol: false,
|
||||||
|
errors: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -86,22 +76,45 @@ export default {
|
|||||||
return `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
return `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
||||||
},
|
},
|
||||||
|
|
||||||
osImageOptions() {
|
skipSingleReplicaDetachedVolFeatureEnabled() {
|
||||||
return this.$store.getters['harvester/all'](HCI.IMAGE)
|
return this.$store.getters['harvester-common/getFeatureEnabled']('skipSingleReplicaDetachedVol');
|
||||||
.filter((I) => I.isOSImage)
|
|
||||||
.map((I) => {
|
|
||||||
return {
|
|
||||||
label: I.spec.displayName,
|
|
||||||
value: I.id,
|
|
||||||
disabled: !I.isReady
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
uploadImage() {
|
allOSImages() {
|
||||||
|
return this.$store.getters['harvester/all'](HCI.IMAGE).filter((I) => I.isOSImage) || [];
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteOSImageOptions() {
|
||||||
|
return this.allOSImages.map((I) => {
|
||||||
|
return {
|
||||||
|
label: I.spec.displayName,
|
||||||
|
value: I.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
osImageOptions() {
|
||||||
|
return this.allOSImages.map((I) => {
|
||||||
|
return {
|
||||||
|
label: I.spec.displayName,
|
||||||
|
value: I.id,
|
||||||
|
disabled: !I.isReady
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
createNewImage() {
|
||||||
return this.imageSource === IMAGE_METHOD.NEW;
|
return this.imageSource === IMAGE_METHOD.NEW;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
selectExistImage() {
|
||||||
|
return this.imageSource === IMAGE_METHOD.EXIST;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteExistImage() {
|
||||||
|
return this.imageSource === IMAGE_METHOD.DELETE;
|
||||||
|
},
|
||||||
|
|
||||||
fileName() {
|
fileName() {
|
||||||
return this.file?.name || '';
|
return this.file?.name || '';
|
||||||
},
|
},
|
||||||
@ -116,7 +129,11 @@ export default {
|
|||||||
return image?.status?.progress;
|
return image?.status?.progress;
|
||||||
},
|
},
|
||||||
|
|
||||||
enableSave() {
|
enableUpgrade() {
|
||||||
|
if (this.deleteExistImage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.sourceType === DOWNLOAD) {
|
if (this.sourceType === DOWNLOAD) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -128,16 +145,28 @@ export default {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
showProgressBar() {
|
isUploading() {
|
||||||
return this.sourceType === UPLOAD && this.fileName !== '';
|
|
||||||
},
|
|
||||||
|
|
||||||
showUploadingWarningBanner() {
|
|
||||||
return this.fileName !== '' && this.uploadProgress !== 100;
|
return this.fileName !== '' && this.uploadProgress !== 100;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showProgressBar() {
|
||||||
|
return this.createNewImage && this.sourceType === UPLOAD && this.isUploading;
|
||||||
|
},
|
||||||
|
|
||||||
|
showUploadSuccessBanner() {
|
||||||
|
return this.createNewImage && this.fileName !== '' && isEmpty(this.errors) && !this.showUploadingWarningBanner && this.uploadResult?._status === 200;
|
||||||
|
},
|
||||||
|
|
||||||
|
showUploadingWarningBanner() {
|
||||||
|
return this.createNewImage && this.isUploading;
|
||||||
|
},
|
||||||
|
|
||||||
|
showUpgradeOptions() {
|
||||||
|
return this.createNewImage || this.selectExistImage;
|
||||||
|
},
|
||||||
|
|
||||||
disableUploadButton() {
|
disableUploadButton() {
|
||||||
return this.sourceType === UPLOAD && this.fileName !== '' && this.uploadProgress !== 100;
|
return this.sourceType === UPLOAD && this.isUploading;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -152,11 +181,30 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async initImageValue() {
|
||||||
|
this.imageValue = await this.$store.dispatch('harvester/create', {
|
||||||
|
type: HCI.IMAGE,
|
||||||
|
metadata: {
|
||||||
|
name: '',
|
||||||
|
namespace: 'harvester-system',
|
||||||
|
generateName: 'image-',
|
||||||
|
annotations: {}
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
sourceType: UPLOAD,
|
||||||
|
displayName: '',
|
||||||
|
checksum: ''
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
async save(buttonCb) {
|
async save(buttonCb) {
|
||||||
let res = null;
|
let res = null;
|
||||||
|
|
||||||
|
this.file = {};
|
||||||
this.errors = [];
|
this.errors = [];
|
||||||
if (!this.imageValue.spec.displayName && this.uploadImage) {
|
|
||||||
|
if (!this.imageValue.spec.displayName && this.createNewImage) {
|
||||||
this.errors.push(this.$store.getters['i18n/t']('validation.required', { key: this.t('generic.name') }));
|
this.errors.push(this.$store.getters['i18n/t']('validation.required', { key: this.t('generic.name') }));
|
||||||
buttonCb(false);
|
buttonCb(false);
|
||||||
|
|
||||||
@ -164,6 +212,28 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (this.deleteExistImage) {
|
||||||
|
// if not select image, show error
|
||||||
|
if (!this.deleteImageId) {
|
||||||
|
this.errors.push(this.$store.getters['i18n/t']('harvester.setting.upgrade.deleteImage'));
|
||||||
|
buttonCb(false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if select image, delete image
|
||||||
|
const image = this.$store.getters['harvester/byId'](HCI.IMAGE, this.deleteImageId);
|
||||||
|
|
||||||
|
if (image) {
|
||||||
|
this.handleImageDelete(image);
|
||||||
|
buttonCb(true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.imageSource === IMAGE_METHOD.NEW) {
|
if (this.imageSource === IMAGE_METHOD.NEW) {
|
||||||
this.imageValue.metadata.annotations[HCI_ANNOTATIONS.OS_UPGRADE_IMAGE] = 'True';
|
this.imageValue.metadata.annotations[HCI_ANNOTATIONS.OS_UPGRADE_IMAGE] = 'True';
|
||||||
|
|
||||||
@ -194,12 +264,15 @@ export default {
|
|||||||
if (this.canEnableLogging) {
|
if (this.canEnableLogging) {
|
||||||
this.value.spec.logEnabled = this.enableLogging;
|
this.value.spec.logEnabled = this.enableLogging;
|
||||||
}
|
}
|
||||||
|
if (this.skipSingleReplicaDetachedVolFeatureEnabled) {
|
||||||
|
this.value.metadata.annotations = { [HCI_ANNOTATIONS.SKIP_SINGLE_REPLICA_DETACHED_VOL]: JSON.stringify(this.skipSingleReplicaDetachedVol) };
|
||||||
|
}
|
||||||
|
|
||||||
await this.value.save();
|
await this.value.save();
|
||||||
this.done();
|
this.done();
|
||||||
buttonCb(true);
|
buttonCb(true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errors = exceptionToErrorsArray(e);
|
this.errors = [e?.message] || exceptionToErrorsArray(e);
|
||||||
buttonCb(false);
|
buttonCb(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -207,36 +280,71 @@ export default {
|
|||||||
async uploadFile(file) {
|
async uploadFile(file) {
|
||||||
const fileName = file.name;
|
const fileName = file.name;
|
||||||
|
|
||||||
this.imageValue.spec.sourceType = UPLOAD;
|
|
||||||
this.imageValue.spec.displayName = fileName;
|
|
||||||
this.imageValue.metadata.annotations[HCI_ANNOTATIONS.OS_UPGRADE_IMAGE] = 'True';
|
|
||||||
|
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
this.errors.push(this.$store.getters['i18n/t']('harvester.setting.upgrade.unknownImageName'));
|
this.errors.push(this.$store.getters['i18n/t']('harvester.setting.upgrade.unknownImageName'));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const isDuplicatedFile = this.allOSImages.some((I) => I.spec.displayName === fileName);
|
||||||
|
|
||||||
this.imageValue.spec.url = '';
|
if (isDuplicatedFile) {
|
||||||
|
this.errors.push(this.$store.getters['i18n/t']('harvester.upgradePage.upload.duplicatedFile'));
|
||||||
|
this.file = {};
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.errors = [];
|
||||||
|
this.imageValue.spec.sourceType = UPLOAD;
|
||||||
|
this.imageValue.spec.displayName = fileName;
|
||||||
|
this.imageValue.metadata.annotations[HCI_ANNOTATIONS.OS_UPGRADE_IMAGE] = 'True';
|
||||||
this.imageValue.metadata.annotations[HCI_ANNOTATIONS.IMAGE_NAME] = fileName;
|
this.imageValue.metadata.annotations[HCI_ANNOTATIONS.IMAGE_NAME] = fileName;
|
||||||
|
this.imageValue.spec.url = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await this.imageValue.save();
|
const res = await this.imageValue.save();
|
||||||
|
|
||||||
this.uploadImageId = res.id;
|
this.uploadImageId = res.id;
|
||||||
this.uploadController = new AbortController();
|
this.uploadController = new AbortController();
|
||||||
|
|
||||||
const signal = this.uploadController.signal;
|
const signal = this.uploadController.signal;
|
||||||
|
|
||||||
await res.uploadImage(file, { signal });
|
this.uploadResult = await res.uploadImage(file, { signal });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errors = exceptionToErrorsArray(e);
|
if (e?.code === 'ERR_NETWORK') {
|
||||||
|
this.errors.push(this.$store.getters['i18n/t']('harvester.setting.upgrade.networkError'));
|
||||||
|
} else if (e?.code === 'ERR_CANCELED') {
|
||||||
|
this.errors.push(this.$store.getters['i18n/t']('harvester.setting.upgrade.cancelUpload'));
|
||||||
|
} else {
|
||||||
|
this.errors = [e?.message] || exceptionToErrorsArray(e);
|
||||||
|
}
|
||||||
|
this.file = {};
|
||||||
|
this.uploadImageId = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleImageDelete(imageId) {
|
||||||
|
const image = this.allOSImages.find((I) => I.id === imageId);
|
||||||
|
|
||||||
|
if (image) {
|
||||||
|
this.$store.dispatch('harvester/promptModal', {
|
||||||
|
resources: [image],
|
||||||
|
component: 'ConfirmRelatedToRemoveDialog',
|
||||||
|
needConfirmation: false,
|
||||||
|
warningMessage: this.$store.getters['i18n/t']('harvester.modal.osImage.message', { name: image.displayName })
|
||||||
|
});
|
||||||
|
this.deleteImageId = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async handleFileUpload() {
|
async handleFileUpload() {
|
||||||
this.file = this.$refs.file.files[0];
|
this.uploadImageId = '';
|
||||||
this.errors = [];
|
this.errors = [];
|
||||||
await this.uploadFile(this.file);
|
this.file = this.$refs.file?.files[0];
|
||||||
|
if (this.file) {
|
||||||
|
await this.initImageValue();
|
||||||
|
await this.uploadFile(this.file);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
selectFile() {
|
selectFile() {
|
||||||
@ -246,6 +354,12 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
imageSource(neu) {
|
||||||
|
if (neu !== IMAGE_METHOD.DELETE) {
|
||||||
|
this.deleteImageId = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
'imageValue.spec.url': {
|
'imageValue.spec.url': {
|
||||||
handler(neu) {
|
handler(neu) {
|
||||||
const suffixName = neu?.split('/')?.pop();
|
const suffixName = neu?.split('/')?.pop();
|
||||||
@ -283,10 +397,11 @@ export default {
|
|||||||
:errors="errors"
|
:errors="errors"
|
||||||
:can-yaml="false"
|
:can-yaml="false"
|
||||||
finish-button-mode="upgrade"
|
finish-button-mode="upgrade"
|
||||||
:validation-passed="enableSave"
|
:validation-passed="enableUpgrade"
|
||||||
:cancel-event="true"
|
:cancel-event="true"
|
||||||
@finish="save"
|
@finish="save"
|
||||||
@cancel="done"
|
@cancel="done"
|
||||||
|
@error="e=>errors = e"
|
||||||
>
|
>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-model:value="imageSource"
|
v-model:value="imageSource"
|
||||||
@ -295,20 +410,55 @@ export default {
|
|||||||
:options="[
|
:options="[
|
||||||
IMAGE_METHOD.NEW,
|
IMAGE_METHOD.NEW,
|
||||||
IMAGE_METHOD.EXIST,
|
IMAGE_METHOD.EXIST,
|
||||||
|
IMAGE_METHOD.DELETE,
|
||||||
]"
|
]"
|
||||||
:labels="[
|
:labels="[
|
||||||
t('harvester.upgradePage.uploadNew'),
|
t('harvester.upgradePage.uploadNew'),
|
||||||
t('harvester.upgradePage.selectExisting'),
|
t('harvester.upgradePage.selectExisting'),
|
||||||
|
t('harvester.upgradePage.deleteExisting'),
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<UpgradeInfo v-if="createNewImage || selectExistImage" />
|
||||||
|
|
||||||
|
<Banner
|
||||||
|
v-if="showUploadSuccessBanner"
|
||||||
|
color="success"
|
||||||
|
class="mt-0 mb-30"
|
||||||
|
:label="t('harvester.setting.upgrade.uploadSuccess', { name: file.name })"
|
||||||
|
/>
|
||||||
<Banner
|
<Banner
|
||||||
v-if="showUploadingWarningBanner"
|
v-if="showUploadingWarningBanner"
|
||||||
color="warning"
|
color="warning"
|
||||||
|
class="mt-0 mb-30"
|
||||||
:label="t('harvester.image.warning.osUpgrade.uploading', { name: file.name })"
|
:label="t('harvester.image.warning.osUpgrade.uploading', { name: file.name })"
|
||||||
/>
|
/>
|
||||||
<UpgradeInfo />
|
|
||||||
|
|
||||||
<div v-if="uploadImage">
|
<div
|
||||||
|
v-if="showUpgradeOptions"
|
||||||
|
class="mt-10 mb-10"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
v-if="canEnableLogging"
|
||||||
|
v-model:value="enableLogging"
|
||||||
|
class="check mb-20"
|
||||||
|
type="checkbox"
|
||||||
|
:label="t('harvester.upgradePage.enableLogging')"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="skipSingleReplicaDetachedVolFeatureEnabled"
|
||||||
|
class="mb-20"
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
v-model:value="skipSingleReplicaDetachedVol"
|
||||||
|
class="check"
|
||||||
|
type="checkbox"
|
||||||
|
:label="t('harvester.upgradePage.skipSingleReplicaDetachedVol')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="createNewImage">
|
||||||
<LabeledInput
|
<LabeledInput
|
||||||
v-model:value.trim="imageValue.spec.displayName"
|
v-model:value.trim="imageValue.spec.displayName"
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
@ -322,14 +472,6 @@ export default {
|
|||||||
label-key="harvester.setting.upgrade.checksum"
|
label-key="harvester.setting.upgrade.checksum"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Checkbox
|
|
||||||
v-if="canEnableLogging"
|
|
||||||
v-model:value="enableLogging"
|
|
||||||
class="check mb-20"
|
|
||||||
type="checkbox"
|
|
||||||
:label="t('harvester.upgradePage.enableLogging')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-model:value="sourceType"
|
v-model:value="sourceType"
|
||||||
class="mb-20 image-group"
|
class="mb-20 image-group"
|
||||||
@ -386,15 +528,33 @@ export default {
|
|||||||
:value="uploadProgress"
|
:value="uploadProgress"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
v-else
|
v-if="selectExistImage"
|
||||||
v-model:value="imageId"
|
v-model:value="imageId"
|
||||||
:options="osImageOptions"
|
:options="osImageOptions"
|
||||||
required
|
required
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
label-key="harvester.fields.image"
|
label-key="harvester.fields.image"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="deleteExistImage"
|
||||||
|
class="mt-20"
|
||||||
|
>
|
||||||
|
<Banner
|
||||||
|
color="info"
|
||||||
|
class="mt-10 mb-30"
|
||||||
|
:label="t('harvester.upgradePage.deleteHeader')"
|
||||||
|
/>
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="deleteImageId"
|
||||||
|
:options="deleteOSImageOptions"
|
||||||
|
required
|
||||||
|
class="mb-20"
|
||||||
|
label-key="harvester.fields.image"
|
||||||
|
@update:value="handleImageDelete"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</CruResource>
|
</CruResource>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user