Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions PendingReleaseNotes
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ example.ver.1 > example.ver.2:
continue to snapshot live.

4.23.0.0 > 24.0.0:
* NSX network offerings can reference existing IP discovery, MAC discovery,
and segment security profiles. New NSX segments bind selected profiles in
the same hierarchical request as segment creation. Existing networks are
not silently reconfigured when profile bindings change.

* VMware-to-KVM import using VDDK can import powered-off VMware VMs directly
into Ceph RBD primary storage when forceconverttopool is enabled and the
selected KVM conversion host supports qemu RBD access and in-place virt-v2v
Expand Down
17 changes: 16 additions & 1 deletion api/src/main/java/com/cloud/offering/NetworkOffering.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,22 @@ public enum State {
}

public enum Detail {
InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RelatedNetworkOffering, domainid, zoneid, pvlanType, internetProtocol
InternalLbProvider,
PublicLbProvider,
servicepackageuuid,
servicepackagedescription,
PromiscuousMode,
MacAddressChanges,
ForgedTransmits,
MacLearning,
NsxIpDiscoveryProfileId,
NsxMacDiscoveryProfileId,
NsxSegmentSecurityProfileId,
RelatedNetworkOffering,
domainid,
zoneid,
pvlanType,
internetProtocol
}

public enum NetworkMode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
// under the License.
package org.apache.cloudstack.api.command.admin.network;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
Expand Down Expand Up @@ -62,6 +64,13 @@ public class CloneNetworkOfferingCmd extends NetworkOfferingBaseCmd {
"If specified along with 'supportedservices', this parameter is ignored.")
private List<String> dropServices;

@Parameter(name = ApiConstants.CLEAN_UP_DETAILS,
type = CommandType.BOOLEAN,
description = "Whether to omit network offering details from the clone. If true, source details are not copied " +
"and the details parameter is ignored. If false or omitted, details are inherited unless explicitly supplied.",
since = "24.0")
private Boolean cleanupDetails;

@Parameter(name = ApiConstants.TRAFFIC_TYPE,
type = CommandType.STRING,
description = "The traffic type for the network offering. Supported type in current release is GUEST only")
Expand All @@ -87,6 +96,15 @@ public List<String> getDropServices() {
return dropServices;
}

public boolean isCleanupDetails() {
return Boolean.TRUE.equals(cleanupDetails);
}

@Override
public Map<String, String> getDetails() {
return isCleanupDetails() ? Collections.emptyMap() : super.getDetails();
}

public String getGuestIpType() {
return guestIptype;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.cloud.network.Network.Service.Dhcp;
import static com.cloud.network.Network.Service.Dns;
Expand Down Expand Up @@ -167,7 +166,9 @@ public abstract class NetworkOfferingBaseCmd extends BaseCmd {

@Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.2.0", description = "Network offering details in key/value pairs."
+ " Supported keys are internallbprovider/publiclbprovider with service provider as a value, and"
+ " promiscuousmode/macaddresschanges/forgedtransmits with true/false as value to accept/reject the security settings if available for a nic/portgroup")
+ " promiscuousmode/macaddresschanges/forgedtransmits with true/false as value to accept/reject the security settings if available for a nic/portgroup."
+ " NSX offerings also support nsxipdiscoveryprofileid, nsxmacdiscoveryprofileid, and nsxsegmentsecurityprofileid."
+ " Values are IDs of existing operator-managed NSX profiles to bind to segments created from the offering.")
protected Map details;

