@@ -8293,6 +8293,34 @@ public NetworkOffering cloneNetworkOffering(final CloneNetworkOfferingCmd cmd) {
82938293 return createNetworkOffering (cmd );
82948294 }
82958295
8296+ /**
8297+ * Converts service provider map from internal format to API parameter format.
8298+ *
8299+ * Internal format: Map<String, List<String>> where key=serviceName, value=list of provider names
8300+ * API parameter format: Map where each value is a HashMap with "service" and "provider" keys
8301+ *
8302+ * Example: {"Lb": ["VirtualRouter"]} becomes {0: {"service": "Lb", "provider": "VirtualRouter"}}
8303+ */
8304+ private Map <String , Map <String , String >> convertToApiParameterFormat (Map <String , List <String >> serviceProviderMap ) {
8305+ Map <String , Map <String , String >> apiFormatMap = new HashMap <>();
8306+ int index = 0 ;
8307+
8308+ for (Map .Entry <String , List <String >> entry : serviceProviderMap .entrySet ()) {
8309+ String serviceName = entry .getKey ();
8310+ List <String > providers = entry .getValue ();
8311+
8312+ for (String provider : providers ) {
8313+ Map <String , String > serviceProviderEntry = new HashMap <>();
8314+ serviceProviderEntry .put ("service" , serviceName );
8315+ serviceProviderEntry .put ("provider" , provider );
8316+ apiFormatMap .put (String .valueOf (index ), serviceProviderEntry );
8317+ index ++;
8318+ }
8319+ }
8320+
8321+ return apiFormatMap ;
8322+ }
8323+
82968324 private void applySourceOfferingValuesToCloneCmd (CloneNetworkOfferingCmd cmd , NetworkOfferingVO sourceOffering ) {
82978325 Long sourceOfferingId = sourceOffering .getId ();
82988326
@@ -8302,10 +8330,10 @@ private void applySourceOfferingValuesToCloneCmd(CloneNetworkOfferingCmd cmd, Ne
83028330 // Build final services list with add/drop support
83038331 List <String > finalServices = resolveFinalServicesList (cmd , sourceServiceProviderMap );
83048332
8305- Map finalServiceProviderMap = resolveServiceProviderMap (cmd , sourceServiceProviderMap , finalServices );
8333+ Map < String , List < String >> finalServiceProviderMap = resolveServiceProviderMap (cmd , sourceServiceProviderMap , finalServices );
83068334
83078335 // Reconstruct service capability list from source offering
8308- Map <String , String > sourceServiceCapabilityList = reconstructNetworkServiceCapabilityList (sourceOffering );
8336+ Map <String , Map < String , String > > sourceServiceCapabilityList = reconstructNetworkServiceCapabilityList (sourceOffering );
83098337
83108338 Map <String , String > sourceDetailsMap = getSourceOfferingDetails (sourceOfferingId );
83118339
@@ -8338,7 +8366,11 @@ private List<String> resolveFinalServicesList(CloneNetworkOfferingCmd cmd,
83388366
83398367 List <String > finalServices = new ArrayList <>();
83408368 for (Network .Service service : sourceServiceProviderMap .keySet ()) {
8341- finalServices .add (service .getName ());
8369+ // Gateway service is automatically added by createNetworkOffering if SourceNat is present
8370+ // It should not be explicitly included in supportedServices list
8371+ if (service != Network .Service .Gateway ) {
8372+ finalServices .add (service .getName ());
8373+ }
83428374 }
83438375
83448376 if (dropServices != null && !dropServices .isEmpty ()) {
@@ -8381,7 +8413,7 @@ private Map<String, List<String>> resolveServiceProviderMap(CloneNetworkOffering
83818413 }
83828414
83838415 private void applyResolvedValuesToCommand (CloneNetworkOfferingCmd cmd , NetworkOfferingVO sourceOffering ,
8384- List <String > finalServices , Map finalServiceProviderMap , Map <String , String > sourceServiceCapabilityList ,
8416+ List <String > finalServices , Map < String , List < String >> finalServiceProviderMap , Map <String , Map < String , String > > sourceServiceCapabilityList ,
83858417 Map <String , String > sourceDetailsMap , List <Long > sourceDomainIds , List <Long > sourceZoneIds ) {
83868418
83878419 try {
@@ -8391,7 +8423,9 @@ private void applyResolvedValuesToCommand(CloneNetworkOfferingCmd cmd, NetworkOf
83918423 setField (cmd , "supportedServices" , finalServices );
83928424 }
83938425 if (cmd .getServiceProviders () == null || cmd .getServiceProviders ().isEmpty ()) {
8394- setField (cmd , "serviceProviderList" , finalServiceProviderMap );
8426+ // Convert to API parameter format: Map with HashMap values containing "service" and "provider" keys
8427+ Map <String , Map <String , String >> apiFormatMap = convertToApiParameterFormat (finalServiceProviderMap );
8428+ setField (cmd , "serviceProviderList" , apiFormatMap );
83958429 }
83968430
83978431 // Apply service capability list if not provided via request parameters
@@ -8400,13 +8434,13 @@ private void applyResolvedValuesToCommand(CloneNetworkOfferingCmd cmd, NetworkOf
84008434 .anyMatch (key -> key .startsWith (ApiConstants .SERVICE_CAPABILITY_LIST ));
84018435
84028436 if (!hasCapabilityParams && sourceServiceCapabilityList != null && !sourceServiceCapabilityList .isEmpty ()) {
8403- setField (cmd , "serviceCapabilitystList " , sourceServiceCapabilityList );
8437+ setField (cmd , "serviceCapabilitiesList " , sourceServiceCapabilityList );
84048438 }
84058439
84068440 applyIfNotProvided (cmd , requestParams , "displayText" , ApiConstants .DISPLAY_TEXT , cmd .getDisplayText (), sourceOffering .getDisplayText ());
84078441 applyIfNotProvided (cmd , requestParams , "traffictype" , ApiConstants .TRAFFIC_TYPE , cmd .getTraffictype (), sourceOffering .getTrafficType ().toString ());
84088442 applyIfNotProvided (cmd , requestParams , "tags" , ApiConstants .TAGS , cmd .getTags (), sourceOffering .getTags ());
8409- applyIfNotProvided (cmd , requestParams , "availability" , ApiConstants .AVAILABILITY , cmd .getAvailability (), sourceOffering . getAvailability () .toString ());
8443+ applyIfNotProvided (cmd , requestParams , "availability" , ApiConstants .AVAILABILITY , cmd .getAvailability (), Availability . Optional .toString ());
84108444 applyIfNotProvided (cmd , requestParams , "networkRate" , ApiConstants .NETWORKRATE , cmd .getNetworkRate (), sourceOffering .getRateMbps ());
84118445 applyIfNotProvided (cmd , requestParams , "serviceOfferingId" , ApiConstants .SERVICE_OFFERING_ID , cmd .getServiceOfferingId (), sourceOffering .getServiceOfferingId ());
84128446 applyIfNotProvided (cmd , requestParams , "guestIptype" , ApiConstants .GUEST_IP_TYPE , cmd .getGuestIpType (), sourceOffering .getGuestType ().toString ());
@@ -8464,79 +8498,93 @@ private void applyResolvedValuesToCommand(CloneNetworkOfferingCmd cmd, NetworkOf
84648498 * These capabilities were originally passed during creation and stored as boolean flags in the offering.
84658499 *
84668500 * Returns a Map in the format expected by CreateNetworkOfferingCmd.serviceCapabilitystList:
8467- * Map<String, String> with keys like "0. service", "0. capabilitytype", "0. capabilityvalue"
8501+ * Map where each value is a HashMap with keys: " service", "capabilitytype", "capabilityvalue"
84688502 */
8469- private Map <String , String > reconstructNetworkServiceCapabilityList (NetworkOfferingVO sourceOffering ) {
8470- Map <String , String > capabilityList = new HashMap <>();
8503+ private Map <String , Map < String , String > > reconstructNetworkServiceCapabilityList (NetworkOfferingVO sourceOffering ) {
8504+ Map <String , Map < String , String > > capabilityList = new HashMap <>();
84718505 int index = 0 ;
84728506
84738507 // LB service capabilities
84748508 if (sourceOffering .isDedicatedLB ()) {
8475- capabilityList .put (index + ".service" , Network .Service .Lb .getName ());
8476- capabilityList .put (index + ".capabilitytype" , Network .Capability .SupportedLBIsolation .getName ());
8477- capabilityList .put (index + ".capabilityvalue" , "dedicated" );
8478- index ++;
8509+ Map <String , String > cap = new HashMap <>();
8510+ cap .put ("service" , Network .Service .Lb .getName ());
8511+ cap .put ("capabilitytype" , Network .Capability .SupportedLBIsolation .getName ());
8512+ cap .put ("capabilityvalue" , "dedicated" );
8513+ capabilityList .put (String .valueOf (index ++), cap );
84798514 }
84808515 if (sourceOffering .isElasticLb ()) {
8481- capabilityList .put (index + ".service" , Network .Service .Lb .getName ());
8482- capabilityList .put (index + ".capabilitytype" , Network .Capability .ElasticLb .getName ());
8483- capabilityList .put (index + ".capabilityvalue" , "true" );
8484- index ++;
8516+ Map <String , String > cap = new HashMap <>();
8517+ cap .put ("service" , Network .Service .Lb .getName ());
8518+ cap .put ("capabilitytype" , Network .Capability .ElasticLb .getName ());
8519+ cap .put ("capabilityvalue" , "true" );
8520+ capabilityList .put (String .valueOf (index ++), cap );
84858521 }
84868522 if (sourceOffering .isInline ()) {
8487- capabilityList .put (index + ".service" , Network .Service .Lb .getName ());
8488- capabilityList .put (index + ".capabilitytype" , Network .Capability .InlineMode .getName ());
8489- capabilityList .put (index + ".capabilityvalue" , "true" );
8490- index ++;
8523+ Map <String , String > cap = new HashMap <>();
8524+ cap .put ("service" , Network .Service .Lb .getName ());
8525+ cap .put ("capabilitytype" , Network .Capability .InlineMode .getName ());
8526+ cap .put ("capabilityvalue" , "true" );
8527+ capabilityList .put (String .valueOf (index ++), cap );
84918528 }
84928529 if (sourceOffering .isPublicLb () || sourceOffering .isInternalLb ()) {
84938530 List <String > schemes = new ArrayList <>();
84948531 if (sourceOffering .isPublicLb ()) schemes .add ("public" );
84958532 if (sourceOffering .isInternalLb ()) schemes .add ("internal" );
8496- capabilityList .put (index + ".service" , Network .Service .Lb .getName ());
8497- capabilityList .put (index + ".capabilitytype" , Network .Capability .LbSchemes .getName ());
8498- capabilityList .put (index + ".capabilityvalue" , String .join ("," , schemes ));
8499- index ++;
8533+ Map <String , String > cap = new HashMap <>();
8534+ cap .put ("service" , Network .Service .Lb .getName ());
8535+ cap .put ("capabilitytype" , Network .Capability .LbSchemes .getName ());
8536+ cap .put ("capabilityvalue" , String .join ("," , schemes ));
8537+ capabilityList .put (String .valueOf (index ++), cap );
85008538 }
85018539 if (sourceOffering .isSupportsVmAutoScaling ()) {
8502- capabilityList .put (index + ".service" , Network .Service .Lb .getName ());
8503- capabilityList .put (index + ".capabilitytype" , Network .Capability .VmAutoScaling .getName ());
8504- capabilityList .put (index + ".capabilityvalue" , "true" );
8505- index ++;
8540+ Map <String , String > cap = new HashMap <>();
8541+ cap .put ("service" , Network .Service .Lb .getName ());
8542+ cap .put ("capabilitytype" , Network .Capability .VmAutoScaling .getName ());
8543+ cap .put ("capabilityvalue" , "true" );
8544+ capabilityList .put (String .valueOf (index ++), cap );
85068545 }
85078546
85088547 // SourceNat service capabilities
8509- if (sourceOffering .isSharedSourceNat () || sourceOffering .isRedundantRouter ()) {
8510- capabilityList .put (index + ".service" , Network .Service .SourceNat .getName ());
8511- capabilityList .put (index + ".capabilitytype" , Network .Capability .SupportedSourceNatTypes .getName ());
8512- capabilityList .put (index + ".capabilityvalue" , sourceOffering .isSharedSourceNat () ? "perzone" : "peraccount" );
8513- index ++;
8548+ // Only add SupportedSourceNatTypes if explicitly set to perzone (sharedSourceNat=true)
8549+ if (sourceOffering .isSharedSourceNat ()) {
8550+ Map <String , String > cap = new HashMap <>();
8551+ cap .put ("service" , Network .Service .SourceNat .getName ());
8552+ cap .put ("capabilitytype" , Network .Capability .SupportedSourceNatTypes .getName ());
8553+ cap .put ("capabilityvalue" , "perzone" );
8554+ capabilityList .put (String .valueOf (index ++), cap );
85148555 }
8556+
8557+ // Only add RedundantRouter capability when it's true
85158558 if (sourceOffering .isRedundantRouter ()) {
8516- capabilityList .put (index + ".service" , Network .Service .SourceNat .getName ());
8517- capabilityList .put (index + ".capabilitytype" , Network .Capability .RedundantRouter .getName ());
8518- capabilityList .put (index + ".capabilityvalue" , "true" );
8519- index ++;
8559+ Map <String , String > cap1 = new HashMap <>();
8560+ cap1 .put ("service" , Network .Service .SourceNat .getName ());
8561+ cap1 .put ("capabilitytype" , Network .Capability .RedundantRouter .getName ());
8562+ cap1 .put ("capabilityvalue" , "true" );
8563+ capabilityList .put (String .valueOf (index ++), cap1 );
85208564
8521- // Also add to Gateway service
8522- capabilityList .put (index + ".service" , Network .Service .Gateway .getName ());
8523- capabilityList .put (index + ".capabilitytype" , Network .Capability .RedundantRouter .getName ());
8524- capabilityList .put (index + ".capabilityvalue" , "true" );
8525- index ++;
8565+ // Also add to Gateway service only when redundantRouter is true
8566+ Map <String , String > cap2 = new HashMap <>();
8567+ cap2 .put ("service" , Network .Service .Gateway .getName ());
8568+ cap2 .put ("capabilitytype" , Network .Capability .RedundantRouter .getName ());
8569+ cap2 .put ("capabilityvalue" , "true" );
8570+ capabilityList .put (String .valueOf (index ++), cap2 );
85268571 }
85278572
85288573 // StaticNat service capabilities
85298574 if (sourceOffering .isElasticIp ()) {
8530- capabilityList .put (index + ".service" , Network .Service .StaticNat .getName ());
8531- capabilityList .put (index + ".capabilitytype" , Network .Capability .ElasticIp .getName ());
8532- capabilityList .put (index + ".capabilityvalue" , "true" );
8533- index ++;
8534- }
8535- if (sourceOffering .isAssociatePublicIP ()) {
8536- capabilityList .put (index + ".service" , Network .Service .StaticNat .getName ());
8537- capabilityList .put (index + ".capabilitytype" , Network .Capability .AssociatePublicIP .getName ());
8538- capabilityList .put (index + ".capabilityvalue" , "true" );
8539- index ++;
8575+ Map <String , String > cap = new HashMap <>();
8576+ cap .put ("service" , Network .Service .StaticNat .getName ());
8577+ cap .put ("capabilitytype" , Network .Capability .ElasticIp .getName ());
8578+ cap .put ("capabilityvalue" , "true" );
8579+ capabilityList .put (String .valueOf (index ++), cap );
8580+ }
8581+ // AssociatePublicIP can only be set when ElasticIp is true
8582+ if (sourceOffering .isElasticIp () && sourceOffering .isAssociatePublicIP ()) {
8583+ Map <String , String > cap = new HashMap <>();
8584+ cap .put ("service" , Network .Service .StaticNat .getName ());
8585+ cap .put ("capabilitytype" , Network .Capability .AssociatePublicIP .getName ());
8586+ cap .put ("capabilityvalue" , "true" );
8587+ capabilityList .put (String .valueOf (index ++), cap );
85408588 }
85418589
85428590 return capabilityList ;
0 commit comments