NSX: Create and delete NSX Static Nat rules

This commit is contained in:
Pearl Dsilva 2023-10-23 15:05:46 -04:00
parent 116d6c3c86
commit 9e51bdfaa9
7 changed files with 284 additions and 1 deletions

View File

@ -0,0 +1,76 @@
package org.apache.cloudstack.agent.api;
import java.util.Objects;
public class CreateNsxStaticNatCommand extends NsxCommand {
private long vpcId;
private String vpcName;
private long vmId;
private String publicIp;
private String vmIp;
public CreateNsxStaticNatCommand(long domainId, long accountId, long zoneId, long vpcId, String vpcName,
long vmId, String publicIp, String vmIp) {
super(domainId, accountId, zoneId);
this.vpcId = vpcId;
this.vpcName = vpcName;
this.vmId = vmId;
this.publicIp = publicIp;
this.vmIp = vmIp;
}
public long getVpcId() {
return vpcId;
}
public void setVpcId(long vpcId) {
this.vpcId = vpcId;
}
public String getVpcName() {
return vpcName;
}
public void setVpcName(String vpcName) {
this.vpcName = vpcName;
}
public long getVmId() {
return vmId;
}
public void setVmId(long vmId) {
this.vmId = vmId;
}
public String getPublicIp() {
return publicIp;
}
public void setPublicIp(String publicIp) {
this.publicIp = publicIp;
}
public String getVmIp() {
return vmIp;
}
public void setVmIp(String vmIp) {
this.vmIp = vmIp;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
CreateNsxStaticNatCommand that = (CreateNsxStaticNatCommand) o;
return vpcId == that.vpcId && Objects.equals(publicIp, that.publicIp) && Objects.equals(vmIp, that.vmIp);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), vpcId, publicIp, vmIp);
}
}

View File

@ -0,0 +1,43 @@
package org.apache.cloudstack.agent.api;
import java.util.Objects;
public class DeleteNsxStaticNatCommand extends NsxCommand {
private long vpcId;
private String vpcName;
public DeleteNsxStaticNatCommand(long domainId, long accountId, long zoneId, long vpcId, String vpcName) {
super(domainId, accountId, zoneId);
this.vpcId = vpcId;
this.vpcName = vpcName;
}
public long getVpcId() {
return vpcId;
}
public void setVpcId(long vpcId) {
this.vpcId = vpcId;
}
public String getVpcName() {
return vpcName;
}
public void setVpcName(String vpcName) {
this.vpcName = vpcName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
DeleteNsxStaticNatCommand that = (DeleteNsxStaticNatCommand) o;
return vpcId == that.vpcId && Objects.equals(vpcName, that.vpcName);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), vpcId, vpcName);
}
}

View File

