Skip to content

Commit 57609c7

Browse files
authored
Add support to add IPv6 Public IP range as IPAM Allocation / Subnet on Netris (#36)
* Add support to add IPv6 Public IP range as IPAM Allocation / Subnet on Netris * Add ipam alloc and subnet for the ipv6 subnet associated to the vpc tier network * remove commented code
1 parent f54cb50 commit 57609c7

File tree

8 files changed

+121
-50
lines changed

8 files changed

+121
-50
lines changed

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.cloud.dc.VlanDetailsVO;
4444
import com.cloud.dc.dao.ASNumberDao;
4545
import com.cloud.dc.dao.VlanDetailsDao;
46+
import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao;
4647
import com.cloud.network.dao.NetrisProviderDao;
4748
import com.cloud.network.dao.NsxProviderDao;
4849
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
@@ -361,6 +362,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
361362
private ASNumberDao asNumberDao;
362363
@Inject
363364
private BGPService bgpService;
365+
@Inject
366+
private Ipv6GuestPrefixSubnetNetworkMapDao ipv6GuestPrefixSubnetNetworkMapDao;
364367

365368
@Override
366369
public List<NetworkGuru> getNetworkGurus() {
@@ -824,6 +827,11 @@ public void doInTransactionWithoutResult(final TransactionStatus status) {
824827
if (domainId != null && aclType == ACLType.Domain) {
825828
_networksDao.addDomainToNetwork(id, domainId, subdomainAccess == null || subdomainAccess);
826829
}
830+
String ipv6Cidr = network.getIp6Cidr();
831+
String ipv6Gateway = network.getIp6Gateway();
832+
if (StringUtils.isNoneBlank(ipv6Cidr, ipv6Gateway)) {
833+
ipv6Service.assignIpv6SubnetToNetwork(ipv6Cidr, networkPersisted.getId());
834+
}
827835
}
828836
});
829837
guru.setup(network, relatedFile);

engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,6 @@ public interface VlanDao extends GenericDao<VlanVO, Long> {
6464
List<VlanVO> listIpv6RangeByZoneIdAndVlanId(long zoneId, String vlanId);
6565

6666
List<VlanVO> listIpv6SupportingVlansByZone(long zoneId);
67+
68+
List<VlanVO> listVlansForExternalNetworkProvider(long zoneId, String detailKey);
6769
}

engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import javax.inject.Inject;
2727
import javax.naming.ConfigurationException;
2828

29+
import com.cloud.dc.VlanDetailsVO;
2930
import org.apache.commons.lang3.ArrayUtils;
3031
import org.apache.commons.lang3.StringUtils;
3132
import org.springframework.stereotype.Component;
@@ -67,6 +68,8 @@ public class VlanDaoImpl extends GenericDaoBase<VlanVO, Long> implements VlanDao
6768
protected SearchBuilder<VlanVO> ZoneVlanIp6Search;
6869
protected SearchBuilder<VlanVO> ZoneIp6Search;
6970
protected SearchBuilder<VlanVO> ZoneVlansSearch;
71+
protected SearchBuilder<VlanVO> ProviderVlanSearch;
72+
protected SearchBuilder<VlanDetailsVO> VlanDetailsProviderSearch;
7073

7174
protected SearchBuilder<AccountVlanMapVO> AccountVlanMapSearch;
7275
protected SearchBuilder<DomainVlanMapVO> DomainVlanMapSearch;
@@ -79,6 +82,8 @@ public class VlanDaoImpl extends GenericDaoBase<VlanVO, Long> implements VlanDao
7982
protected DomainVlanMapDao _domainVlanMapDao;
8083
@Inject
8184
protected IPAddressDao _ipAddressDao;
85+
@Inject
86+
protected VlanDetailsDao vlanDetailsDao;
8287

