From d7afd53164f9154d8e0fa033b9984dc8f5fe4afa Mon Sep 17 00:00:00 2001 From: nvazquez Date: Thu, 26 Oct 2023 01:16:59 -0300 Subject: [PATCH] Reserve source NAT IP when its not passed for NSX VPC --- .../com/cloud/network/NetworkService.java | 3 + .../com/cloud/network/NetworkServiceImpl.java | 56 +++++++++++++++++++ .../com/cloud/network/vpc/VpcManagerImpl.java | 17 ++++++ .../com/cloud/vpc/MockNetworkManagerImpl.java | 6 ++ 4 files changed, 82 insertions(+) diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index 099d73d5fcb..fedde27835e 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -19,6 +19,7 @@ package com.cloud.network; import java.util.List; import java.util.Map; +import com.cloud.dc.DataCenter; import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; @@ -85,6 +86,8 @@ public interface NetworkService { IpAddress reserveIpAddress(Account account, Boolean displayIp, Long ipAddressId) throws ResourceAllocationException; + IpAddress reserveIpAddressWithVlanDetail(Account account, DataCenter zone, Boolean displayIp, String vlanDetailKey) throws ResourceAllocationException; + boolean releaseReservedIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; boolean releaseIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index 3d8c9e10fec..f50851aeeb3 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -41,6 +41,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 com.cloud.offering.ServiceOffering; import com.cloud.service.dao.ServiceOfferingDao; import org.apache.cloudstack.acl.ControlledEntity.ACLType; @@ -281,6 +283,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @Inject VlanDao _vlanDao = null; @Inject + private VlanDetailsDao vlanDetailsDao; + @Inject IPAddressDao _ipAddressDao = null; @Inject AccountDao _accountDao = null; @@ -1134,6 +1138,58 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C return ipVO; } + @Override + public IpAddress reserveIpAddressWithVlanDetail(Account account, DataCenter zone, Boolean displayIp, String vlanDetailKey) throws ResourceAllocationException { + // verify permissions + Account caller = CallContext.current().getCallingAccount(); + _accountMgr.checkAccess(caller, null, true, account); + + VlanVO vlan = findOneVlanRangeMatchingVlanDetailKey(zone, vlanDetailKey); + if (vlan == null) { + String msg = String.format("Cannot find any vlan matching the detail key %s on zone %s", vlanDetailKey, zone.getName()); + s_logger.error(msg); + throw new CloudRuntimeException(msg); + } + + List freeIps = _ipAddressDao.listByVlanIdAndState(vlan.getId(), State.Free); + if (CollectionUtils.isEmpty(freeIps)) { + String msg = String.format("Cannot find any free IP matching on the VLAN range %s on zone %s", vlan.getIpRange(), zone.getName()); + s_logger.error(msg); + throw new CloudRuntimeException(msg); + } + + Collections.shuffle(freeIps); + IPAddressVO selectedIp = freeIps.get(0); + + selectedIp.setAllocatedTime(new Date()); + selectedIp.setAllocatedToAccountId(account.getAccountId()); + selectedIp.setAllocatedInDomainId(account.getDomainId()); + selectedIp.setState(State.Reserved); + if (displayIp != null) { + selectedIp.setDisplay(displayIp); + } + selectedIp = _ipAddressDao.persist(selectedIp); + + Long ipDedicatedAccountId = getIpDedicatedAccountId(selectedIp.getVlanId()); + if (ipDedicatedAccountId == null) { + _resourceLimitMgr.incrementResourceCount(account.getId(), Resource.ResourceType.public_ip); + } + + return selectedIp; + } + + private VlanVO findOneVlanRangeMatchingVlanDetailKey(DataCenter zone, String vlanDetailKey) { + List zoneVlans = _vlanDao.listByZone(zone.getId()); + for (VlanVO zoneVlan : zoneVlans) { + VlanDetailsVO detail = vlanDetailsDao.findDetail(zoneVlan.getId(), vlanDetailKey); + if (detail != null && detail.getValue().equalsIgnoreCase("true")) { + s_logger.debug(String.format("Found the VLAN range %s is set for NSX on zone %s", zoneVlan.getIpRange(), zone.getName())); + return zoneVlan; + } + } + return null; + } + private Long getIpDedicatedAccountId(Long vlanId) { List accountVlanMaps = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanId); if (CollectionUtils.isNotEmpty(accountVlanMaps)) { diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java index 6725324aed6..79f2cf131cf 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -1171,11 +1171,23 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return vpc; } + private boolean isVpcForNsx(Vpc vpc) { + VpcOfferingServiceMapVO mapVO = _vpcOffSvcMapDao.findByServiceProviderAndOfferingId(Service.SourceNat.getName(), Provider.Nsx.getName(), vpc.getVpcOfferingId()); + if (mapVO != null) { + s_logger.debug(String.format("The VPC %s is NSX-based and supports the %s service", vpc.getName(), Service.SourceNat.getName())); + } + return mapVO != null; + } + private void allocateSourceNatIp(Vpc vpc, String sourceNatIP) { Account account = _accountMgr.getAccount(vpc.getAccountId()); DataCenter zone = _dcDao.findById(vpc.getZoneId()); // reserve this ip and then try { + if (isVpcForNsx(vpc) && org.apache.commons.lang3.StringUtils.isBlank(sourceNatIP)) { + s_logger.debug(String.format("Reserving a source NAT IP for NSX VPC %s", vpc.getName())); + sourceNatIP = reserveSourceNatIpForNsxVpc(account, zone, vpc); + } IpAddress ip = _ipAddrMgr.allocateIp(account, false, CallContext.current().getCallingAccount(), CallContext.current().getCallingUserId(), zone, null, sourceNatIP); this.associateIPToVpc(ip.getId(), vpc.getId()); } catch (ResourceAllocationException | ResourceUnavailableException | InsufficientAddressCapacityException e){ @@ -1183,6 +1195,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } + private String reserveSourceNatIpForNsxVpc(Account account, DataCenter zone, Vpc vpc) throws ResourceAllocationException { + IpAddress ipAddress = _ntwkSvc.reserveIpAddressWithVlanDetail(account, zone, true, ApiConstants.NSX_DETAIL_KEY); + return ipAddress.getAddress().addr(); + } + @DB protected Vpc createVpc(final Boolean displayVpc, final VpcVO vpc) { final String cidr = vpc.getCidr(); diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index da251cb502b..8e8f3e36428 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.vpc; +import com.cloud.dc.DataCenter; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; @@ -176,6 +177,11 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches return null; } + @Override + public IpAddress reserveIpAddressWithVlanDetail(Account account, DataCenter zone, Boolean displayIp, String vlanDetailKey) throws ResourceAllocationException { + return null; + } + @Override public boolean releaseReservedIpAddress(long ipAddressId) throws InsufficientAddressCapacityException { return false;