Skip to content

Commit e4dcfd5

Browse files
committed
server: assign Public IP to VPC VR and enable static nat if VR is not Source NAT
1 parent 9e7272c commit e4dcfd5

File tree

7 files changed

+132
-13
lines changed

7 files changed

+132
-13
lines changed

engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.cloud.network.Network.Service;
3636
import com.cloud.network.PhysicalNetwork;
3737
import com.cloud.network.addr.PublicIp;
38+
import com.cloud.network.dao.IPAddressVO;
3839
import com.cloud.offering.NetworkOffering;
3940
import com.cloud.user.Account;
4041

@@ -180,4 +181,8 @@ public interface VpcManager {
180181
List<StaticRouteProfile> getVpcStaticRoutes(List<? extends StaticRoute> routes);
181182

182183
boolean isProviderSupportServiceInVpc(long vpcId, Service service, Provider provider);
184+
185+
IPAddressVO getIpAddressForVpcVR(Vpc vpc, IPAddressVO ipAddress);
186+
187+
boolean enableStaticNatForVpcVR(Vpc vpc, IPAddressVO ipAddress);
183188
}

engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ public class IPAddressVO implements IpAddress {
116116
@Column(name = "forsystemvms")
117117
private boolean forSystemVms = false;
118118

119+
@Column(name = "for_virtual_router")
120+
private boolean forVirtualRouter = false;
121+
119122
@Column(name= GenericDao.REMOVED_COLUMN)
120123
private Date removed;
121124

@@ -385,4 +388,12 @@ public void setRuleState(State ruleState) {
385388
public boolean isForSystemVms() {
386389
return forSystemVms;
387390
}
391+
392+
public boolean isForVirtualRouter() {
393+
return forVirtualRouter;
394+
}
395+
396+
public void setForVirtualRouter(boolean forVirtualRouter) {
397+
this.forVirtualRouter = forVirtualRouter;
398+
}
388399
}

engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@ CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.mshost_peer', 'fk_mshost_peer__
3434
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'last_id', 'bigint(20) unsigned DEFAULT NULL');
3535

3636
-- Add next_hop to the static_routes table
37-
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.static_routes', 'next_hop', 'varchar(50) COMMENT "next hop of the static route" AFTER `vpc_gateway_id`');
37+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.static_routes', 'next_hop', 'varchar(50) COMMENT "next hop of the static route" AFTER `vpc_gateway_id`');
38+
39+
-- Add `for_virtual_router` to `user_ip_address` table
40+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_ip_address', 'for_virtual_router', 'tinyint(1) DEFAULT 0 COMMENT "If the IP is used by Virtual Router to expose services"');

server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,8 @@ private boolean enableStaticNat(long ipId, long vmId, long networkId, boolean is
480480
throw new InvalidParameterValueException("Unable to create static nat rule; StaticNat service is not " + "supported in network with specified id");
481481
}
482482

