This is an automated email from the ASF dual-hosted git repository.

RosiKyu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 492682697a7 UI: fix clone offering forms silently reverting user edits 
on services (#14062)
492682697a7 is described below

commit 492682697a7438bb6a7af59cca39a02071b3b716
Author: Pearl Dsilva <[email protected]>
AuthorDate: Wed Nov 11 06:47:24 2026 -0500

    UI: fix clone offering forms silently reverting user edits on services 
(#14062)
    
    * UI: fix clone offering forms silently reverting user edits on services
    
    * add support for conserve mode
    
    * ui: prefill conserve mode in clone VPC offering form
---
 .../java/com/cloud/network/vpc/VpcManagerImpl.java |  5 ++
 ui/src/views/offering/CloneNetworkOffering.vue     | 11 ++--
 ui/src/views/offering/CloneVpcOffering.vue         | 68 ++++++++++++++++++----
 3 files changed, 68 insertions(+), 16 deletions(-)

diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java 
b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index 1f5fe7382fc..41f2d3fe539 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -1081,6 +1081,11 @@ public class VpcManagerImpl extends ManagerBase 
implements VpcManager, VpcProvis
                 ConfigurationManagerImpl.setField(cmd, "specifyAsNumber", 
sourceOffering.isSpecifyAsNumber());
             }
 
+            Boolean conserveModeFieldValue = getRawFieldValue(cmd, 
"conserveMode", Boolean.class);
+            if (conserveModeFieldValue == null) {
+                ConfigurationManagerImpl.setField(cmd, "conserveMode", 
sourceOffering.isConserveMode());
+            }
+
             if (cmd.getInternetProtocol() == null) {
                 String internetProtocol = 
vpcOfferingDetailsDao.getDetail(sourceOffering.getId(), 
ApiConstants.INTERNET_PROTOCOL);
                 if (internetProtocol != null) {
diff --git a/ui/src/views/offering/CloneNetworkOffering.vue 
b/ui/src/views/offering/CloneNetworkOffering.vue
index 0cd5e733b24..4b4343a823d 100644
--- a/ui/src/views/offering/CloneNetworkOffering.vue
+++ b/ui/src/views/offering/CloneNetworkOffering.vue
@@ -1228,12 +1228,7 @@ export default {
     },
     handleSupportedServiceChange (service, checked, provider) {
       if (checked) {
-        const correctProvider = this.serviceProviderMap[service]
-        if (correctProvider && provider !== correctProvider) {
-          this.selectedServiceProviderMap[service] = correctProvider
-        } else {
-          this.selectedServiceProviderMap[service] = provider
-        }
+        this.selectedServiceProviderMap[service] = provider
       } else {
         delete this.selectedServiceProviderMap[service]
       }
@@ -1294,6 +1289,10 @@ export default {
           params.guestiptype = values.guestiptype
         }
 
+        if (values.forvpc === true) {
+          params.forvpc = true
+        }
+
         // Use composable for service capability params
         if (this.selectedServiceProviderMap != null) {
           buildServiceCapabilityParams(params, values, 
this.selectedServiceProviderMap, this.registeredServicePackages)
diff --git a/ui/src/views/offering/CloneVpcOffering.vue 
b/ui/src/views/offering/CloneVpcOffering.vue
index 6adbfafad61..e4c013b12af 100644
--- a/ui/src/views/offering/CloneVpcOffering.vue
+++ b/ui/src/views/offering/CloneVpcOffering.vue
@@ -212,6 +212,12 @@
             </a-select-option>
           </a-select>
         </a-form-item>
+        <a-form-item name="conservemode" ref="conservemode">
+          <template #label>
+            <tooltip-label :title="$t('label.conservemode')" 
:tooltip="apiParams.conservemode.description"/>
+          </template>
+          <a-switch v-model:checked="form.conservemode" />
+        </a-form-item>
         <a-form-item name="ispublic" ref="ispublic" 
:label="$t('label.ispublic')" v-if="isAdmin()">
           <a-switch v-model:checked="form.ispublic" @change="val => { isPublic 
= val }" />
         </a-form-item>
@@ -308,7 +314,6 @@ export default {
     return {
       selectedDomains: [],
       selectedZones: [],
-      isConserveMode: true,
       internetProtocolValue: 'ipv4',
       domains: [],
       domainLoading: false,
@@ -405,7 +410,7 @@ export default {
       this.fetchZoneData()
       this.fetchIpv6NetworkOfferingConfiguration()
       this.fetchRoutedNetworkConfiguration()
-      this.fetchSupportedServiceData()
+      this.fetchSupportedServiceData(true)
     },
     isAdmin () {
       return isAdmin()
@@ -455,7 +460,7 @@ export default {
         this.zoneLoading = false
       })
     },
-    fetchSupportedServiceData () {
+    fetchSupportedServiceData (isInitialLoad = false) {
       this.supportedServiceLoading = true
       getAPI('listSupportedNetworkServices', {}).then(json => {
         const networkServices = 
json.listsupportednetworkservicesresponse.networkservice || []
@@ -491,7 +496,11 @@ export default {
         this.supportedServiceLoading = false
 
         this.$nextTick(() => {
-          this.populateFormFromResource()
+          if (isInitialLoad) {
+            this.populateFormFromResource()
+          } else {
+            this.syncServiceSelectionsForCurrentMode()
+          }
         })
       })
     },
@@ -541,6 +550,7 @@ export default {
       const r = this.resource
       this.form.name = r.name + ' - Clone'
       this.form.displaytext = r.displaytext || r.name
+      if (r.conservemode !== undefined) this.form.conservemode = r.conservemode
 
       if (r.internetprotocol) {
         this.form.internetprotocol = r.internetprotocol.toLowerCase()
@@ -656,6 +666,45 @@ export default {
         this.form.nsxsupportlb = Boolean(this.serviceProviderMap.Lb)
       }
     },
+    syncServiceSelectionsForCurrentMode () {
+      const updatedServices = this.supportedServices.map(svc => {
+        const serviceCopy = { ...svc, provider: [...svc.provider] }
+        const providerName = this.selectedServiceProviderMap[serviceCopy.name]
+
+        if (providerName) {
+          const providerIndex = serviceCopy.provider.findIndex(p => p.name === 
providerName)
+          if (providerIndex > 0) {
+            const targetProvider = serviceCopy.provider[providerIndex]
+            serviceCopy.provider.splice(providerIndex, 1)
+            serviceCopy.provider.unshift(targetProvider)
+          }
+          serviceCopy.defaultChecked = true
+          serviceCopy.selectedProvider = providerName
+        } else {
+          serviceCopy.defaultChecked = false
+          serviceCopy.selectedProvider = null
+        }
+        return serviceCopy
+      })
+      this.supportedServices = updatedServices
+
+      const availableNames = new Set(updatedServices.map(svc => svc.name))
+      Object.keys(this.selectedServiceProviderMap).forEach(name => {
+        if (!availableNames.has(name)) {
+          delete this.selectedServiceProviderMap[name]
+        }
+      })
+
+      this.connectivityServiceChecked = 
Boolean(this.selectedServiceProviderMap.Connectivity)
+      this.sourceNatServiceChecked = 
Boolean(this.selectedServiceProviderMap.SourceNat)
+
+      this.$nextTick(() => {
+        this.servicesReady = true
+        this.$nextTick(() => {
+          this.checkVpcVirtualRouterForServices()
+        })
+      })
+    },
     async handleProviderChange (value) {
       this.provider = value
       if (this.provider === 'NSX') {
@@ -682,12 +731,7 @@ export default {
     },
     handleSupportedServiceChange (service, checked, provider) {
       if (checked) {
-        const correctProvider = this.serviceProviderMap[service]
-        if (correctProvider && provider !== correctProvider) {
-          this.selectedServiceProviderMap[service] = correctProvider
-        } else {
-          this.selectedServiceProviderMap[service] = provider
-        }
+        this.selectedServiceProviderMap[service] = provider
       } else {
         delete this.selectedServiceProviderMap[service]
       }
@@ -818,6 +862,10 @@ export default {
           params.enable = values.enable
         }
 
+        if (values.conservemode !== undefined) {
+          params.conservemode = values.conservemode
+        }
+
         this.loading = true
         postAPI('cloneVPCOffering', params).then(json => {
           
this.$message.success(`${this.$t('message.success.clone.vpc.offering')} 
${values.name}`)

Reply via email to