8388
@Override
8489
public VlanVO findByZoneAndVlanId(long zoneId, String vlanId) {
@@ -277,6 +282,19 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
277282
ZoneVlansSearch.and("zoneId", ZoneVlansSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
278283
ZoneVlansSearch.and("vlan", ZoneVlansSearch.entity().getVlanTag(), SearchCriteria.Op.IN);
279284
ZoneVlansSearch.done();
285+
286+
ProviderVlanSearch = createSearchBuilder();
287+
ProviderVlanSearch.and("removed", ProviderVlanSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
288+
ProviderVlanSearch.and("dataCenterId", ProviderVlanSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
289+
VlanDetailsProviderSearch = vlanDetailsDao.createSearchBuilder();
290+
VlanDetailsProviderSearch.and("name", VlanDetailsProviderSearch.entity().getName(), SearchCriteria.Op.EQ);
291+
VlanDetailsProviderSearch.and("value", VlanDetailsProviderSearch.entity().getValue(), SearchCriteria.Op.EQ);
292+
ProviderVlanSearch.join("VlanDetailsProviderSearch", VlanDetailsProviderSearch, ProviderVlanSearch.entity().getId(),
293+
VlanDetailsProviderSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
294+
295+
VlanDetailsProviderSearch.done();
296+
ProviderVlanSearch.done();
297+
280298
return result;
281299
}
282300

@@ -434,4 +452,13 @@ public List<VlanVO> listIpv6SupportingVlansByZone(long zoneId) {
434452
return listBy(sc);
435453
}
436454

455+
@Override
456+
public List<VlanVO> listVlansForExternalNetworkProvider(long zoneId, String detailKey) {
457+
SearchCriteria<VlanVO> sc = ProviderVlanSearch.create();
458+
sc.setParameters("dataCenterId", zoneId);
459+
sc.setJoinParameters("VlanDetailsProviderSearch", "name", detailKey);
460+
sc.setJoinParameters("VlanDetailsProviderSearch", "value", "true");
461+
return search(sc, null);
462+
}
463+
437464
}

plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
2323
private Integer vxlanId;
2424
private String gateway;
2525
private String netrisTag;
26+
private String ipv6Cidr;
2627

2728
public CreateNetrisVnetCommand(Long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, String cidr, String gateway, boolean isVpc) {
2829
super(zoneId, accountId, domainId, vNetName, networkId, isVpc);
@@ -64,4 +65,12 @@ public String getNetrisTag() {
6465
public void setNetrisTag(String netrisTag) {
6566
this.netrisTag = netrisTag;
6667
}
68+
69+
public String getIpv6Cidr() {
70+
return ipv6Cidr;
71+
}
72+
73+
public void setIpv6Cidr(String ipv6Cidr) {
74+
this.ipv6Cidr = ipv6Cidr;
75+
}
6776
}

plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ public boolean createVnet(CreateNetrisVnetCommand cmd) {
594594
String netrisTag = cmd.getNetrisTag();
595595
String netmask = vnetCidr.split("/")[1];
596596
String netrisGateway = cmd.getGateway() + "/" + netmask;
597+
String netrisV6Cidr = cmd.getIpv6Cidr();
597598
boolean isVpc = cmd.isVpc();
598599

599600
String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
@@ -614,9 +615,18 @@ public boolean createVnet(CreateNetrisVnetCommand cmd) {
614615
String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
615616

616617
createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
618+
if (Objects.nonNull(netrisV6Cidr)) {
619+
String netrisV6IpamAllocationName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, netrisV6Cidr);
620+
String netrisV6SubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, netrisV6Cidr) ;
621+
InlineResponse2004Data createdIpamAllocation = createIpamAllocationInternal(netrisV6IpamAllocationName, netrisV6Cidr, associatedVpc);
622+
if (Objects.isNull(createdIpamAllocation)) {
623+
throw new CloudRuntimeException(String.format("Failed to create Netris IPAM Allocation %s for VPC %s", netrisV6IpamAllocationName, netrisVpcName));
624+
}
625+
createIpamSubnetInternal(netrisV6SubnetName, netrisV6Cidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
626+
}
617627
logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
618628

619-
VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, netrisGateway, vxlanId, netrisTag);
629+
VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, netrisGateway, netrisV6Cidr, vxlanId, netrisTag);
620630
if (vnetResponse == null || !vnetResponse.isIsSuccess()) {
621631
String reason = vnetResponse == null ? "Empty response" : "Operation failed on Netris";
622632
logger.debug("The Netris vNet creation {} failed: {}", vNetName, reason);
@@ -1107,25 +1117,33 @@ private InlineResponse2004Data createIpamSubnetInternal(String subnetName, Strin
11071117
}
11081118
}
11091119

1110-
VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String netrisGateway, Integer vxlanId, String netrisTag) {
1120+
VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String netrisGateway, String netrisV6Gateway, Integer vxlanId, String netrisTag) {
11111121
logger.debug("Creating Netris VPC vNet {} for CIDR {}", netrisVnetName, netrisGateway);
11121122
try {
11131123
VnetAddBody vnetBody = new VnetAddBody();
11141124

11151125
vnetBody.setCustomAnycastMac("");
11161126

1117-
VnetAddBodyGateways gateways = new VnetAddBodyGateways();
1118-
gateways.prefix(netrisGateway);
1119-
gateways.setDhcpEnabled(false);
1127+
VnetAddBodyGateways gatewayV4 = new VnetAddBodyGateways();
1128+
gatewayV4.prefix(netrisGateway);
1129+
gatewayV4.setDhcpEnabled(false);
11201130
VnetAddBodyDhcp dhcp = new VnetAddBodyDhcp();
11211131
dhcp.setEnd("");
11221132
dhcp.setStart("");
11231133
dhcp.setOptionSet(new VnetAddBodyDhcpOptionSet());
1124-
gateways.setDhcp(dhcp);
1134+
gatewayV4.setDhcp(dhcp);
11251135
List<VnetAddBodyGateways> gatewaysList = new ArrayList<>();
1126-
gatewaysList.add(gateways);
1127-
vnetBody.setGateways(gatewaysList);
1136+
gatewaysList.add(gatewayV4);
1137+
1138+
if (Objects.nonNull(netrisV6Gateway)) {
1139+
VnetAddBodyGateways gatewayV6 = new VnetAddBodyGateways();
1140+
gatewayV6.prefix(netrisV6Gateway);
1141+
gatewayV6.setDhcpEnabled(false);
1142+
gatewayV6.setDhcp(dhcp);
1143+
gatewaysList.add(gatewayV6);
1144+
}
11281145

1146+
vnetBody.setGateways(gatewaysList);
11291147
vnetBody.setGuestTenants(new ArrayList<>());
11301148
vnetBody.setL3vpn(false);
11311149
vnetBody.setName(netrisVnetName);

plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,15 @@
1818

1919
import com.cloud.agent.AgentManager;
2020
import com.cloud.agent.api.Answer;
21-
import com.cloud.dc.VlanDetailsVO;
2221
import com.cloud.dc.VlanVO;
2322
import com.cloud.dc.dao.VlanDao;
24-
import com.cloud.dc.dao.VlanDetailsDao;
2523
import com.cloud.exception.InvalidParameterValueException;
2624
import com.cloud.network.IpAddress;
25+
import com.cloud.network.Ipv6GuestPrefixSubnetNetworkMapVO;
2726
import com.cloud.network.Network;
2827
import com.cloud.network.Networks;
2928
import com.cloud.network.SDNProviderNetworkRule;
30-
import com.cloud.network.dao.IPAddressDao;
31-
import com.cloud.network.dao.IPAddressVO;
29+
import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao;
3230
import com.cloud.network.dao.NetrisProviderDao;
3331
import com.cloud.network.dao.NetworkDao;
3432
import com.cloud.network.dao.NetworkVO;
@@ -37,6 +35,7 @@
3735
import com.cloud.network.element.NetrisProviderVO;
3836
import com.cloud.network.netris.NetrisService;
3937
import com.cloud.network.vpc.Vpc;
38+
import com.cloud.utils.Pair;
4039
import com.cloud.utils.exception.CloudRuntimeException;
4140
import com.cloud.utils.net.NetUtils;
4241
import inet.ipaddr.IPAddress;
@@ -82,11 +81,9 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
8281
@Inject
8382
private PhysicalNetworkDao physicalNetworkDao;
8483
@Inject
85-
private IPAddressDao ipAddressDao;
86-
@Inject
8784
private VlanDao vlanDao;
8885
@Inject
89-
private VlanDetailsDao vlanDetailsDao;
86+
private Ipv6GuestPrefixSubnetNetworkMapDao ipv6PrefixNetworkMapDao;
9087

9188
@Override
9289
public String getConfigComponentName() {
@@ -138,10 +135,10 @@ protected String calculateSubnetCidrFromIpRange(String ipRange) {
138135
/**
139136
* Prepare the Netris Public Range to be used by CloudStack after the zone is created and the Netris provider is added
140137
*/
141-
public SetupNetrisPublicRangeCommand createSetupPublicRangeCommand(long zoneId, String gateway, String netmask, String ipRange) {
138+
private Pair<String, String> getAllocationAndSubnet(String gateway, String netmask, String ipRange) {
142139
String superCidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, netmask);
143140
String subnetNatCidr = calculateSubnetCidrFromIpRange(ipRange);
144-
return new SetupNetrisPublicRangeCommand(zoneId, superCidr, subnetNatCidr);
141+
return new Pair<>(superCidr, subnetNatCidr);
145142
}
146143

147144
@Override
@@ -151,38 +148,38 @@ public boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId) {
151148
if (CollectionUtils.isEmpty(physicalNetworks)) {
152149
return false;
153150
}
154-
for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
155-
List<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
156-
List<Long> vlanDbIds = publicIps.stream()
157-
.filter(x -> !x.isForSystemVms())
158-
.map(IPAddressVO::getVlanId)
159-
.collect(Collectors.toList());
160-
if (CollectionUtils.isEmpty(vlanDbIds)) {
161-
String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
162-
logger.error(msg);
163-
throw new CloudRuntimeException(msg);
151+
152+
List<VlanVO> providerVlanIds = vlanDao.listVlansForExternalNetworkProvider(zoneId, ApiConstants.NETRIS_DETAIL_KEY);
153+
List<Long> vlanDbIds = providerVlanIds.stream().map(VlanVO::getId).collect(Collectors.toList());
154+
if (CollectionUtils.isEmpty(vlanDbIds)) {
155+
String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
156+
logger.error(msg);
157+
throw new CloudRuntimeException(msg);
158+
}
159+
for (Long vlanDbId : vlanDbIds) {
160+
VlanVO vlanRecord = vlanDao.findById(vlanDbId);
161+
162+
String gateway = Objects.nonNull(vlanRecord.getVlanGateway()) ? vlanRecord.getVlanGateway() : vlanRecord.getIp6Gateway();
163+
String netmask = vlanRecord.getVlanNetmask();
164+
String ipRange = vlanRecord.getIpRange();
165+
String ip6Cidr = vlanRecord.getIp6Cidr();
166+
SetupNetrisPublicRangeCommand cmd = null;
167+
if (NetUtils.isValidIp4(gateway)) {
168+
Pair<String, String> allocationAndSubnet = getAllocationAndSubnet(gateway, netmask, ipRange);
169+
cmd = new SetupNetrisPublicRangeCommand(zoneId, allocationAndSubnet.first(), allocationAndSubnet.second());
170+
} else if (NetUtils.isValidIp6(gateway)) {
171+
cmd = new SetupNetrisPublicRangeCommand(zoneId, ip6Cidr, ip6Cidr);
164172
}
165-
for (Long vlanDbId : vlanDbIds) {
166-
VlanVO vlanRecord = vlanDao.findById(vlanDbId);
167-
if (vlanRecord == null) {
168-
logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
169-
return false;
170-
}
171-
VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
172-
if (vlanDetail == null) {
173-
logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
174-
continue;
175-
}
176-
String gateway = vlanRecord.getVlanGateway();
177-
String netmask = vlanRecord.getVlanNetmask();
178-
String ipRange = vlanRecord.getIpRange();
179-
SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
180-
NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
181-
if (!answer.getResult()) {
182-
throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
183-
}
173+
174+
if (cmd == null) {
175+
throw new CloudRuntimeException("Incorrect gateway and netmask details provided for the Netris Public IP range setup");
176+
}
177+
NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
178+
if (!answer.getResult()) {
179+
throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
184180
}
185181
}
182+
186183
return true;
187184
}
188185

@@ -208,6 +205,10 @@ public boolean createVnetResource(Long zoneId, long accountId, long domainId, St
208205
cmd.setVxlanId(Integer.parseInt(vxlanId));
209206
NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId);
210207
cmd.setNetrisTag(netrisProvider.getNetrisTag());
208+
if (Objects.nonNull(networkId)) {
209+
Ipv6GuestPrefixSubnetNetworkMapVO ipv6PrefixNetworkMapVO = ipv6PrefixNetworkMapDao.findByNetworkId(networkId);
210+
cmd.setIpv6Cidr(ipv6PrefixNetworkMapVO.getSubnet());
211+
}
211212
NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
212213
return answer.getResult();
213214
}

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,10 +1800,6 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac
18001800
_networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.hideIpAddressUsage, String.valueOf(hideIpAddressUsage), false));
18011801
}
18021802

1803-
if (ip6GatewayCidr != null) {
1804-
ipv6Service.assignIpv6SubnetToNetwork(ip6Cidr, network.getId());
1805-
}
1806-
18071803
// assign to network
18081804
if (NetworkOffering.NetworkMode.ROUTED.equals(ntwkOff.getNetworkMode())) {
18091805
routedIpv4Manager.assignIpv4SubnetToNetwork(network);

ui/src/views/infra/network/IpRangesTabPublic.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,16 @@
230230
<a-form-item name="ip6cidr" ref="ip6cidr" :label="$t('label.cidr')" class="form__item">
231231
<a-input v-model:value="form.ip6cidr" />
232232
</a-form-item>
233+
<a-form-item name="provider" ref="provider">
234+
<template #label>
235+
<tooltip-label :title="$t('label.provider')"/>
236+
</template>
237+
<a-select v-model:value="form.provider">
238+
<a-select-option value=""></a-select-option>
239+
<a-select-option value="NSX">{{ $t('label.nsx') }}</a-select-option>
240+
<a-select-option value="Netris">{{ $t('label.netris') }}</a-select-option>
241+
</a-select>
242+
</a-form-item>
233243
</div>
234244
<div v-else>
235245
<a-form-item name="gateway" ref="gateway" :label="$t('label.gateway')" class="form__item">

0 commit comments

Comments
 (0)