Skip to content

Commit 7c7e8cc

Browse files
committed
Flexibilized public IP allocation
1 parent 7632814 commit 7c7e8cc

File tree

4 files changed

+93
-38
lines changed

4 files changed

+93
-38
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,21 @@
1919
import com.cloud.network.vo.PublicIpQuarantineVO;
2020
import com.cloud.utils.db.GenericDao;
2121

22+
import java.util.Date;
23+
import java.util.List;
24+
2225
public interface PublicIpQuarantineDao extends GenericDao<PublicIpQuarantineVO, Long> {
2326

2427
PublicIpQuarantineVO findByPublicIpAddressId(long publicIpAddressId);
2528

2629
PublicIpQuarantineVO findByIpAddress(String publicIpAddress);
30+
31+
/**
32+
* Returns a list of public IP addresses that are actively quarantined at the specified date and the previous owner differs from the specified user.
33+
*
34+
* @param userId used to check against the IP's previous owner.
35+
* @param date used to check if the quarantine is active;
36+
* @return a list of PublicIpQuarantineVOs
37+
*/
38+
List<PublicIpQuarantineVO> listQuarantinedIpAddressesToUser(Long userId, Date date);
2739
}

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,19 @@
2626

2727
import javax.annotation.PostConstruct;
2828
import javax.inject.Inject;
29+
import java.util.Date;
30+
import java.util.List;
2931