483+
VMInstanceVO vm = _vmInstanceDao.findById(vmId);
483484
if (!isSystemVm) {
484-
VMInstanceVO vm = _vmInstanceDao.findById(vmId);
485485
if (vm == null) {
486486
throw new InvalidParameterValueException("Can't enable static nat for the address id=" + ipId + ", invalid virtual machine id specified (" + vmId +
487487
").");
@@ -593,6 +593,9 @@ private boolean enableStaticNat(long ipId, long vmId, long networkId, boolean is
593593

594594
ipAddress.setOneToOneNat(true);
595595
ipAddress.setAssociatedWithVmId(vmId);
596+
if (vm != null && Type.DomainRouter.equals(vm.getType())) {
597+
ipAddress.setForVirtualRouter(true);
598+
}
596599

597600
ipAddress.setVmIp(dstIp);
598601
if (_ipAddressDao.update(ipAddress.getId(), ipAddress)) {
@@ -1347,6 +1350,7 @@ public boolean disableStaticNat(long ipId, Account caller, long callerUserId, bo
13471350
ipAddress.setAssociatedWithVmId(null);
13481351
ipAddress.setRuleState(null);
13491352
ipAddress.setVmIp(null);
1353+
ipAddress.setForVirtualRouter(false);
13501354
if (isIpSystem && !releaseIpIfElastic) {
13511355
ipAddress.setSystem(false);
13521356
}

server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@
5050
import com.cloud.dc.Vlan;
5151
import com.cloud.network.dao.NsxProviderDao;
5252
import com.cloud.network.element.NsxProviderVO;
53+
import com.cloud.network.rules.RulesManager;
5354
import com.cloud.resourcelimit.CheckedReservation;
55+
import com.cloud.vm.VMInstanceVO;
56+
import com.cloud.vm.dao.VMInstanceDao;
5457
import com.google.common.collect.Sets;
5558
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
5659
import org.apache.cloudstack.alert.AlertService;
@@ -292,6 +295,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
292295
private NsxProviderDao nsxProviderDao;
293296
@Inject
294297
RoutedIpv4Manager routedIpv4Manager;
298+
@Inject
299+
DomainRouterDao domainRouterDao;
300+
@Inject
301+
RulesManager rulesManager;
302+
@Inject
303+
VMInstanceDao vmInstanceDao;
295304

296305
private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker"));
297306
private List<VpcProvider> vpcElements = null;
@@ -3581,4 +3590,74 @@ public List<StaticRouteProfile> getVpcStaticRoutes(final List<? extends StaticRo
35813590
public boolean isProviderSupportServiceInVpc(long vpcId, Service service, Provider provider) {
35823591
return _vpcSrvcDao.canProviderSupportServiceInVpc(vpcId, service, provider);
35833592
}
3593+
3594+
@Override
3595+
public IPAddressVO getIpAddressForVpcVR(Vpc vpc, IPAddressVO ipAddress) {
3596+
// Validate if the IP address is associated to a VPC VR
3597+
final List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpc.getId(), null);
3598+
IPAddressVO ipAddressForVR = ips.stream().filter(ip -> ip.isForVirtualRouter()).findFirst().orElse(null);
3599+
if (ipAddressForVR != null) {
3600+
if (ipAddress != null && ipAddressForVR.getId() != ipAddress.getId()) {
3601+
throw new InvalidParameterValueException(String.format("Cannot assign Public IP %s to VPC VR as %s has been associated to the VPC VR.",
3602+
ipAddress.getAddress().addr(), ipAddressForVR.getAddress().addr()));
3603+
}
3604+
return ipAddressForVR;
3605+
} else if (ipAddress != null) {
3606+
return ipAddress;
3607+
}
3608+
3609+
Account account = _accountMgr.getAccount(vpc.getAccountId());
3610+
DataCenter zone = _dcDao.findById(vpc.getZoneId());
3611+
try {
3612+
IpAddress ip = _ipAddrMgr.allocateIp(account, false, CallContext.current().getCallingAccount(),
3613+
CallContext.current().getCallingUserId(), zone, null, null);
3614+
this.associateIPToVpc(ip.getId(), vpc.getId());
3615+
return _ipAddressDao.findById(ip.getId());
3616+
} catch (InsufficientAddressCapacityException | ResourceAllocationException | ResourceUnavailableException ex) {
3617+
throw new InvalidParameterValueException("Cannot assign Public IP to VPC VR: " + ex.getMessage());
3618+
}
3619+
}
3620+
3621+
@Override
3622+
public boolean enableStaticNatForVpcVR(Vpc vpc, IPAddressVO ipAddress) {
3623+
// Get VPC networks
3624+
List<NetworkVO> networks = _ntwkDao.listByVpc(vpc.getId());
3625+
if (CollectionUtils.isEmpty(networks)) {
3626+
throw new InvalidParameterValueException("Cannot assign Public IP to VPC VR as there is no VPC networks");
3627+
}
3628+
3629+
List<DomainRouterVO> routers = domainRouterDao.listByVpcId(vpc.getId());
3630+
if (CollectionUtils.isEmpty(routers)) {
3631+
throw new InvalidParameterValueException("Cannot assign Public IP to VPC VR as there is no VPC VR");
3632+
}
3633+
3634+
if (ipAddress.isOneToOneNat()) {
3635+
Long vmId = ipAddress.getAssociatedWithVmId();
3636+
VMInstanceVO vm = vmInstanceDao.findById(vmId);
3637+
if (vm != null && VirtualMachine.Type.DomainRouter.equals(vm.getType())) {
3638+
logger.debug("The Public IP has been already associated with VM " + vm);
3639+
return true;
3640+
} else {
3641+
throw new InvalidParameterValueException("Cannot assign Public IP to VPC VR as the Public IP is associated with vm with id=" + vmId);
3642+
}
3643+
}
3644+
3645+
for (NetworkVO network : networks) {
3646+
for (DomainRouterVO router : routers) {
3647+
NicVO nic = nicDao.findByNtwkIdAndInstanceId(network.getId(), router.getId());
3648+
if (nic != null) {
3649+
try {
3650+
// Enable static nat for the VPC VR
3651+
rulesManager.enableStaticNat(ipAddress.getId(), router.getId(), network.getId(), null);
3652+
} catch (ResourceUnavailableException | NetworkRuleConflictException ex) {
3653+
throw new CloudRuntimeException("Failed to enable static nat for VPC VR due to " + ex.getMessage());
3654+
}
3655+
return true;
3656+
}
3657+
}
3658+
}
3659+
3660+
logger.debug("Failed to enable static nat for VPC VR as there is no VPC VR on VPC networks");
3661+
return false;
3662+
}
35843663
}

server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,11 @@ public RemoteAccessVpn createRemoteAccessVpn(final long publicIpId, String ipRan
243243
throw new InvalidParameterValueException("Vpn service is not supported in network id=" + ipAddr.getAssociatedWithNetworkId());
244244
}
245245
cidr = NetUtils.getCidr(network.getCidr());
246-
validateIpAddressForVpnServiceOnNetwork(networkId, ipAddress);
246+
validateIpAddressForVpnServiceOnNetwork(network, ipAddress);
247247
} else {
248248
Vpc vpc = _vpcDao.findById(vpcId);
249249
cidr = NetUtils.getCidr(vpc.getCidr());
250-
validateIpAddressForVpnServiceOnVpc(vpcId, ipAddress);
250+
validateIpAddressForVpnServiceOnVpc(vpc, ipAddress);
251251
}
252252

253253
String[] guestIpRange = NetUtils.getIpRangeFromCidr(cidr.first(), cidr.second());
@@ -279,7 +279,8 @@ public RemoteAccessVpn createRemoteAccessVpn(final long publicIpId, String ipRan
279279
}
280280
}
281281

282-
private void validateIpAddressForVpnServiceOnNetwork(Long networkId, IPAddressVO ipAddress) {
282+
private void validateIpAddressForVpnServiceOnNetwork(Network network, IPAddressVO ipAddress) {
283+
Long networkId = network.getId();
283284
if (_networkMgr.isProviderSupportServiceInNetwork(networkId, Service.Dhcp, Network.Provider.VirtualRouter)) {
284285
// if VR is the VPN provider,
285286
// (1) if VR is Source NAT, the IP address must be used as Source NAT
@@ -291,10 +292,14 @@ private void validateIpAddressForVpnServiceOnNetwork(Long networkId, IPAddressVO
291292
if (!isVRSourceNat && ipAddress.isSourceNat()) {
292293
throw new InvalidParameterValueException("Vpn service can not be configured on the Source NAT IP of network id=" + ipAddress.getAssociatedWithNetworkId());
293294
}
295+
if (!isVRSourceNat) {
296+
throw new InvalidParameterValueException("Currently it is not supported to create Vpn service on a non-Source NAT IP of network id=" + ipAddress.getAssociatedWithNetworkId());
297+
}
294298
}
295299
}
296300

297-
private void validateIpAddressForVpnServiceOnVpc(Long vpcId, IPAddressVO ipAddress) {
301+
private void validateIpAddressForVpnServiceOnVpc(Vpc vpc, IPAddressVO ipAddress) {
302+
Long vpcId = vpc.getId();
298303
if (vpcManager.isProviderSupportServiceInVpc(vpcId, Service.Dhcp, Network.Provider.VPCVirtualRouter)) {
299304
// if VPC VR is the VPN provider,
300305
// (1) if VPC VR is Source NAT, the IP address must be used as Source NAT
@@ -306,6 +311,12 @@ private void validateIpAddressForVpnServiceOnVpc(Long vpcId, IPAddressVO ipAddre
306311
if (!isVpcVRSourceNat && ipAddress.isSourceNat()) {
307312
throw new InvalidParameterValueException("Vpn service can not be configured on the Source NAT IP of VPC id=" + ipAddress.getVpcId());
308313
}
314+
if (!isVpcVRSourceNat) {
315+
IPAddressVO ipAddressForVpcVR = vpcManager.getIpAddressForVpcVR(vpc, ipAddress);
316+
if (!vpcManager.enableStaticNatForVpcVR(vpc, ipAddressForVpcVR)) {
317+
throw new CloudRuntimeException("Failed to enable static nat for VPC VR as part of remote access vpn creation");
318+
}
319+
}
309320
}
310321
}
311322

server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.cloud.exception.InvalidParameterValueException;
4949
import com.cloud.exception.PermissionDeniedException;
5050
import com.cloud.exception.ResourceUnavailableException;
51+
import com.cloud.network.IpAddressManager;
5152
import com.cloud.network.Network;
5253
import com.cloud.network.Site2SiteCustomerGateway;
5354
import com.cloud.network.Site2SiteVpnConnection;
@@ -62,6 +63,8 @@
6263
import com.cloud.network.dao.Site2SiteVpnGatewayDao;
6364
import com.cloud.network.dao.Site2SiteVpnGatewayVO;
6465
import com.cloud.network.element.Site2SiteVpnServiceProvider;
66+
import com.cloud.network.vpc.Vpc;
67+
import com.cloud.network.vpc.VpcManager;
6568
import com.cloud.network.vpc.VpcVO;
6669
import com.cloud.network.vpc.VpcOfferingServiceMapVO;
6770
import com.cloud.network.vpc.dao.VpcDao;
@@ -110,6 +113,10 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
110113
VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
111114
@Inject
112115
private DomainRouterDao domainRouterDao;
116+
@Inject
117+
private IpAddressManager ipAddressManager;
118+
@Inject
119+
private VpcManager vpcManager;
113120

114121
int _connLimit;
115122
int _subnetsLimit;
@@ -158,14 +165,13 @@ private Long getIpAddressIdForVpn(Long vpcId, Long vpcOferingId) {
158165
if (mapForSourceNat == null && mapForVpn != null) {
159166
// Use Static NAT IP of VPC VR
160167
logger.debug(String.format("The VPC VR provides %s Service, however it does not provide %s service, trying to configure using IP of VPC VR", Network.Service.Vpn.getName(), Network.Service.SourceNat.getName()));
161-
List<DomainRouterVO> routers = domainRouterDao.listByVpcId(vpcId);
162-
for (DomainRouterVO router : routers) {
163-
List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVmId(router.getId());
164-
if (!ips.isEmpty()) {
165-
return ips.get(0).getId();
166-
}
168+
169+
Vpc vpc = _vpcDao.findById(vpcId);
170+
IPAddressVO ipAddressForVpcVR = vpcManager.getIpAddressForVpcVR(vpc, null);
171+
if (!vpcManager.enableStaticNatForVpcVR(vpc, ipAddressForVpcVR)) {
172+
throw new CloudRuntimeException("Failed to enable static nat for VPC VR as part of vpn gateway");
167173
}
168-
throw new CloudRuntimeException("Cannot found static nat ip for VR of vpc " + vpcId);
174+
return ipAddressForVpcVR.getId();
169175
} else {
170176
//Use source NAT ip for VPC
171177
List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpcId, true);

0 commit comments

Comments
 (0)