Skip to content

Commit d31b5ff

Browse files
prashanthr2Prashanth Reddyclaude
authored
network: scope routed network/VPC subnets to a zone (#14421)
* network: scope routed network/VPC subnets to a zone The ip4_guest_subnet_network_map table had no zone column, so the subnet bookkeeping for routed networks and VPCs was global. Creating a routed VPC or network with a CIDR already used in another zone failed with "The subnet is in use", even though the two zones are independent. Add a nullable data_center_id column to ip4_guest_subnet_network_map and scope the subnet lookup and the parentless overlap check by zone, so the same CIDR can be used once per zone. Existing rows are backfilled from their parent zone subnet, VPC or network. Fixes #14419 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017rnv8vT6LQSKfwkzp8tvg9 * network: list and report routed subnets by their own zone listIpv4SubnetsForGuestNetwork resolved the zoneid filter through dc_ip4_guest_subnets, so a subnet with no parent never matched, and a zone with no zone-level subnet returned an empty list. The response derived the zone the same way, leaving parentless subnets with no zone at all. Now that the record carries its zone, filter on data_center_id directly and fall back to it when building the response. Addresses review feedback on #14419. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017rnv8vT6LQSKfwkzp8tvg9 --------- Co-authored-by: Prashanth Reddy <prashanth.reddy@shapeblue.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 0d380a0 commit d31b5ff

7 files changed

Lines changed: 121 additions & 32 deletions

File tree

‎api/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMap.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ enum State {
3636

3737
Long getParentId();
3838

39+
Long getDataCenterId();
40+
3941
String getSubnet();
4042

4143
Long getVpcId();

‎engine/schema/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMapVO.java‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public class Ipv4GuestSubnetNetworkMapVO implements Ipv4GuestSubnetNetworkMap {
4646
@Column(name = "parent_id")
4747
private Long parentId;
4848

49+
@Column(name = "data_center_id")
50+
private Long dataCenterId;
51+
4952
@Column(name = "subnet")
5053
private String subnet;
5154

@@ -72,8 +75,9 @@ protected Ipv4GuestSubnetNetworkMapVO() {
7275
uuid = UUID.randomUUID().toString();
7376
}
7477

75-
protected Ipv4GuestSubnetNetworkMapVO(Long parentId, String subnet, Long networkId, Ipv4GuestSubnetNetworkMap.State state) {
78+
protected Ipv4GuestSubnetNetworkMapVO(Long parentId, Long dataCenterId, String subnet, Long networkId, Ipv4GuestSubnetNetworkMap.State state) {
7679
this.parentId = parentId;
80+
this.dataCenterId = dataCenterId;
7781
this.subnet = subnet;
7882
this.networkId = networkId;
7983
this.state = state;
@@ -95,6 +99,15 @@ public Long getParentId() {
9599
return parentId;
96100
}
97101

102+
@Override
103+
public Long getDataCenterId() {
104+
return dataCenterId;
105+
}
106+
107+
public void setDataCenterId(Long dataCenterId) {
108+
this.dataCenterId = dataCenterId;
109+
}
110+
98111
@Override
99112
public String getSubnet() {
100113
return subnet;

‎engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDao.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public interface Ipv4GuestSubnetNetworkMapDao extends GenericDao<Ipv4GuestSubnet
3131
Ipv4GuestSubnetNetworkMapVO findFirstAvailable(long parentId, long cidrSize);
3232
Ipv4GuestSubnetNetworkMapVO findByNetworkId(long networkId);
3333
Ipv4GuestSubnetNetworkMapVO findByVpcId(long vpcId);
34-
Ipv4GuestSubnetNetworkMapVO findBySubnet(String subnet);
34+
Ipv4GuestSubnetNetworkMapVO findBySubnetAndDataCenterId(String subnet, Long dataCenterId);
3535
List<Ipv4GuestSubnetNetworkMapVO> findSubnetsInStates(Ipv4GuestSubnetNetworkMap.State... states);
3636
void deleteByParentId(long parentId);
37-
List<Ipv4GuestSubnetNetworkMapVO> listAllNoParent();
37+
List<Ipv4GuestSubnetNetworkMapVO> listAllNoParentByDataCenterId(Long dataCenterId);
3838
}

‎engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDaoImpl.java‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,15 @@ public void init() {
6363
ParentIdSearch.done();
6464
NoParentSearch = createSearchBuilder();
6565
NoParentSearch.and("parentId", NoParentSearch.entity().getParentId(), SearchCriteria.Op.NULL);
66+
NoParentSearch.and("dataCenterId", NoParentSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
6667
NoParentSearch.done();
6768
NetworkIdSearch = createSearchBuilder();
6869
NetworkIdSearch.and("networkId", NetworkIdSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
6970
NetworkIdSearch.and("vpcId", NetworkIdSearch.entity().getVpcId(), SearchCriteria.Op.EQ);
7071
NetworkIdSearch.done();
7172
SubnetSearch = createSearchBuilder();
7273
SubnetSearch.and("subnet", SubnetSearch.entity().getSubnet(), SearchCriteria.Op.EQ);
74+
SubnetSearch.and("dataCenterId", SubnetSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
7375
SubnetSearch.done();
7476
StatesSearch = createSearchBuilder();
7577
StatesSearch.and("state", StatesSearch.entity().getState(), SearchCriteria.Op.IN);
@@ -142,9 +144,10 @@ public Ipv4GuestSubnetNetworkMapVO findByVpcId(long vpcId) {
142144
}
143145

144146
@Override
145-
public Ipv4GuestSubnetNetworkMapVO findBySubnet(String subnet) {
147+
public Ipv4GuestSubnetNetworkMapVO findBySubnetAndDataCenterId(String subnet, Long dataCenterId) {
146148
SearchCriteria<Ipv4GuestSubnetNetworkMapVO> sc = SubnetSearch.create();
147149
sc.setParameters("subnet", subnet);
150+
sc.setParameters("dataCenterId", dataCenterId);
148151
return findOneBy(sc);
149152
}
150153

@@ -163,8 +166,9 @@ public void deleteByParentId(long parentId) {
163166
}
164167

165168
@Override
166-
public List<Ipv4GuestSubnetNetworkMapVO> listAllNoParent() {
169+
public List<Ipv4GuestSubnetNetworkMapVO> listAllNoParentByDataCenterId(Long dataCenterId) {
167170
SearchCriteria<Ipv4GuestSubnetNetworkMapVO> sc = NoParentSearch.create();
171+
sc.setParameters("dataCenterId", dataCenterId);
168172
return listBy(sc, null);
169173
}
170174
}

‎engine/schema/src/main/resources/META-INF/db/schema-42210to42220.sql‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,23 @@ UPDATE op_dc_vnet_alloc a
4343
a.reservation_id = NULL,
4444
a.taken = NULL
4545
WHERE n.removed IS NOT NULL;
46+
47+
-- Scope routed network/VPC subnets to a zone, so that the same CIDR can be used in more than one zone. See #14419.
48+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.ip4_guest_subnet_network_map', 'data_center_id', 'bigint unsigned DEFAULT NULL COMMENT "zone it belongs to"');
49+
50+
UPDATE `cloud`.`ip4_guest_subnet_network_map` m
51+
JOIN `cloud`.`dc_ip4_guest_subnets` p ON p.id = m.parent_id
52+
SET m.data_center_id = p.data_center_id
53+
WHERE m.data_center_id IS NULL;
54+
55+
UPDATE `cloud`.`ip4_guest_subnet_network_map` m
56+
JOIN `cloud`.`vpc` v ON v.id = m.vpc_id
57+
SET m.data_center_id = v.zone_id
58+
WHERE m.data_center_id IS NULL;
59+
60+
UPDATE `cloud`.`ip4_guest_subnet_network_map` m
61+
JOIN `cloud`.`networks` n ON n.id = m.network_id
62+
SET m.data_center_id = n.data_center_id
63+
WHERE m.data_center_id IS NULL;
64+
65+
CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.ip4_guest_subnet_network_map', 'fk_ip4_guest_subnet_network_map__data_center_id', '(`data_center_id`)', '`data_center`(`id`)');

‎server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java‎

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@
109109
import java.util.HashMap;
110110
import java.util.List;
111111
import java.util.Map;
112-
import java.util.stream.Collectors;
113112

114113

115114
public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements RoutedIpv4Manager {
@@ -519,12 +518,7 @@ public List<Ipv4GuestSubnetNetworkMap> listIpv4GuestSubnetsForGuestNetwork(ListI
519518
sc.addAnd("id", SearchCriteria.Op.EQ, id);
520519
}
521520
if (zoneId != null) {
522-
List<DataCenterIpv4GuestSubnetVO> subnets = dataCenterIpv4GuestSubnetDao.listByDataCenterId(zoneId);
523-
if (CollectionUtils.isEmpty(subnets)) {
524-
return new ArrayList<>();
525-
}
526-
List<Long> parentIds = subnets.stream().map(DataCenterIpv4GuestSubnetVO::getId).collect(Collectors.toList());
527-
sc.addAnd("parentId", SearchCriteria.Op.IN, parentIds.toArray());
521+
sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId);
528522
}
529523
if (parentId != null) {
530524
sc.addAnd("parentId", SearchCriteria.Op.EQ, parentId);
@@ -554,7 +548,7 @@ public Ipv4SubnetForGuestNetworkResponse createIpv4SubnetForGuestNetworkResponse
554548
response.setState(subnet.getState().name());
555549
response.setId(subnet.getUuid());
556550
response.setAllocatedTime(subnet.getAllocated());
557-
Long zoneId = null;
551+
Long zoneId = subnet.getDataCenterId();
558552
if (subnet.getNetworkId() != null) {
559553
Network network = ApiDBUtils.findNetworkById(subnet.getNetworkId());
560554
response.setNetworkId(network.getUuid());
@@ -650,7 +644,7 @@ protected Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInt
650644
}
651645

652646
protected void getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(String networkCidr, Long ownerDomainId, Long ownerAccountId, Long zoneId) {
653-
Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(networkCidr);
647+
Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnetAndDataCenterId(networkCidr, zoneId);
654648
if (subnetMap != null) {
655649
// check if the subnet is in use
656650
if (subnetMap.getNetworkId() != null || subnetMap.getVpcId() != null) {
@@ -672,7 +666,7 @@ protected void getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(String networkC
672666

673667
// Create new record without networkId
674668
final Long parentId = parent != null ? parent.getId() : null;
675-
subnetMap = new Ipv4GuestSubnetNetworkMapVO(parentId, NetUtils.transformCidr(networkCidr), null, State.Free);
669+
subnetMap = new Ipv4GuestSubnetNetworkMapVO(parentId, zoneId, NetUtils.transformCidr(networkCidr), null, State.Free);
676670
ipv4GuestSubnetNetworkMapDao.persist(subnetMap);
677671
}
678672

@@ -698,7 +692,7 @@ protected DataCenterIpv4GuestSubnet getParentOfNetworkCidr(Long zoneId, String n
698692
}
699693
}
700694
// check conflicts
701-
List<Ipv4GuestSubnetNetworkMapVO> subnetsForNetworkNoParents = ipv4GuestSubnetNetworkMapDao.listAllNoParent();
695+
List<Ipv4GuestSubnetNetworkMapVO> subnetsForNetworkNoParents = ipv4GuestSubnetNetworkMapDao.listAllNoParentByDataCenterId(zoneId);
702696
checkConflicts(subnetsForNetworkNoParents, networkCidr);
703697
return null;
704698
}
@@ -730,7 +724,7 @@ private List<DataCenterIpv4GuestSubnetVO> getZoneSubnetsForAccount(long domainId
730724
protected Ipv4GuestSubnetNetworkMap createIpv4SubnetFromParentSubnet(DataCenterIpv4GuestSubnet parent, Integer networkCidrSize) {
731725
String networkCidr = createIpv4SubnetStringFromParentSubnet(parent, networkCidrSize);
732726
// create DB record
733-
Ipv4GuestSubnetNetworkMapVO subnetMap = new Ipv4GuestSubnetNetworkMapVO(parent.getId(), NetUtils.transformCidr(networkCidr), null, State.Free);
727+
Ipv4GuestSubnetNetworkMapVO subnetMap = new Ipv4GuestSubnetNetworkMapVO(parent.getId(), parent.getDataCenterId(), NetUtils.transformCidr(networkCidr), null, State.Free);
734728
return ipv4GuestSubnetNetworkMapDao.persist(subnetMap);
735729
}
736730

@@ -767,19 +761,19 @@ protected String createIpv4SubnetStringFromParentSubnet(DataCenterIpv4GuestSubne
767761
}
768762

769763
// Allocate a subnet automatically
770-
String networkCidr = getFreeNetworkCidr(subnetsInFreeIpRanges, networkCidrSize);
764+
String networkCidr = getFreeNetworkCidr(subnetsInFreeIpRanges, networkCidrSize, parent.getDataCenterId());
771765
if (networkCidr == null) {
772766
throw new InvalidParameterValueException("Failed to automatically allocate a subnet with specified cidrsize");
773767
}
774768
return networkCidr;
775769
}
776770

777-
private String getFreeNetworkCidr(List<Pair<Long, Integer>> subnetsInFreeIpRanges, int networkCidrSize) {
771+
private String getFreeNetworkCidr(List<Pair<Long, Integer>> subnetsInFreeIpRanges, int networkCidrSize, Long dataCenterId) {
778772
for (int cidrSize = networkCidrSize; cidrSize >= 1; cidrSize--) {
779773
for (Pair<Long, Integer> freeSubnet : subnetsInFreeIpRanges) {
780774
if (freeSubnet.second().equals(cidrSize)) {
781775
String networkCidr = String.format("%s/%s", NetUtils.long2Ip(freeSubnet.first()), networkCidrSize);
782-
if (ipv4GuestSubnetNetworkMapDao.findBySubnet(networkCidr) == null) {
776+
if (ipv4GuestSubnetNetworkMapDao.findBySubnetAndDataCenterId(networkCidr, dataCenterId) == null) {
783777
return networkCidr;
784778
}
785779
}
@@ -798,7 +792,7 @@ protected Ipv4GuestSubnetNetworkMap createIpv4SubnetFromParentSubnet(DataCenterI
798792
checkConflicts(existingSubnets, networkCidr);
799793

800794
// create DB record
801-
Ipv4GuestSubnetNetworkMapVO subnetMap = new Ipv4GuestSubnetNetworkMapVO(parent.getId(), NetUtils.transformCidr(networkCidr), null, State.Free);
795+
Ipv4GuestSubnetNetworkMapVO subnetMap = new Ipv4GuestSubnetNetworkMapVO(parent.getId(), parent.getDataCenterId(), NetUtils.transformCidr(networkCidr), null, State.Free);
802796
return ipv4GuestSubnetNetworkMapDao.persist(subnetMap);
803797
}
804798

@@ -807,7 +801,7 @@ public void assignIpv4SubnetToNetwork(Network network) {
807801
if (network == null || network.getCidr() == null) {
808802
return;
809803
}
810-
Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(network.getCidr());
804+
Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnetAndDataCenterId(network.getCidr(), network.getDataCenterId());
811805
if (subnetMap != null) {
812806
if (network.getId() > 0L) {
813807
subnetMap.setNetworkId(network.getId());
@@ -823,7 +817,7 @@ public void assignIpv4SubnetToVpc(Vpc vpc) {
823817
if (vpc == null || vpc.getCidr() == null) {
824818
return;
825819
}
826-
Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(vpc.getCidr());
820+
Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnetAndDataCenterId(vpc.getCidr(), vpc.getZoneId());
827821
if (subnetMap != null) {
828822
if (vpc != null && vpc.getId() > 0L) {
829823
subnetMap.setVpcId(vpc.getId());

0 commit comments

Comments
 (0)