@Parameter(name = ApiConstants.EGRESS_DEFAULT_POLICY,
Expand Down Expand Up @@ -382,18 +383,18 @@ private void getServiceProviderMapForExternalProvider(Map<String, List<String>>
VirtualRouterProvider.Type.VirtualRouter.name();
List<String> unsupportedServices = new ArrayList<>(List.of("Vpn", "Gateway", "SecurityGroup", "Connectivity", "BaremetalPxeService"));
List<String> routerSupported = List.of("Dhcp", "Dns", "UserData");
List<String> allServices = Network.Service.listAllServices().stream().map(Network.Service::getName).collect(Collectors.toList());
if (routerProvider.equals(VirtualRouterProvider.Type.VPCVirtualRouter.name())) {
unsupportedServices.add("Firewall");
} else {
unsupportedServices.add("NetworkACL");
}
for (String service : allServices) {
for (String service : getSupportedServices()) {
if (unsupportedServices.contains(service))
continue;
if (routerSupported.contains(service))
serviceProviderMap.put(service, List.of(routerProvider));
else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || NetworkACL.getName().equalsIgnoreCase(service)) {
else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || NetworkACL.getName().equalsIgnoreCase(service)
|| (isForNsx() && Firewall.getName().equalsIgnoreCase(service))) {
serviceProviderMap.put(service, List.of(provider));
}
if (isNsxWithoutLb(getProvider(), getNsxSupportsLbService()) || isNetrisRouted(getProvider(), getNetworkMode())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -86,6 +88,43 @@ public void testGetDropServices() {
assertEquals(dropServices, cloneNetworkOfferingCmd.getDropServices());
}

@Test
public void testDetailsAreInheritedWhenCleanupIsOmittedOrFalse() {
Map<String, String> sourceDetails = Map.of("NsxIpDiscoveryProfileId", "source-ip-profile");
ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "sourceDetailsMap", sourceDetails);

assertFalse(cloneNetworkOfferingCmd.isCleanupDetails());
assertEquals(sourceDetails, cloneNetworkOfferingCmd.getDetails());

ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "cleanupDetails", false);
assertEquals(sourceDetails, cloneNetworkOfferingCmd.getDetails());
}

@Test
public void testSuppliedDetailsReplaceSourceDetails() {
Map<String, String> sourceDetails = Map.of("NsxIpDiscoveryProfileId", "source-ip-profile",
"NsxMacDiscoveryProfileId", "source-mac-profile");
Map<String, String> requestedDetails = Map.of("nsxipdiscoveryprofileid", "replacement-ip-profile");
ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "sourceDetailsMap", sourceDetails);
ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "details", Map.of("0", requestedDetails));

assertEquals(requestedDetails, cloneNetworkOfferingCmd.getDetails());
}

@Test
public void testCleanupDetailsOmitsInheritedAndSuppliedDetails() {
Map<String, String> sourceDetails = Map.of("NsxIpDiscoveryProfileId", "source-ip-profile");
ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "sourceDetailsMap", sourceDetails);
ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "cleanupDetails", true);

assertTrue(cloneNetworkOfferingCmd.getDetails().isEmpty());

ReflectionTestUtils.setField(cloneNetworkOfferingCmd, "details",
Map.of("0", Map.of("nsxipdiscoveryprofileid", "replacement-ip-profile")));
assertTrue(cloneNetworkOfferingCmd.getDetails().isEmpty());
assertEquals("source-ip-profile", sourceDetails.get("NsxIpDiscoveryProfileId"));
}

