diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index b2ed53b2cef..101bc2f88a0 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -39,6 +39,8 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -340,6 +342,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra Ipv6Service ipv6Service; @Inject RouterNetworkDao routerNetworkDao; + @Inject + private VlanDetailsDao vlanDetailsDao; List networkGurus; @@ -1029,6 +1033,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (profile == null) { return null; } + // TODO: Fix with the public network PR + if (isNicAllocatedForNsxPublicNetworkOnVR(network, profile, vm)) { + String guruName = "NsxPublicNetworkGuru"; + NetworkGuru nsxGuru = AdapterBase.getAdapterByName(networkGurus, guruName); + nsxGuru.allocate(network, profile, vm); + } if (isDefaultNic != null) { profile.setDefaultNic(isDefaultNic); @@ -1062,6 +1072,18 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return new Pair(vmNic, Integer.valueOf(deviceId)); } + private boolean isNicAllocatedForNsxPublicNetworkOnVR(Network network, NicProfile requested, VirtualMachineProfile vm) { + boolean isVirtualRouter = vm.getType() == Type.DomainRouter; + boolean isPublicTraffic = network.getTrafficType() == TrafficType.Public; + if (!isVirtualRouter || !isPublicTraffic || requested.getIPv4Address() == null) { + return false; + } + IPAddressVO ip = _ipAddressDao.findByIp(requested.getIPv4Address()); + VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(ip.getVlanId(), ApiConstants.NSX_DETAIL_KEY); + boolean isForNsx = vlanDetail != null && vlanDetail.getValue().equalsIgnoreCase("true"); + return isForNsx && ip.isSourceNat() && !ip.isForSystemVms(); + } + private void setMtuDetailsInVRNic(final Pair networks, Network network, NicVO vo) { if (TrafficType.Public == network.getTrafficType()) { if (networks == null) { diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxTier1NatRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxTier1NatRuleCommand.java new file mode 100644 index 00000000000..66726053316 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxTier1NatRuleCommand.java @@ -0,0 +1,50 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +public class CreateNsxTier1NatRuleCommand extends NsxCommand { + + private String tier1GatewayName; + private String action; + private String translatedIpAddress; + private String natRuleId; + + public CreateNsxTier1NatRuleCommand(long domainId, long accountId, long zoneId, + String tier1GatewayName, String action, String translatedIpAddress, String natRuleId) { + super(domainId, accountId, zoneId); + this.tier1GatewayName = tier1GatewayName; + this.action = action; + this.translatedIpAddress = translatedIpAddress; + this.natRuleId = natRuleId; + } + + public String getTier1GatewayName() { + return tier1GatewayName; + } + + public String getAction() { + return action; + } + + public String getTranslatedIpAddress() { + return translatedIpAddress; + } + + public String getNatRuleId() { + return natRuleId; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java index 20665c2827f..d103c232f51 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java @@ -37,6 +37,7 @@ import org.apache.cloudstack.StartupNsxCommand; import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand; import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.CreateNsxTier1NatRuleCommand; import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand; import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand; import org.apache.cloudstack.service.NsxApiClient; @@ -104,6 +105,8 @@ public class NsxResource implements ServerResource { return executeRequest((CreateNsxTier1GatewayCommand) cmd); } else if (cmd instanceof CreateNsxDhcpRelayConfigCommand) { return executeRequest((CreateNsxDhcpRelayConfigCommand) cmd); + } else if (cmd instanceof CreateNsxTier1NatRuleCommand) { + return executeRequest((CreateNsxTier1NatRuleCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -210,6 +213,22 @@ public class NsxResource implements ServerResource { return true; } + private Answer executeRequest(CreateNsxTier1NatRuleCommand cmd) { + String tier1GatewayName = cmd.getTier1GatewayName(); + String action = cmd.getAction(); + String translatedIpAddress = cmd.getTranslatedIpAddress(); + String natRuleId = cmd.getNatRuleId(); + String natId = "USER"; + try { + nsxApiClient.createTier1NatRule(tier1GatewayName, natId, natRuleId, action, translatedIpAddress); + } catch (CloudRuntimeException e) { + String msg = String.format("Error creating the NAT rule with ID %s on Tier1 Gateway %s: %s", natRuleId, tier1GatewayName, e.getMessage()); + LOGGER.error(msg, e); + return new NsxAnswer(cmd, e); + } + return new NsxAnswer(cmd, true, ""); + } + private Answer executeRequest(CreateNsxDhcpRelayConfigCommand cmd) { long zoneId = cmd.getZoneId(); long domainId = cmd.getDomainId(); diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java index e160d3e1b2a..0e196154732 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java @@ -26,10 +26,12 @@ import com.vmware.nsx_policy.infra.Sites; import com.vmware.nsx_policy.infra.Tier1s; import com.vmware.nsx_policy.infra.sites.EnforcementPoints; import com.vmware.nsx_policy.infra.tier_0s.LocaleServices; +import com.vmware.nsx_policy.infra.tier_1s.nat.NatRules; import com.vmware.nsx_policy.model.ApiError; import com.vmware.nsx_policy.model.DhcpRelayConfig; import com.vmware.nsx_policy.model.EnforcementPointListResult; import com.vmware.nsx_policy.model.LocaleServicesListResult; +import com.vmware.nsx_policy.model.PolicyNatRule; import com.vmware.nsx_policy.model.Segment; import com.vmware.nsx_policy.model.SegmentSubnet; import com.vmware.nsx_policy.model.SiteListResult; @@ -108,6 +110,16 @@ public class NsxApiClient { nsxService = apiClient::createStub; } + public void createTier1NatRule(String tier1GatewayName, String natId, String natRuleId, + String action, String translatedIp) { + NatRules natRulesService = (NatRules) nsxService.apply(NatRules.class); + PolicyNatRule natPolicy = new PolicyNatRule.Builder() + .setAction(action) + .setTranslatedNetwork(translatedIp) + .build(); + natRulesService.patch(tier1GatewayName, natId, natRuleId, natPolicy); + } + public void createDhcpRelayConfig(String dhcpRelayConfigName, List addresses) { try { DhcpRelayConfigs service = (DhcpRelayConfigs) nsxService.apply(DhcpRelayConfigs.class); diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java index f67b8cb5c3a..161d67acdfd 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java @@ -298,7 +298,8 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS DomainVO domain = getDomainFromAccount(account); Network.Service[] services = { Network.Service.SourceNat }; boolean sourceNatEnabled = vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), services); - return nsxService.createVpcNetwork(vpc.getZoneId(), account.getId(), domain.getId(), vpc.getId(), vpc.getName(), sourceNatEnabled); + // return nsxService.createVpcNetwork(vpc.getZoneId(), account.getId(), domain.getId(), vpc.getId(), vpc.getName(), sourceNatEnabled); + return true; } @Override diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java new file mode 100644 index 00000000000..f7c02c0e73c --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java @@ -0,0 +1,118 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.network.Network; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.guru.PublicNetworkGuru; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateNsxTier1NatRuleCommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.apache.cloudstack.utils.NsxHelper; +import org.apache.log4j.Logger; + +import javax.inject.Inject; + +public class NsxPublicNetworkGuru extends PublicNetworkGuru { + + @Inject + private VlanDetailsDao vlanDetailsDao; + @Inject + private VpcDao vpcDao; + @Inject + private VpcOfferingServiceMapDao vpcOfferingServiceMapDao; + @Inject + private NsxControllerUtils nsxControllerUtils; + @Inject + private NsxService nsxService; + + private static final Logger s_logger = Logger.getLogger(NsxPublicNetworkGuru.class); + + public NsxPublicNetworkGuru() { + super(); + } + + @Override + public NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException { + s_logger.debug("NSX Public network guru: allocate"); + // NicProfile createdProfile = super.allocate(network, nic, vm); + + IPAddressVO ipAddress = _ipAddressDao.findByIp(nic.getIPv4Address()); + if (ipAddress == null) { + String err = String.format("Cannot find the IP address %s", nic.getIPv4Address()); + s_logger.error(err); + throw new CloudRuntimeException(err); + } + Long vpcId = ipAddress.getVpcId(); + if (vpcId == null) { + // TODO: Pass network.getId() to support isolated networks + throw new CloudRuntimeException("TODO: Add support for isolated networks public network"); + } + VpcVO vpc = vpcDao.findById(vpcId); + if (vpc == null) { + String err = String.format("Cannot find a VPC with ID %s", vpcId); + s_logger.error(err); + throw new CloudRuntimeException(err); + } + + if (ipAddress.isSourceNat() && !ipAddress.isForSystemVms()) { + VlanDetailsVO detail = vlanDetailsDao.findDetail(ipAddress.getVlanId(), ApiConstants.NSX_DETAIL_KEY); + if (detail != null && detail.getValue().equalsIgnoreCase("true")) { + long accountId = vpc.getAccountId(); + long domainId = vpc.getDomainId(); + long dataCenterId = vpc.getZoneId(); + boolean isForVpc = true; + long resourceId = isForVpc ? vpc.getId() : network.getId(); + Network.Service[] services = { Network.Service.SourceNat }; + boolean sourceNatEnabled = vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), services); + + s_logger.info(String.format("Creating Tier 1 Gateway for VPC %s", vpc.getName())); + boolean result = nsxService.createVpcNetwork(dataCenterId, accountId, domainId, resourceId, vpc.getName(), sourceNatEnabled); + if (!result) { + String msg = String.format("Error creating Tier 1 Gateway for VPC %s", vpc.getName()); + s_logger.error(msg); + throw new CloudRuntimeException(msg); + } + + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(domainId, accountId, dataCenterId, resourceId, isForVpc); + String translatedIp = ipAddress.getAddress().addr(); + s_logger.debug(String.format("Creating NSX Nat Rule for Tier1 GW %s for translated IP %s", tier1GatewayName, translatedIp)); + String natRuleId = NsxControllerUtils.getNsxNatRuleId(domainId, accountId, dataCenterId, resourceId, isForVpc); + CreateNsxTier1NatRuleCommand cmd = NsxHelper.createNsxNatRuleCommand(domainId, accountId, dataCenterId, tier1GatewayName, "SNAT", translatedIp, natRuleId); + NsxAnswer nsxAnswer = nsxControllerUtils.sendNsxCommand(cmd, dataCenterId); + if (!nsxAnswer.getResult()) { + String msg = String.format("Could not create NSX Nat Rule on Tier1 Gateway %s for IP %s", tier1GatewayName, translatedIp); + s_logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + } + return nic; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxService.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxService.java index 98ff8e770da..7365ea176cc 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxService.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxService.java @@ -17,4 +17,6 @@ package org.apache.cloudstack.service; public interface NsxService { + + boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled); } diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java index f26c7bf39ee..05614f66ba6 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java @@ -39,6 +39,11 @@ public class NsxControllerUtils { @Inject NsxProviderDao nsxProviderDao; + public static String getNsxNatRuleId(long domainId, long accountId, long dataCenterId, long resourceId, boolean isForVpc) { + String resourcePrefix = isForVpc ? "V" : "N"; + return String.format("D%s-A%s-Z%s-%s%s-NAT", domainId, accountId, dataCenterId, resourcePrefix, resourceId); + } + public NsxAnswer sendNsxCommand(NsxCommand cmd, long zoneId) throws IllegalArgumentException { NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId); diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxHelper.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxHelper.java index 848133c7e0a..819b68dd2fb 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxHelper.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxHelper.java @@ -25,6 +25,7 @@ import com.cloud.user.Account; import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand; import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.CreateNsxTier1NatRuleCommand; import java.util.List; @@ -46,4 +47,10 @@ public class NsxHelper { Long networkResourceId, String networkResourceName, boolean isResourceVpc) { return new CreateNsxTier1GatewayCommand(domain.getId(), account.getId(), zone.getId(), networkResourceId, networkResourceName, isResourceVpc, false); } + + public static CreateNsxTier1NatRuleCommand createNsxNatRuleCommand(long domainId, long accountId, long zoneId, + String tier1Gateway, String action, String ipAddress, + String natRuleId) { + return new CreateNsxTier1NatRuleCommand(domainId, accountId, zoneId, tier1Gateway, action, ipAddress, natRuleId); + } } diff --git a/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/spring-nsx-context.xml b/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/spring-nsx-context.xml index 43e03657b7a..d7a2ae2e765 100644 --- a/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/spring-nsx-context.xml +++ b/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/spring-nsx-context.xml @@ -31,6 +31,9 @@ + + + diff --git a/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java index 5e157999357..1f6c49e4b1a 100644 --- a/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java @@ -70,7 +70,7 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru { @Inject NetworkOrchestrationService _networkMgr; @Inject - IPAddressDao _ipAddressDao; + protected IPAddressDao _ipAddressDao; @Inject IpAddressManager _ipAddrMgr; @Inject @@ -96,7 +96,7 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru { } protected boolean canHandle(NetworkOffering offering) { - return isMyTrafficType(offering.getTrafficType()) && offering.isSystemOnly(); + return isMyTrafficType(offering.getTrafficType()) && offering.isSystemOnly() && !offering.isForNsx(); } @Override