@ -36,8 +36,10 @@ import org.apache.cloudstack.NsxAnswer;
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.CreateNsxStaticNatCommand;
import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand;
import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand;
import org.apache.cloudstack.agent.api.DeleteNsxStaticNatCommand;
import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand;
import org.apache.cloudstack.service.NsxApiClient;
import org.apache.cloudstack.utils.NsxControllerUtils;
@ -103,6 +105,10 @@ public class NsxResource implements ServerResource {
return executeRequest((CreateNsxTier1GatewayCommand) cmd);
} else if (cmd instanceof CreateNsxDhcpRelayConfigCommand) {
return executeRequest((CreateNsxDhcpRelayConfigCommand) cmd);
} else if (cmd instanceof CreateNsxStaticNatCommand) {
return executeRequest((CreateNsxStaticNatCommand) cmd);
} else if (cmd instanceof DeleteNsxStaticNatCommand) {
return executeRequest((DeleteNsxStaticNatCommand) cmd);
} else {
return Answer.createUnsupportedCommandAnswer(cmd);
}
@ -331,6 +337,34 @@ public class NsxResource implements ServerResource {
return new NsxAnswer(cmd, true, null);
}
private NsxAnswer executeRequest(CreateNsxStaticNatCommand cmd) {
String staticNatRuleName = NsxControllerUtils.getStaticNatRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(),
cmd.getVpcId());
String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(),
cmd.getVpcId());
try {
nsxApiClient.createStaticNatRule(cmd.getVpcName(), tier1GatewayName, staticNatRuleName, cmd.getPublicIp(), cmd.getVmIp());
} catch (Exception e) {
LOGGER.error(String.format("Failed to add NSX static NAT rule %s for network: %s", staticNatRuleName, cmd.getVpcName()));
return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage()));
}
return new NsxAnswer(cmd, true, null);
}
private NsxAnswer executeRequest(DeleteNsxStaticNatCommand cmd) {
String staticNatRuleName = NsxControllerUtils.getStaticNatRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(),
cmd.getVpcId());
String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(),
cmd.getVpcId());
try {
nsxApiClient.deleteStaticNatRule(cmd.getVpcName(), tier1GatewayName, staticNatRuleName);
} catch (Exception e) {
LOGGER.error(String.format("Failed to add NSX static NAT rule %s for network: %s", staticNatRuleName, cmd.getVpcName()));
return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage()));
}
return new NsxAnswer(cmd, true, null);
}
@Override
public boolean start() {
return true;

View File

@ -26,10 +26,14 @@ 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;
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.PolicyNat;
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;
@ -79,6 +83,16 @@ public class NsxApiClient {
private enum TransportType { OVERLAY, VLAN }
private enum NatId { USER, INTERNAL, DEFAULT }
private enum NatAction {SNAT, DNAT, REFLEXIVE}
private enum FirewallMatch {
MATCH_INTERNAL_ADDRESS,
MATCH_EXTERNAL_ADDRESS,
BYPASS
}
public enum RouteAdvertisementType { TIER1_STATIC_ROUTES, TIER1_CONNECTED, TIER1_NAT,
TIER1_LB_VIP, TIER1_LB_SNAT, TIER1_DNS_FORWARDER_IP, TIER1_IPSEC_LOCAL_ENDPOINT
}
@ -290,4 +304,43 @@ public class NsxApiClient {
throw new CloudRuntimeException(msg);
}
}
public void createStaticNatRule(String vpcName, String tier1GatewayName,
String ruleName, String publicIp, String vmIp) {
try {
NatRules natService = (NatRules) nsxService.apply(NatRules.class);
PolicyNatRule rule = new PolicyNatRule.Builder()
.setId(ruleName)
.setDisplayName(ruleName)
.setAction(NatAction.DNAT.name())
.setFirewallMatch(FirewallMatch.MATCH_INTERNAL_ADDRESS.name())
.setDestinationNetwork(publicIp)
.setTranslatedNetwork(vmIp)
.setEnabled(true)
.build();
LOGGER.debug(String.format("Creating NSX static NAT rule %s for tier-1 gateway %s (VPC: %s)", ruleName, tier1GatewayName, vpcName));
natService.patch(tier1GatewayName, NatId.USER.name(), ruleName, rule);
} catch (Error error) {
ApiError ae = error.getData()._convertTo(ApiError.class);
String msg = String.format("Error creating NSX Static NAT rule %s for tier-1 gateway %s (VPC: %s), due to %s",
ruleName, tier1GatewayName, vpcName, ae.getErrorMessage());
LOGGER.error(msg);
throw new CloudRuntimeException(msg);
}
}
public void deleteStaticNatRule(String vpcName, String tier1GatewayName, String ruleName) {
try {
NatRules natService = (NatRules) nsxService.apply(NatRules.class);
LOGGER.debug(String.format("Deleting NSX static NAT rule %s for tier-1 gateway %s (VPC: %s)", ruleName, tier1GatewayName, vpcName));
natService.delete(tier1GatewayName, NatId.USER.name(), ruleName);
} catch (Error error) {
ApiError ae = error.getData()._convertTo(ApiError.class);
String msg = String.format("Failed to delete NSX Static NAT rule %s for tier-1 gateway %s (VPC: %s), due to %s",
ruleName, tier1GatewayName, vpcName, ae.getErrorMessage());
LOGGER.error(msg);
throw new CloudRuntimeException(msg);
}
}
}

View File

