Skip to content

Commit 3787aaa

Browse files
committed
Add support to clone network and vpc offering with the right parameters
1 parent 24ddfd2 commit 3787aaa

File tree

5 files changed

+134
-42
lines changed

5 files changed

+134
-42
lines changed

api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CloneVPCOfferingCmd.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package org.apache.cloudstack.api.command.admin.vpc;
1818

19+
import com.cloud.exception.ResourceAllocationException;
1920
import com.cloud.network.vpc.VpcOffering;
2021
import org.apache.cloudstack.api.APICommand;
2122
import org.apache.cloudstack.api.ApiConstants;
@@ -81,10 +82,23 @@ public List<String> getDropServices() {
8182
/////////////// API Implementation///////////////////
8283
/////////////////////////////////////////////////////
8384

85+
@Override
86+
public void create() throws ResourceAllocationException {
87+
// Set a temporary entity ID (source offering ID) to prevent NullPointerException
88+
// in ApiServer.queueCommand(). This will be updated in execute() with the actual
89+
// cloned offering ID.
90+
if (sourceOfferingId != null) {
91+
setEntityId(sourceOfferingId);
92+
}
93+
}
94+
8495
@Override
8596
public void execute() {
8697
VpcOffering result = _vpcProvSvc.cloneVPCOffering(this);
8798
if (result != null) {
99+
setEntityId(result.getId());
100+
setEntityUuid(result.getUuid());
101+
88102
VpcOfferingResponse response = _responseGenerator.createVpcOfferingResponse(result);
89103
response.setResponseName(getCommandName());
90104
this.setResponseObject(response);

api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.util.stream.Collectors;
2929
import java.util.stream.Stream;
3030

31-
import com.cloud.exception.InvalidParameterValueException;
3231
import com.cloud.network.Network;
3332
import com.cloud.network.VirtualRouterProvider;
3433
import com.cloud.offering.NetworkOffering;
@@ -179,9 +178,7 @@ public boolean isExternalNetworkProvider() {
179178
}
180179

181180
public List<String> getSupportedServices() {
182-
if (!isExternalNetworkProvider() && CollectionUtils.isEmpty(supportedServices)) {
183-
throw new InvalidParameterValueException("Supported services needs to be provided");
184-
}
181+
// For external network providers, auto-populate services based on network mode
185182
if (isExternalNetworkProvider()) {
186183
supportedServices = new ArrayList<>(List.of(
187184
Dhcp.getName(),

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8332,7 +8332,6 @@ private void applySourceOfferingValuesToCloneCmd(CloneNetworkOfferingCmd cmd, Ne
83328332

83338333
Map<String, List<String>> finalServiceProviderMap = resolveServiceProviderMap(cmd, sourceServiceProviderMap, finalServices);
83348334

8335-
// Reconstruct service capability list from source offering
83368335
Map<String, Map<String, String>> sourceServiceCapabilityList = reconstructNetworkServiceCapabilityList(sourceOffering);
83378336

83388337
Map<String, String> sourceDetailsMap = getSourceOfferingDetails(sourceOfferingId);
@@ -8366,25 +8365,38 @@ private List<String> resolveFinalServicesList(CloneNetworkOfferingCmd cmd,
83668365

83678366
List<String> finalServices = new ArrayList<>();
83688367
for (Network.Service service : sourceServiceProviderMap.keySet()) {
8369-
// Gateway service is automatically added by createNetworkOffering if SourceNat is present
8370-
// It should not be explicitly included in supportedServices list
83718368
if (service != Network.Service.Gateway) {
83728369
finalServices.add(service.getName());
83738370
}
83748371
}
83758372

83768373
if (dropServices != null && !dropServices.isEmpty()) {
8377-
finalServices.removeAll(dropServices);
8378-
logger.debug("Dropped services from clone: {}", dropServices);
8374+
List<String> normalizedDropServices = new ArrayList<>();
8375+
for (String serviceName : dropServices) {
8376+
Network.Service service = Network.Service.getService(serviceName);
8377+
if (service == null) {
8378+
throw new InvalidParameterValueException("Invalid service name in dropServices: " + serviceName);
8379+
}
8380+
normalizedDropServices.add(service.getName());
8381+
}
8382+
finalServices.removeAll(normalizedDropServices);
8383+
logger.debug("Dropped services from clone: {}", normalizedDropServices);
83798384
}
83808385

83818386
if (addServices != null && !addServices.isEmpty()) {
8382-
for (String service : addServices) {
8383-
if (!finalServices.contains(service)) {
8384-
finalServices.add(service);
8387+
List<String> normalizedAddServices = new ArrayList<>();
8388+
for (String serviceName : addServices) {
8389+
Network.Service service = Network.Service.getService(serviceName);
8390+
if (service == null) {
8391+
throw new InvalidParameterValueException("Invalid service name in addServices: " + serviceName);
8392+
}
8393+
String canonicalName = service.getName();
8394+
if (!finalServices.contains(canonicalName)) {
8395+
finalServices.add(canonicalName);
8396+
normalizedAddServices.add(canonicalName);
83858397
}
83868398
}
8387-
logger.debug("Added services to clone: {}", addServices);
8399+
logger.debug("Added services to clone: {}", normalizedAddServices);
83888400
}
83898401

83908402
return finalServices;
@@ -8423,13 +8435,10 @@ private void applyResolvedValuesToCommand(CloneNetworkOfferingCmd cmd, NetworkOf
84238435
setField(cmd, "supportedServices", finalServices);
84248436
}
84258437
if (cmd.getServiceProviders() == null || cmd.getServiceProviders().isEmpty()) {
8426-
// Convert to API parameter format: Map with HashMap values containing "service" and "provider" keys
84278438
Map<String, Map<String, String>> apiFormatMap = convertToApiParameterFormat(finalServiceProviderMap);
84288439
setField(cmd, "serviceProviderList", apiFormatMap);
84298440
}
84308441

8431-
// Apply service capability list if not provided via request parameters
8432-
// Check if any servicecapabilitylist parameters were passed (e.g., servicecapabilitylist[0].service)
84338442
boolean hasCapabilityParams = requestParams.keySet().stream()
84348443
.anyMatch(key -> key.startsWith(ApiConstants.SERVICE_CAPABILITY_LIST));
84358444

@@ -8504,7 +8513,6 @@ private Map<String, Map<String, String>> reconstructNetworkServiceCapabilityList
85048513
Map<String, Map<String, String>> capabilityList = new HashMap<>();
85058514
int index = 0;
85068515

8507-
// LB service capabilities
85088516
if (sourceOffering.isDedicatedLB()) {
85098517
Map<String, String> cap = new HashMap<>();
85108518
cap.put("service", Network.Service.Lb.getName());
@@ -8544,8 +8552,6 @@ private Map<String, Map<String, String>> reconstructNetworkServiceCapabilityList
85448552
capabilityList.put(String.valueOf(index++), cap);
85458553
}
85468554

8547-
// SourceNat service capabilities
8548-
// Only add SupportedSourceNatTypes if explicitly set to perzone (sharedSourceNat=true)
85498555
if (sourceOffering.isSharedSourceNat()) {
85508556
Map<String, String> cap = new HashMap<>();
85518557
cap.put("service", Network.Service.SourceNat.getName());
@@ -8554,31 +8560,28 @@ private Map<String, Map<String, String>> reconstructNetworkServiceCapabilityList
85548560
capabilityList.put(String.valueOf(index++), cap);
85558561
}
85568562

8557-
// Only add RedundantRouter capability when it's true
85588563
if (sourceOffering.isRedundantRouter()) {
85598564
Map<String, String> cap1 = new HashMap<>();
85608565
cap1.put("service", Network.Service.SourceNat.getName());
85618566
cap1.put("capabilitytype", Network.Capability.RedundantRouter.getName());
85628567
cap1.put("capabilityvalue", "true");
85638568
capabilityList.put(String.valueOf(index++), cap1);
85648569

8565-
// Also add to Gateway service only when redundantRouter is true
85668570
Map<String, String> cap2 = new HashMap<>();
85678571
cap2.put("service", Network.Service.Gateway.getName());
85688572
cap2.put("capabilitytype", Network.Capability.RedundantRouter.getName());
85698573
cap2.put("capabilityvalue", "true");
85708574
capabilityList.put(String.valueOf(index++), cap2);
85718575
}
85728576

8573-
// StaticNat service capabilities
85748577
if (sourceOffering.isElasticIp()) {
85758578
Map<String, String> cap = new HashMap<>();
85768579
cap.put("service", Network.Service.StaticNat.getName());
85778580
cap.put("capabilitytype", Network.Capability.ElasticIp.getName());
85788581
cap.put("capabilityvalue", "true");
85798582
capabilityList.put(String.valueOf(index++), cap);
85808583
}
8581-
// AssociatePublicIP can only be set when ElasticIp is true
8584+
85828585
if (sourceOffering.isElasticIp() && sourceOffering.isAssociatePublicIP()) {
85838586
Map<String, String> cap = new HashMap<>();
85848587
cap.put("service", Network.Service.StaticNat.getName());
@@ -8592,16 +8595,14 @@ private Map<String, Map<String, String>> reconstructNetworkServiceCapabilityList
85928595

85938596
public static void applyIfNotProvided(Object cmd, Map<String, String> requestParams, String fieldName,
85948597
String apiConstant, Object currentValue, Object sourceValue) throws Exception {
8595-
// If parameter was not provided in request and source has a value, use source value
8596-
if (!requestParams.containsKey(apiConstant) && sourceValue != null) {
8598+
if ((requestParams == null || !requestParams.containsKey(apiConstant)) && sourceValue != null) {
85978599
setField(cmd, fieldName, sourceValue);
85988600
}
8599-
// If parameter WAS provided in request, the framework already set it correctly
86008601
}
86018602

86028603
public static void applyBooleanIfNotProvided(Object cmd, Map<String, String> requestParams,
86038604
String fieldName, String apiConstant, Boolean sourceValue) throws Exception {
8604-
if (!requestParams.containsKey(apiConstant) && sourceValue != null) {
8605+
if ((requestParams == null || !requestParams.containsKey(apiConstant)) && sourceValue != null) {
86058606
setField(cmd, fieldName, sourceValue);
86068607
}
86078608
}

0 commit comments

Comments
 (0)