@Test
public void testGetGuestIpType() {
String guestIpType = "Isolated";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

package org.apache.cloudstack.api.command.admin.offering;

import java.util.List;
import java.util.Map;

import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
import org.junit.Assert;
import org.junit.Test;
Expand All @@ -36,4 +39,57 @@ public void createVpcNtwkOffWithEmptyDisplayText() {
ReflectionTestUtils.setField(createNetworkOfferingCmd, "networkOfferingName", netName);
Assert.assertEquals(createNetworkOfferingCmd.getDisplayText(), netName);
}

@Test
public void nsxNattedNetworkUsesNsxFirewallAndNatServices() {
assertServiceProviders("NSX", "NATTED", false, Map.of(
"Dhcp", List.of("VirtualRouter"), "Dns", List.of("VirtualRouter"), "UserData", List.of("VirtualRouter"),
"Firewall", List.of("Nsx"), "SourceNat", List.of("Nsx"), "StaticNat", List.of("Nsx"), "PortForwarding", List.of("Nsx")));
}

@Test
public void nsxNattedVpcTierUsesNsxAclAndNatServices() {
assertServiceProviders("NSX", "NATTED", true, Map.of(
"Dhcp", List.of("VPCVirtualRouter"), "Dns", List.of("VPCVirtualRouter"), "UserData", List.of("VPCVirtualRouter"),
"NetworkACL", List.of("Nsx"), "SourceNat", List.of("Nsx"), "StaticNat", List.of("Nsx"), "PortForwarding", List.of("Nsx")));
}

@Test
public void nsxRoutedNetworkUsesNsxFirewallWithoutNatServices() {
assertServiceProviders("NSX", "ROUTED", false, Map.of(
"Dhcp", List.of("VirtualRouter"), "Dns", List.of("VirtualRouter"), "UserData", List.of("VirtualRouter"),
"Firewall", List.of("Nsx")));
}

@Test
public void nsxRoutedVpcTierUsesNsxAclWithoutNatServices() {
assertServiceProviders("NSX", "ROUTED", true, Map.of(
"Dhcp", List.of("VPCVirtualRouter"), "Dns", List.of("VPCVirtualRouter"), "UserData", List.of("VPCVirtualRouter"),
"NetworkACL", List.of("Nsx")));
}

@Test
public void netrisNattedNetworkKeepsNetrisNatAndLoadBalancerProviders() {
assertServiceProviders("Netris", "NATTED", false, Map.of(
"Dhcp", List.of("VirtualRouter"), "Dns", List.of("VirtualRouter"), "UserData", List.of("VirtualRouter"),
"Firewall", List.of("Netris"), "SourceNat", List.of("Netris"), "StaticNat", List.of("Netris"),
"PortForwarding", List.of("Netris"), "Lb", List.of("Netris"), "Vpn", List.of("VirtualRouter")));
}

@Test
public void netrisRoutedNetworkKeepsOnlyRouterProviders() {
assertServiceProviders("Netris", "ROUTED", false, Map.of(
"Dhcp", List.of("VirtualRouter"), "Dns", List.of("VirtualRouter"), "UserData", List.of("VirtualRouter")));
}

private void assertServiceProviders(String provider, String networkMode, boolean forVpc, Map<String, List<String>> expected) {
ReflectionTestUtils.setField(createNetworkOfferingCmd, "provider", provider);
ReflectionTestUtils.setField(createNetworkOfferingCmd, "networkMode", networkMode);
ReflectionTestUtils.setField(createNetworkOfferingCmd, "forVpc", forVpc);

Map<String, List<String>> providers = createNetworkOfferingCmd.getServiceProviders();

Assert.assertEquals(expected, providers);
Assert.assertTrue(createNetworkOfferingCmd.getSupportedServices().containsAll(providers.keySet()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,31 @@ public class CreateNsxSegmentCommand extends NsxCommand {
private String networkName;
private String networkGateway;
private String networkCidr;
private String ipDiscoveryProfileId;
private String macDiscoveryProfileId;
private String segmentSecurityProfileId;

public CreateNsxSegmentCommand(long domainId, long accountId, long zoneId,
Long vpcId, String vpcName, long networkId, String networkName,
String networkGateway, String networkCidr) {
this(domainId, accountId, zoneId, vpcId, vpcName, networkId, networkName, networkGateway,
networkCidr, null, null, null);
}

public CreateNsxSegmentCommand(long domainId, long accountId, long zoneId,
Long vpcId, String vpcName, long networkId, String networkName,
String networkGateway, String networkCidr, String ipDiscoveryProfileId,
String macDiscoveryProfileId, String segmentSecurityProfileId) {
super(domainId, accountId, zoneId);
this.vpcId = vpcId;
this.vpcName = vpcName;
this.networkId = networkId;
this.networkName = networkName;
this.networkGateway = networkGateway;
this.networkCidr = networkCidr;
this.ipDiscoveryProfileId = ipDiscoveryProfileId;
this.macDiscoveryProfileId = macDiscoveryProfileId;
this.segmentSecurityProfileId = segmentSecurityProfileId;
}

public Long getVpcId() {
Expand All @@ -63,6 +77,18 @@ public String getNetworkCidr() {
return networkCidr;
}

public String getIpDiscoveryProfileId() {
return ipDiscoveryProfileId;
}

public String getMacDiscoveryProfileId() {
return macDiscoveryProfileId;
}

public String getSegmentSecurityProfileId() {
return segmentSecurityProfileId;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,8 @@ private Answer executeRequest(CreateNsxSegmentCommand cmd) {
boolean isResourceVpc = !Objects.isNull(cmd.getVpcId());
String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(),
cmd.getZoneId(), networkResourceId, isResourceVpc);
nsxApiClient.createSegment(segmentName, tier1GatewayName, gatewayAddress, enforcementPointPath, transportZones);
nsxApiClient.createSegment(segmentName, tier1GatewayName, gatewayAddress, enforcementPointPath, transportZones,
cmd.getIpDiscoveryProfileId(), cmd.getMacDiscoveryProfileId(), cmd.getSegmentSecurityProfileId());
nsxApiClient.createGroupForSegment(segmentName);
} catch (Exception e) {
logger.error(String.format("Failed to create network: %s", cmd.getNetworkName()));
Expand Down
Loading