@ -41,13 +41,19 @@ import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
import com.cloud.network.Networks;
import com.cloud.network.PhysicalNetworkServiceProvider;
import com.cloud.network.PublicIpAddress;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.PhysicalNetworkDao;
import com.cloud.network.dao.PhysicalNetworkVO;
import com.cloud.network.element.DhcpServiceProvider;
import com.cloud.network.element.DnsServiceProvider;
import com.cloud.network.element.IpDeployer;
import com.cloud.network.element.StaticNatServiceProvider;
import com.cloud.network.element.VpcProvider;
import com.cloud.network.rules.StaticNat;
import com.cloud.network.vpc.NetworkACLItem;
import com.cloud.network.vpc.PrivateGateway;
import com.cloud.network.vpc.StaticRouteProfile;
@ -62,9 +68,12 @@ import com.cloud.user.AccountManager;
import com.cloud.utils.Pair;
import com.cloud.utils.component.AdapterBase;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.VMInstanceDao;
import net.sf.ehcache.config.InvalidConfigurationException;
import org.apache.cloudstack.StartupNsxCommand;
import org.apache.log4j.Logger;
@ -81,7 +90,7 @@ import java.util.function.LongFunction;
@Component
public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider,
ResourceStateAdapter, Listener {
StaticNatServiceProvider, IpDeployer, ResourceStateAdapter, Listener {
@Inject
AccountManager accountMgr;
@ -101,6 +110,10 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS
NetworkModel networkModel;
@Inject
DomainDao domainDao;
@Inject
IPAddressDao ipAddressDao;
@Inject
VMInstanceDao vmInstanceDao;
private static final Logger LOGGER = Logger.getLogger(NsxElement.class);
@ -172,6 +185,11 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS
return capabilities;
}
@Override
public boolean applyIps(Network network, List<? extends PublicIpAddress> ipAddress, Set<Network.Service> services) throws ResourceUnavailableException {
return true;
}
@Override
public Network.Provider getProvider() {
return Network.Provider.Nsx;
@ -412,4 +430,32 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS
}
private final LongFunction<DataCenterVO> zoneFunction = zoneId -> dataCenterDao.findById(zoneId);
@Override
public IpDeployer getIpDeployer(Network network) {
return this;
}
@Override
public boolean applyStaticNats(Network config, List<? extends StaticNat> rules) throws ResourceUnavailableException {
for(StaticNat staticNat : rules) {
long sourceIpAddressId = staticNat.getSourceIpAddressId();
IPAddressVO ipAddressVO = ipAddressDao.findByIdIncludingRemoved(sourceIpAddressId);
VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(ipAddressVO.getAssociatedWithVmId());
// floating ip is released when nic was deleted
if (vm == null || networkModel.getNicInNetworkIncludingRemoved(vm.getId(), config.getId()) == null) {
continue;
}
Nic nic = networkModel.getNicInNetworkIncludingRemoved(vm.getId(), config.getId());
Network publicNetwork = networkModel.getSystemNetworkByZoneAndTrafficType(config.getDataCenterId(), Networks.TrafficType.Public);
if (!staticNat.isForRevoke()) {
return nsxService.createStaticNatRule(config.getDataCenterId(), config.getDomainId(), config.getAccountId(),
config.getVpcId(), vm.getId(), ipAddressVO.getAddress().addr(), staticNat.getDestIpAddress());
} else {
return nsxService.deleteStaticNatRule(config.getDataCenterId(), config.getDomainId(), config.getAccountId(),
config.getVpcId());
}
}
return false;
}
}

View File

@ -19,9 +19,12 @@ package org.apache.cloudstack.service;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.NsxAnswer;
import org.apache.cloudstack.agent.api.CreateNsxStaticNatCommand;
import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand;
import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand;
import org.apache.cloudstack.agent.api.DeleteNsxStaticNatCommand;
import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand;
import org.apache.cloudstack.utils.NsxControllerUtils;
@ -59,4 +62,27 @@ public class NsxServiceImpl implements NsxService {
NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteNsxSegmentCommand, network.getDataCenterId());
return result.getResult();
}
public boolean createStaticNatRule(long zoneId, long accountId, long domainId, long vpcId,
long vmId, String publicIp, String vmIp) {
VpcVO vpc = vpcDao.findById(vpcId);
if (Objects.isNull(vpc)) {
throw new CloudRuntimeException(String.format("Failed to find VPC with id: %s", vpcId));
}
CreateNsxStaticNatCommand createNsxStaticNatCommand = new CreateNsxStaticNatCommand(domainId, accountId, zoneId,
vpcId, vpc.getName(), vmId, publicIp, vmIp);
NsxAnswer result = nsxControllerUtils.sendNsxCommand(createNsxStaticNatCommand, zoneId);
return result.getResult();
}
public boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, long vpcId) {
VpcVO vpc = vpcDao.findById(vpcId);
if (Objects.isNull(vpc)) {
throw new CloudRuntimeException(String.format("Failed to find VPC with id: %s", vpcId));
}
DeleteNsxStaticNatCommand deleteNsxStaticNatCommand = new DeleteNsxStaticNatCommand(domainId, accountId, zoneId,
vpcId, vpc.getName());
NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteNsxStaticNatCommand, zoneId);
return result.getResult();
}
}

View File

@ -75,4 +75,9 @@ public class NsxControllerUtils {
}
return String.format("D%s-A%s-Z%s-V%s-S%s-%s", domainId, accountId, zoneId, vpcId, networkId, suffix);
}
public static String getStaticNatRuleName(long zoneId, long domainId, long accountId, Long vpcId) {
String suffix = "-STATICNAT";
return getTier1GatewayName(domainId, accountId, zoneId, vpcId) + suffix;
}
}