diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 091116d2bba0..6549908b7911 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -3785,6 +3785,8 @@ private DiskOfferingVO createDiskOfferingInternal(final String name, final Strin diskOffering.setMinIops(minIops); diskOffering.setMaxIops(maxIops); diskOffering.setEncrypt(encrypt); + diskOffering.setShareable(shareable); + diskOffering.setKvdoEnable(kvdoEnable); setBytesRate(diskOffering, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength); setIopsRate(diskOffering, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength); diff --git a/ui/src/components/offering/ComputeOfferingForm.vue b/ui/src/components/offering/ComputeOfferingForm.vue index c621324f108e..9b4b9612bf71 100644 --- a/ui/src/components/offering/ComputeOfferingForm.vue +++ b/ui/src/components/offering/ComputeOfferingForm.vue @@ -366,11 +366,11 @@ - { computeonly = val }"/> + - + @@ -390,14 +390,28 @@ {{ $t('label.provisioningtype.fat') }} - + + + + + + + + + + + + + + + { handleCacheModeChange(selected.target.value) }"> - {{ $t('label.nodiskcache') }} {{ $t('label.writeback') }} {{ $t('label.writethrough') }} + {{ $t('label.nodiskcache') }} {{ $t('label.hypervisor.default') }} @@ -517,7 +531,7 @@ { encryptdisk = val }" /> - + {{ $t('label.add.disk.offering') }} 0) + if (this.form.deploymentplanner) { + this.handleDeploymentPlannerChange(this.form.deploymentplanner) + } + }, fetchData () { this.fetchDomainData() this.fetchZoneData() @@ -714,12 +761,18 @@ export default { this.diskOfferingLoading = true getAPI('listDiskOfferings', { listall: true }).then(json => { this.diskOfferings = json.listdiskofferingsresponse.diskoffering || [] - if (this.selectedDiskOfferingId === '') { + if (!this.form.diskofferingid) { this.selectedDiskOfferingId = this.diskOfferings[0]?.id || '' + this.form.diskofferingid = this.selectedDiskOfferingId } }).finally(() => { this.diskOfferingLoading = false }) }, - updateSelectedDiskOffering (id) { if (id) this.selectedDiskOfferingId = id }, + updateSelectedDiskOffering (id) { + if (id) { + this.selectedDiskOfferingId = id + this.form.diskofferingid = id + } + }, closeDiskOfferingModal () { this.fetchDiskOfferings(); this.showDiskOfferingModal = false }, isDomainAdmin () { return ['DomainAdmin'].includes(this.$store.getters.userInfo.roletype) @@ -771,7 +824,9 @@ export default { const planners = json.listdeploymentplannersresponse.deploymentPlanner this.deploymentPlanners = this.deploymentPlanners.concat(planners) this.deploymentPlanners.unshift({ name: '' }) - this.form.deploymentplanner = this.deploymentPlanners.length > 0 ? this.deploymentPlanners[0].name : '' + if (!this.form.deploymentplanner) { + this.form.deploymentplanner = this.deploymentPlanners.length > 0 ? this.deploymentPlanners[0].name : '' + } }).finally(() => { this.deploymentPlannerLoading = false }) }, fetchvSphereStoragePolicies (zoneIndex) { diff --git a/ui/src/components/offering/DiskOfferingForm.vue b/ui/src/components/offering/DiskOfferingForm.vue index f3f39647fefa..85a748a42d2d 100644 --- a/ui/src/components/offering/DiskOfferingForm.vue +++ b/ui/src/components/offering/DiskOfferingForm.vue @@ -76,13 +76,13 @@ - { encryptdisk = val }" /> + - { disksizestrictness = val }" /> + @@ -191,7 +191,19 @@ v-model:value="form.hypervisorsnapshotreserve" :placeholder="apiParams.hypervisorsnapshotreserve.description"/> - + + + + + + + + + + + + + @@ -199,15 +211,15 @@ v-model:value="form.writecachetype" buttonStyle="solid" @change="selected => { handleWriteCacheTypeChange(selected.target.value) }"> - - {{ $t('label.nodiskcache') }} - {{ $t('label.writeback') }} {{ $t('label.writethrough') }} + + {{ $t('label.nodiskcache') }} + {{ $t('label.hypervisor.default') }} @@ -235,9 +247,9 @@ - { isPublic = val }" /> + - + @@ -349,11 +361,13 @@ export default { storagetype: 'shared', provisioningtype: 'thin', customdisksize: true, - writecachetype: 'none', + writecachetype: 'writeback', qostype: '', ispublic: true, disksizestrictness: false, - encryptdisk: false + encryptdisk: false, + kvdoenable: false, + shareable: false }, this.initialValues || {})), rules: reactive({}), storageTags: [], @@ -364,17 +378,25 @@ export default { domainLoading: false, zones: [], zoneLoading: false, - disksizestrictness: false, - encryptdisk: false, isDomainAdminAllowedToInformTags: false } }, + watch: { + initialValues: { + deep: true, + handler (values) { + this.applyInitialValues(values) + } + } + }, created () { this.zones = [{ id: null, name: this.$t('label.all.zone') }] this.initForm() + this.applyInitialValues(this.initialValues) this.fetchData() - this.isPublic = isAdmin() - this.form.ispublic = this.isPublic + if (this.form.ispublic === undefined) { + this.form.ispublic = isAdmin() + } }, methods: { initForm () { @@ -406,6 +428,40 @@ export default { }] }) }, + applyInitialValues (values) { + if (!values) { + return + } + Object.assign(this.form, values) + this.normalizeQosType() + this.syncUiStateFromForm() + }, + normalizeQosType () { + const hasHypervisorQos = [ + this.form.diskbytesreadrate, + this.form.diskbytesreadratemax, + this.form.diskbyteswriterate, + this.form.diskbyteswriteratemax, + this.form.diskiopsreadrate, + this.form.diskiopswriterate + ].some(value => value !== undefined && value !== null && value !== '') + const hasStorageQos = [ + this.form.diskiopsmin, + this.form.diskiopsmax, + this.form.hypervisorsnapshotreserve + ].some(value => value !== undefined && value !== null && value !== '') || this.form.iscustomizeddiskiops === true + + if (hasHypervisorQos) { + this.form.qostype = 'hypervisor' + } else if (hasStorageQos) { + this.form.qostype = 'storage' + } + }, + syncUiStateFromForm () { + this.isPublic = this.form.ispublic !== false + this.normalizeDomainSelection() + this.normalizeZoneSelection() + }, fetchData () { this.fetchDomainData() this.fetchZoneData() @@ -440,6 +496,7 @@ export default { getAPI('listDomains', params).then(json => { const listDomains = json.listdomainsresponse.domain this.domains = this.domains.concat(listDomains) + this.normalizeDomainSelection() }).finally(() => { this.domainLoading = false }) @@ -453,6 +510,7 @@ export default { if (listZones) { this.zones = this.zones.concat(listZones) } + this.normalizeZoneSelection() }).finally(() => { this.zoneLoading = false }) @@ -486,6 +544,35 @@ export default { }) } }, + normalizeDomainSelection () { + if (!Array.isArray(this.form.domainid) || this.form.domainid.length === 0 || this.domains.length === 0) { + return + } + if (typeof this.form.domainid[0] === 'number') { + return + } + const domainIndexes = this.form.domainid.map(domainId => { + return this.domains.findIndex(domain => domain.id === domainId) + }).filter(index => index >= 0) + if (domainIndexes.length > 0) { + this.form.domainid = domainIndexes + } + }, + normalizeZoneSelection () { + if (!Array.isArray(this.form.zoneid) || this.form.zoneid.length === 0 || this.zones.length === 0) { + return + } + if (typeof this.form.zoneid[0] === 'number') { + return + } + const zoneIndexes = this.form.zoneid.map(zoneId => { + return this.zones.findIndex(zone => zone.id === zoneId) + }).filter(index => index >= 0) + if (zoneIndexes.length > 0) { + this.form.zoneid = zoneIndexes + this.fetchvSphereStoragePolicies(zoneIndexes[0]) + } + }, validate () { return this.$refs.internalFormRef.validate().then(() => { const formRaw = toRaw(this.form) diff --git a/ui/src/views/offering/AddComputeOffering.vue b/ui/src/views/offering/AddComputeOffering.vue index 2a687bb6500a..50764a45f697 100644 --- a/ui/src/views/offering/AddComputeOffering.vue +++ b/ui/src/views/offering/AddComputeOffering.vue @@ -18,664 +18,22 @@ - - - - - - - - - - - - - - - - - - - {{ $t('label.domain.router') }} - {{ $t('label.console.proxy') }} - {{ $t('label.secondary.storage.vm') }} - - - - { handleComputeOfferingTypeChange(selected.target.value) }" - buttonStyle="solid"> - - {{ $t('label.fixed') }} - - - {{ $t('label.customconstrained') }} - - - {{ $t('label.customunconstrained') }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { dynamicscalingenabled = val }"/> - - - - - - - - - - - - - - - - - - - - - - - - - - { handleDeploymentPlannerChange(val) }"> - - {{ opt.name || opt.description }} - - - - - - - {{ $t('label.none') }} - - - {{ $t('label.strict') }} - - - {{ $t('label.preferred') }} - - - - - - - {{ opt.description || opt.name || '' }} - - - - - - - {{ vgpu.name }} {{ getVgpuProfileDetails(vgpu) }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ opt.path || opt.name || opt.description }} - - - - - - - - - fetchvSphereStoragePolicies(val)" - :loading="zoneLoading" - :placeholder="apiParams.zoneid.description"> - - - - - {{ opt.name || opt.description }} - - - - - - - - - - - {{ policy.name || policy.id }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { computeonly = val }"/> - - - - - - - - { handleStorageTypeChange(selected.target.value) }"> - - {{ $t('label.shared') }} - - - {{ $t('label.local') }} - - - - - - - - { handleProvisioningTypeChange(selected.target.value) }"> - - {{ $t('label.provisioningtype.thin') }} - - - {{ $t('label.provisioningtype.sparse') }} - - - {{ $t('label.provisioningtype.fat') }} - - - - - - - - - - - - - - - - - - - - { handleCacheModeChange(selected.target.value) }"> - - {{ $t('label.nodiskcache') }} - - - {{ $t('label.writeback') }} - - - {{ $t('label.writethrough') }} - - - {{ $t('label.hypervisor.default') }} - - - - - { handleQosTypeChange(selected.target.value) }"> - - {{ $t('label.none') }} - - - {{ $t('label.hypervisor') }} - - - {{ $t('label.storage') }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { isCustomizedDiskIops = val }" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ opt }} - - - - - - - - - - { encryptdisk = val }" /> - - - - - {{ $t('label.add.disk.offering') }} - - updateSelectedDiskOffering($event)"/> - - - - - - {{ offering.displaytext || offering.name }} - - - - - - - - - - { diskofferingstrictness = val }"/> - - - - - - - - - {{ $t('message.add.orchestrator.resource.details') }} - - - - - - - {{ $t('label.cancel') }} - {{ $t('label.ok') }} - + :apiParams="apiParams" + :isSystem="isSystem" + :isAdmin="isAdmin" + :ref="formRef" + @submit="handleSubmit"> + + + + {{ $t('label.cancel') }} + {{ $t('label.ok') }} + + + @@ -720,7 +78,7 @@ export default { }, storageType: 'shared', provisioningType: 'thin', - cacheMode: 'none', + cacheMode: 'writeback', offeringType: 'fixed', isCustomizedDiskIops: false, isPublic: true, @@ -890,10 +248,8 @@ export default { }, fetchGPUCards () { this.gpuCardLoading = true - getAPI('listGpuCards', { - }).then(json => { + getAPI('listGpuCards', {}).then(json => { this.gpuCards = json.listgpucardsresponse.gpucard || [] - // Add a "None" option at the beginning this.gpuCards.unshift({ id: '', name: this.$t('label.none') @@ -909,7 +265,7 @@ export default { }).then(json => { this.diskOfferings = json.listdiskofferingsresponse.diskoffering || [] if (this.selectedDiskOfferingId === '') { - this.selectedDiskOfferingId = this.diskOfferings[0].id || '' + this.selectedDiskOfferingId = this.diskOfferings?.[0]?.id || '' } }).finally(() => { this.diskOfferingLoading = false @@ -985,7 +341,7 @@ export default { if (this.loading) return this.formRef.value.validate().then((values) => { - var params = { + const params = { issystem: this.isSystem, name: values.name, displaytext: values.displaytext, @@ -1048,7 +404,7 @@ export default { params.rootdisksize = values.rootdisksize } if (values.qostype === 'storage') { - var customIops = values.iscustomizeddiskiops === true + const customIops = values.iscustomizeddiskiops === true params.customizediops = customIops if (!customIops) { if (values.diskiopsmin != null && values.diskiopsmin.length > 0) { @@ -1077,8 +433,7 @@ export default { } } if (values.storagetags != null && values.storagetags.length > 0) { - var tags = values.storagetags.join(',') - params.tags = tags + params.tags = values.storagetags.join(',') } if (values.hosttags != null && values.hosttags.length > 0) { params.hosttags = values.hosttags @@ -1112,12 +467,12 @@ export default { } if (values.ispublic !== true) { - var domainIndexes = values.domainid - var domainId = null + const domainIndexes = values.domainid + let domainId = null if (domainIndexes && domainIndexes.length > 0) { - var domainIds = [] - for (var i = 0; i < domainIndexes.length; i++) { - domainIds = domainIds.concat(this.domains[domainIndexes[i]].id) + const domainIds = [] + for (let i = 0; i < domainIndexes.length; i++) { + domainIds.push(this.domains[domainIndexes[i]].id) } domainId = domainIds.join(',') } @@ -1125,12 +480,12 @@ export default { params.domainid = domainId } } - var zoneIndexes = values.zoneid - var zoneId = null + const zoneIndexes = values.zoneid + let zoneId = null if (zoneIndexes && zoneIndexes.length > 0) { - var zoneIds = [] - for (var j = 0; j < zoneIndexes.length; j++) { - zoneIds = zoneIds.concat(this.zones[zoneIndexes[j]].id) + const zoneIds = [] + for (let j = 0; j < zoneIndexes.length; j++) { + zoneIds.push(this.zones[zoneIndexes[j]].id) } zoneId = zoneIds.join(',') } @@ -1146,6 +501,7 @@ export default { }) } + this.loading = true postAPI('createServiceOffering', params).then(json => { const message = this.isSystem ? `${this.$t('message.create.service.offering')}: ` diff --git a/ui/src/views/offering/AddDiskOffering.vue b/ui/src/views/offering/AddDiskOffering.vue index 0ebbef18058b..d8978733770e 100644 --- a/ui/src/views/offering/AddDiskOffering.vue +++ b/ui/src/views/offering/AddDiskOffering.vue @@ -18,331 +18,33 @@ - - - - - - - - - - - - - - - - - - - - {{ $t('label.shared') }} - - - {{ $t('label.local') }} - - - - - - - - - - {{ $t('label.provisioningtype.thin') }} - - - {{ $t('label.provisioningtype.sparse') }} - - - {{ $t('label.provisioningtype.fat') }} - - - - - - - - { encryptdisk = val }" /> - - - - - - { disksizestrictness = val }" /> - - - - - - - - - - - - - - - - - {{ $t('label.none') }} - - - {{ $t('label.hypervisor') }} - - - {{ $t('label.storage') }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { handleWriteCacheTypeChange(selected.target.value) }"> - - {{ $t('label.nodiskcache') }} - - - {{ $t('label.writeback') }} - - - {{ $t('label.writethrough') }} - - - {{ $t('label.hypervisor.default') }} - - - - - - - - - - {{ opt }} - - - - - { isPublic = val }" /> - - - - - - - - - - - {{ opt.path || opt.name || opt.description }} - - - - - - - - - fetchvSphereStoragePolicies(val)" - :loading="zoneLoading" - :placeholder="apiParams.zoneid.description"> - - - - - {{ opt.name || opt.description }} - - - - - - - - - - - {{ policy.name || policy.id }} - - - - - - {{ $t('label.cancel') }} - {{ $t('label.ok') }} - + + + + {{ $t('label.cancel') }} + {{ $t('label.ok') }} + + +