Create source NAT rule on VR NIC allocation

This commit is contained in:
nvazquez 2023-10-27 20:32:06 -03:00
parent d7afd53164
commit a997a9cd9e
No known key found for this signature in database
GPG Key ID: 656E1BCC8CB54F84
11 changed files with 242 additions and 3 deletions

View File

@ -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<NetworkGuru> 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<NicProfile, Integer>(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<NetworkVO, VpcVO> networks, Network network, NicVO vo) {
if (TrafficType.Public == network.getTrafficType()) {
if (networks == null) {

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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<String> addresses) {
try {
DhcpRelayConfigs service = (DhcpRelayConfigs) nsxService.apply(DhcpRelayConfigs.class);

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -31,6 +31,9 @@
<bean id="nsxGuestNetworkGuru" class="org.apache.cloudstack.service.NsxGuestNetworkGuru">
<property name="name" value="NsxGuestNetworkGuru" />
</bean>
<bean id="NsxPublicNetworkGuru" class="org.apache.cloudstack.service.NsxPublicNetworkGuru">
<property name="name" value="NsxPublicNetworkGuru" />
</bean>
<bean id="nsxProviderService" class="org.apache.cloudstack.service.NsxProviderServiceImpl"/>
<bean id="nsxService" class="org.apache.cloudstack.service.NsxServiceImpl"/>
<bean id="nsxControllerUtils" class="org.apache.cloudstack.utils.NsxControllerUtils" />

View File

@ -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