3032
@Component
3133
public class PublicIpQuarantineDaoImpl extends GenericDaoBase<PublicIpQuarantineVO, Long> implements PublicIpQuarantineDao {
3234
private SearchBuilder<PublicIpQuarantineVO> publicIpAddressByIdSearch;
3335

3436
private SearchBuilder<IPAddressVO> ipAddressSearchBuilder;
3537

38+
private SearchBuilder<PublicIpQuarantineVO> quarantinedIpAddressesSearch;
39+
3640
@Inject
37-
IPAddressDao ipAddressDao;
41+
private IPAddressDao ipAddressDao;
3842

3943
@PostConstruct
4044
public void init() {
@@ -47,8 +51,18 @@ public void init() {
4751
publicIpAddressByIdSearch.join("quarantineJoin", ipAddressSearchBuilder, ipAddressSearchBuilder.entity().getId(),
4852
publicIpAddressByIdSearch.entity().getPublicIpAddressId(), JoinBuilder.JoinType.INNER);
4953

54+
quarantinedIpAddressesSearch = createSearchBuilder();
55+
quarantinedIpAddressesSearch.and("previousOwnerId", quarantinedIpAddressesSearch.entity().getPreviousOwnerId(), SearchCriteria.Op.NEQ);
56+
quarantinedIpAddressesSearch.and();
57+
quarantinedIpAddressesSearch.op("removedIsNull", quarantinedIpAddressesSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
58+
quarantinedIpAddressesSearch.and("endDate", quarantinedIpAddressesSearch.entity().getEndDate(), SearchCriteria.Op.GT);
59+
quarantinedIpAddressesSearch.or("removedIsNotNull", quarantinedIpAddressesSearch.entity().getRemoved(), SearchCriteria.Op.NNULL);
60+
quarantinedIpAddressesSearch.and("removedDateGt", quarantinedIpAddressesSearch.entity().getRemoved(), SearchCriteria.Op.GT);
61+
quarantinedIpAddressesSearch.cp();
62+
5063
ipAddressSearchBuilder.done();
5164
publicIpAddressByIdSearch.done();
65+
quarantinedIpAddressesSearch.done();
5266
}
5367

5468
@Override
@@ -68,4 +82,15 @@ public PublicIpQuarantineVO findByIpAddress(String publicIpAddress) {
6882

6983
return findOneBy(sc, filter);
7084
}
85+
86+
@Override
87+
public List<PublicIpQuarantineVO> listQuarantinedIpAddressesToUser(Long userId, Date date) {
88+
SearchCriteria<PublicIpQuarantineVO> sc = quarantinedIpAddressesSearch.create();
89+
90+
sc.setParameters("previousOwnerId", userId);
91+
sc.setParameters("endDate", date);
92+
sc.setParameters("removedDateGt", date);
93+
94+
return searchIncludingRemoved(sc, null, false, false);
95+
}
7196
}

server/src/main/java/com/cloud/network/IpAddressManagerImpl.java

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,9 @@ public boolean configure(String name, Map<String, Object> params) {
514514
AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL);
515515
AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.IN);
516516
AssignIpAddressSearch.and("forSystemVms", AssignIpAddressSearch.entity().isForSystemVms(), Op.EQ);
517+
AssignIpAddressSearch.and("id", AssignIpAddressSearch.entity().getId(), Op.NIN);
518+
AssignIpAddressSearch.and("requestedAddress", AssignIpAddressSearch.entity().getAddress(), Op.EQ);
519+
AssignIpAddressSearch.and("routerAddress", AssignIpAddressSearch.entity().getAddress(), Op.NEQ);
517520

518521
SearchBuilder<VlanVO> vlanSearch = _vlanDao.createSearchBuilder();
519522
vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ);
@@ -883,22 +886,35 @@ public List<IPAddressVO> listAvailablePublicIps(final long dcId, final Long podI
883886
if (podId != null) {
884887
sc = AssignIpAddressFromPodVlanSearch.create();
885888
sc.setJoinParameters("podVlanMapSB", "podId", podId);
886-
errorMessage.append(" pod id=" + podId);
889+
errorMessage.append(" pod id=").append(podId);
887890
} else {
888891
sc = AssignIpAddressSearch.create();
889-
errorMessage.append(" zone id=" + dcId);
892+
errorMessage.append(" zone id=").append(dcId);
893+
}
894+
895+
if (lockOneRow) {
896+
logger.debug("Listing quarantined public IPs to ignore on search for public IP for system VM. The IPs ignored will be the ones that: were not associated to account [{}]; were not removed yet; and with quarantine end dates after [{}].", owner.getUuid(), new Date());
897+
898+
List<PublicIpQuarantineVO> quarantinedAddresses = publicIpQuarantineDao.listQuarantinedIpAddressesToUser(owner.getId(), new Date());
899+
List<Long> quarantinedAddressesIDs = quarantinedAddresses.stream().map(PublicIpQuarantineVO::getPublicIpAddressId).collect(Collectors.toList());
900+
901+
logger.debug("Found addresses with the following IDs: {}.", quarantinedAddressesIDs);
902+
903+
if (CollectionUtils.isNotEmpty(quarantinedAddressesIDs)) {
904+
sc.setParameters("id", quarantinedAddressesIDs.toArray());
905+
}
890906
}
891907

892908
sc.setParameters("dc", dcId);
893909

894910
// for direct network take ip addresses only from the vlans belonging to the network
895911
if (vlanUse == VlanType.DirectAttached) {
896912
sc.setJoinParameters("vlan", "networkId", guestNetworkId);
897-
errorMessage.append(", network id=" + guestNetworkId);
913+
errorMessage.append(", network id=").append(guestNetworkId);
898914
}
899915
if (requestedGateway != null) {
900916
sc.setJoinParameters("vlan", "vlanGateway", requestedGateway);
901-
errorMessage.append(", requested gateway=" + requestedGateway);
917+
errorMessage.append(", requested gateway=").append(requestedGateway);
902918
}
903919
sc.setJoinParameters("vlan", "type", vlanUse);
904920

@@ -908,38 +924,39 @@ public List<IPAddressVO> listAvailablePublicIps(final long dcId, final Long podI
908924
NetworkDetailVO routerIpDetail = _networkDetailsDao.findDetail(network.getId(), ApiConstants.ROUTER_IP);
909925
routerIpAddress = routerIpDetail != null ? routerIpDetail.getValue() : null;
910926
}
927+
911928
if (requestedIp != null) {
912-
sc.addAnd("address", SearchCriteria.Op.EQ, requestedIp);
913-
errorMessage.append(": requested ip " + requestedIp + " is not available");
929+
sc.setParameters("requestedAddress", requestedIp);
930+
errorMessage.append(": requested ip ").append(requestedIp).append(" is not available");
914931
} else if (routerIpAddress != null) {
915-
sc.addAnd("address", Op.NEQ, routerIpAddress);
932+
sc.setParameters("routerAddress", routerIpAddress);
916933
}
917934

918935
boolean ascOrder = ! forSystemVms;
919-
Filter filter = new Filter(IPAddressVO.class, "forSystemVms", ascOrder, 0l, 1l);
936+
Filter filter = new Filter(IPAddressVO.class, "forSystemVms", ascOrder, 0L, 1L);
920937

921938
filter.addOrderBy(IPAddressVO.class,"vlanId", true);
922939

923-
List<IPAddressVO> addrs = new ArrayList<>();
940+
List<IPAddressVO> addresses = new ArrayList<>();
924941

925942
if (forSystemVms) {
926943
// Get Public IPs for system vms in dedicated ranges
927944
sc.setParameters("forSystemVms", true);
928945
if (lockOneRow) {
929-
addrs = _ipAddressDao.lockRows(sc, filter, true);
946+
addresses = _ipAddressDao.lockRows(sc, filter, true);
930947
} else {
931-
addrs = new ArrayList<>(_ipAddressDao.search(sc, null));
948+
addresses = new ArrayList<>(_ipAddressDao.search(sc, null));
932949
}
933950
}
934-
if ((!lockOneRow || (lockOneRow && CollectionUtils.isEmpty(addrs))) &&
951+
if ((!lockOneRow || (lockOneRow && CollectionUtils.isEmpty(addresses))) &&
935952
!(forSystemVms && SystemVmPublicIpReservationModeStrictness.value())) {
936953
sc.setParameters("forSystemVms", false);
937954
// If owner has dedicated Public IP ranges, fetch IP from the dedicated range
938955
// Otherwise fetch IP from the system pool
939956
// Checking if network is null in the case of system VM's. At the time of allocation of IP address to systemVm, no network is present.
940957
if (network == null || !(network.getGuestType() == GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced)) {
941-
List<AccountVlanMapVO> maps = _accountVlanMapDao.listAccountVlanMapsByAccount(owner.getId());
942-
for (AccountVlanMapVO map : maps) {
958+
List<AccountVlanMapVO> accountVlanMaps = _accountVlanMapDao.listAccountVlanMapsByAccount(owner.getId());
959+
for (AccountVlanMapVO map : accountVlanMaps) {
943960
if (vlanDbIds == null || vlanDbIds.contains(map.getVlanDbId()))
944961
dedicatedVlanDbIds.add(map.getVlanDbId());
945962
}
@@ -958,10 +975,10 @@ public List<IPAddressVO> listAvailablePublicIps(final long dcId, final Long podI
958975
if (!dedicatedVlanDbIds.isEmpty()) {
959976
fetchFromDedicatedRange = true;
960977
sc.setParameters("vlanId", dedicatedVlanDbIds.toArray());
961-
errorMessage.append(", vlanId id=" + Arrays.toString(dedicatedVlanDbIds.toArray()));
978+
errorMessage.append(", vlanId id=").append(Arrays.toString(dedicatedVlanDbIds.toArray()));
962979
} else if (!nonDedicatedVlanDbIds.isEmpty()) {
963980
sc.setParameters("vlanId", nonDedicatedVlanDbIds.toArray());
964-
errorMessage.append(", vlanId id=" + Arrays.toString(nonDedicatedVlanDbIds.toArray()));
981+
errorMessage.append(", vlanId id=").append(Arrays.toString(nonDedicatedVlanDbIds.toArray()));
965982
} else {
966983
if (podId != null) {
967984
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", Pod.class, podId);
@@ -975,29 +992,29 @@ public List<IPAddressVO> listAvailablePublicIps(final long dcId, final Long podI
975992
}
976993
}
977994
if (lockOneRow) {
978-
addrs = _ipAddressDao.lockRows(sc, filter, true);
995+
addresses = _ipAddressDao.lockRows(sc, filter, true);
979996
} else {
980-
addrs = new ArrayList<>(_ipAddressDao.search(sc, null));
997+
addresses = new ArrayList<>(_ipAddressDao.search(sc, null));
981998
}
982999

9831000
// If all the dedicated IPs of the owner are in use fetch an IP from the system pool
984-
if ((!lockOneRow || (lockOneRow && addrs.size() == 0)) && fetchFromDedicatedRange && vlanUse == VlanType.VirtualNetwork) {
1001+
if ((!lockOneRow || (lockOneRow && addresses.isEmpty())) && fetchFromDedicatedRange && vlanUse == VlanType.VirtualNetwork) {
9851002
// Verify if account is allowed to acquire IPs from the system
9861003
boolean useSystemIps = UseSystemPublicIps.valueIn(owner.getId());
9871004
if (useSystemIps && !nonDedicatedVlanDbIds.isEmpty()) {
9881005
fetchFromDedicatedRange = false;
9891006
sc.setParameters("vlanId", nonDedicatedVlanDbIds.toArray());
9901007
errorMessage.append(", vlanId id=" + Arrays.toString(nonDedicatedVlanDbIds.toArray()));
9911008
if (lockOneRow) {
992-
addrs = _ipAddressDao.lockRows(sc, filter, true);
1009+
addresses = _ipAddressDao.lockRows(sc, filter, true);
9931010
} else {
994-
addrs.addAll(_ipAddressDao.search(sc, null));
1011+
addresses.addAll(_ipAddressDao.search(sc, null));
9951012
}
9961013
}
9971014
}
9981015
}
9991016

1000-
if (lockOneRow && addrs.size() == 0) {
1017+
if (lockOneRow && addresses.isEmpty()) {
10011018
if (podId != null) {
10021019
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", Pod.class, podId);
10031020
// for now, we hardcode the table names, but we should ideally do a lookup for the tablename from the VO object.
@@ -1011,13 +1028,12 @@ public List<IPAddressVO> listAvailablePublicIps(final long dcId, final Long podI
10111028
}
10121029

10131030
if (lockOneRow) {
1014-
assert (addrs.size() == 1) : "Return size is incorrect: " + addrs.size();
1015-
IpAddress ipAddress = addrs.get(0);
1016-
boolean ipCanBeAllocated = canPublicIpAddressBeAllocated(ipAddress, owner);
1031+
IPAddressVO allocatableIp = addresses.get(0);
1032+
1033+
boolean isPublicIpAllocatable = canPublicIpAddressBeAllocated(allocatableIp, owner);
10171034

1018-
if (!ipCanBeAllocated) {
1019-
throw new InsufficientAddressCapacityException(String.format("Failed to allocate public IP address [%s] as it is in quarantine.", ipAddress.getAddress()),
1020-
DataCenter.class, dcId);
1035+
if (!isPublicIpAllocatable) {
1036+
throw new InsufficientAddressCapacityException(String.format("Failed to allocate public IP [%s] as it is in quarantine.", allocatableIp.getAddress()), DataCenter.class, dcId);
10211037
}
10221038
}
10231039

@@ -1026,12 +1042,12 @@ public List<IPAddressVO> listAvailablePublicIps(final long dcId, final Long podI
10261042
try {
10271043
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.public_ip);
10281044
} catch (ResourceAllocationException ex) {
1029-
logger.warn("Failed to allocate resource of type " + ex.getResourceType() + " for account " + owner);
1045+
logger.warn("Failed to allocate resource of type {} for account {}", ex.getResourceType(), owner);
10301046
throw new AccountLimitException("Maximum number of public IP addresses for account: " + owner.getAccountName() + " has been exceeded.");
10311047
}
10321048
}
10331049

1034-
return addrs;
1050+
return addresses;
10351051
}
10361052

10371053
@DB
@@ -2458,26 +2474,27 @@ public boolean canPublicIpAddressBeAllocated(IpAddress ip, Account newOwner) {
24582474
PublicIpQuarantineVO publicIpQuarantineVO = publicIpQuarantineDao.findByPublicIpAddressId(ip.getId());
24592475

24602476
if (publicIpQuarantineVO == null) {
2461-
logger.debug(String.format("Public IP address [%s] is not in quarantine; therefore, it is allowed to be allocated.", ip));
2477+
logger.debug("Public IP address [{}] is not in quarantine; therefore, it is allowed to be allocated.", ip);
24622478
return true;
24632479
}
24642480

24652481
if (!isPublicIpAddressStillInQuarantine(publicIpQuarantineVO, new Date())) {
2466-
logger.debug(String.format("Public IP address [%s] is no longer in quarantine; therefore, it is allowed to be allocated.", ip));
2482+
logger.debug("Public IP address [{}] is no longer in quarantine; therefore, it is allowed to be allocated.", ip);
2483+
removePublicIpAddressFromQuarantine(publicIpQuarantineVO.getId(), "IP was removed from quarantine because it was no longer in quarantine.");
24672484
return true;
24682485
}
24692486

24702487
Account previousOwner = _accountMgr.getAccount(publicIpQuarantineVO.getPreviousOwnerId());
24712488

24722489
if (Objects.equals(previousOwner.getUuid(), newOwner.getUuid())) {
2473-
logger.debug(String.format("Public IP address [%s] is in quarantine; however, the Public IP previous owner [%s] is the same as the new owner [%s]; therefore the IP" +
2474-
" can be allocated. The public IP address will be removed from quarantine.", ip, previousOwner, newOwner));
2490+
logger.debug("Public IP address [{}] is in quarantine; however, the Public IP previous owner [{}] is the same as the new owner [{}]; therefore the IP" +
2491+
" can be allocated. The public IP address will be removed from quarantine.", ip, previousOwner, newOwner);
24752492
removePublicIpAddressFromQuarantine(publicIpQuarantineVO.getId(), "IP was removed from quarantine because it has been allocated by the previous owner");
24762493
return true;
24772494
}
24782495

2479-
logger.error(String.format("Public IP address [%s] is in quarantine and the previous owner [%s] is different than the new owner [%s]; therefore, the IP cannot be " +
2480-
"allocated.", ip, previousOwner, newOwner));
2496+
logger.error("Public IP address [{}] is in quarantine and the previous owner [{}] is different than the new owner [{}]; therefore, the IP cannot be " +
2497+
"allocated.", ip, previousOwner, newOwner);
24812498
return false;
24822499
}
24832500

@@ -2528,7 +2545,7 @@ public void removePublicIpAddressFromQuarantine(Long quarantineProcessId, String
25282545
publicIpQuarantineVO.setRemovalReason(removalReason);
25292546
publicIpQuarantineVO.setRemoverAccountId(removerAccountId);
25302547

2531-
logger.debug(String.format("Removing public IP Address [%s] from quarantine by updating the removed date to [%s].", ipAddress, removedDate));
2548+
logger.debug("Removing public IP Address [{}] from quarantine by updating the removed date to [{}].", ipAddress, removedDate);
25322549
publicIpQuarantineDao.persist(publicIpQuarantineVO);
25332550
}
25342551

server/src/test/java/com/cloud/network/IpAddressManagerTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ public void checkIfPublicIpAddressIsNotInQuarantineAndCanBeAllocatedTestIpIsNoLo
356356
Mockito.when(ipAddressMock.getId()).thenReturn(dummyID);
357357
Mockito.when(publicIpQuarantineDaoMock.findByPublicIpAddressId(Mockito.anyLong())).thenReturn(publicIpQuarantineVOMock);
358358
Mockito.doReturn(false).when(ipAddressManager).isPublicIpAddressStillInQuarantine(Mockito.any(PublicIpQuarantineVO.class), Mockito.any(Date.class));
359+
Mockito.doNothing().when(ipAddressManager).removePublicIpAddressFromQuarantine(Mockito.anyLong(), Mockito.anyString());
359360

360361
boolean result = ipAddressManager.canPublicIpAddressBeAllocated(ipAddressMock, newOwnerMock);
361362

0 commit comments

Comments
 (0)