Skip to content

Commit 4bf4daf

Browse files
Pearl1594nvazquez
andauthored
Support to pass provider when creating public ip range and create IPAM on Netris (#28)
* UI: support to pass provider when creating public ip range * prevent adding public ip range for a provider that isnt supported in zone * Create public range on Netris when created on CloudStack --------- Co-authored-by: nvazquez <[email protected]>
1 parent aef6197 commit 4bf4daf

File tree

6 files changed

+130
-90
lines changed

6 files changed

+130
-90
lines changed

api/src/main/java/com/cloud/network/netris/NetrisService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.cloud.network.vpc.Vpc;
2222

2323
public interface NetrisService {
24+
boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId);
2425
boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
2526
boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
2627
boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);

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

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,27 @@
1616
// under the License.
1717
package org.apache.cloudstack.service;
1818

19-
import com.cloud.agent.api.Answer;
2019
import com.cloud.dc.DataCenterVO;
21-
import com.cloud.dc.VlanDetailsVO;
22-
import com.cloud.dc.VlanVO;
2320
import com.cloud.dc.dao.DataCenterDao;
24-
import com.cloud.dc.dao.VlanDao;
25-
import com.cloud.dc.dao.VlanDetailsDao;
2621
import com.cloud.exception.InvalidParameterValueException;
2722
import com.cloud.host.DetailVO;
2823
import com.cloud.host.Host;
2924
import com.cloud.host.dao.HostDetailsDao;
3025
import com.cloud.network.Network;
3126
import com.cloud.network.Networks;
32-
import com.cloud.network.dao.IPAddressDao;
33-
import com.cloud.network.dao.IPAddressVO;
3427
import com.cloud.network.dao.NetrisProviderDao;
3528
import com.cloud.network.dao.NetworkDao;
3629
import com.cloud.network.dao.NetworkVO;
3730
import com.cloud.network.dao.PhysicalNetworkDao;
3831
import com.cloud.network.dao.PhysicalNetworkVO;
3932
import com.cloud.network.element.NetrisProviderVO;
4033
import com.cloud.network.netris.NetrisProvider;
34+
import com.cloud.network.netris.NetrisService;
4135
import com.cloud.resource.ResourceManager;
4236
import com.cloud.utils.db.Transaction;
4337
import com.cloud.utils.db.TransactionCallback;
4438
import com.cloud.utils.exception.CloudRuntimeException;
45-
import com.cloud.utils.net.NetUtils;
4639
import com.google.common.annotations.VisibleForTesting;
47-
import inet.ipaddr.IPAddress;
48-
import inet.ipaddr.IPAddressString;
49-
import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
50-
import org.apache.cloudstack.api.ApiConstants;
5140
import org.apache.cloudstack.api.BaseResponse;
5241
import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
5342
import org.apache.cloudstack.api.command.DeleteNetrisProviderCmd;
@@ -56,7 +45,6 @@
5645
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
5746
import org.apache.cloudstack.resource.NetrisResource;
5847
import org.apache.commons.collections.CollectionUtils;
59-
import org.apache.commons.lang3.StringUtils;
6048
import org.apache.logging.log4j.LogManager;
6149
import org.apache.logging.log4j.Logger;
6250

@@ -68,7 +56,6 @@
6856
import java.util.Map;
6957
import java.util.Objects;
7058
import java.util.UUID;
71-
import java.util.stream.Collectors;
7259

7360
public class NetrisProviderServiceImpl implements NetrisProviderService {
7461

@@ -87,11 +74,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
8774
@Inject
8875
NetworkDao networkDao;
8976
@Inject
90-
private IPAddressDao ipAddressDao;
91-
@Inject
92-
private VlanDao vlanDao;
93-
@Inject
94-
private VlanDetailsDao vlanDetailsDao;
77+
private NetrisService netrisService;
9578

9679
@Override
9780
public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
@@ -147,81 +130,13 @@ public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
147130
} else {
148131
throw new CloudRuntimeException("Failed to add Netris controller due to internal error.");
149132
}
150-
createNetrisPublicIpRangesOnNetrisProvider(zoneId, netrisResource);
133+
netrisService.createIPAMAllocationsForZoneLevelPublicRanges(zoneId);
151134
} catch (ConfigurationException e) {
152135
throw new CloudRuntimeException(e.getMessage());
153136
}
154137
return netrisProvider;
155138
}
156139

157-
/**
158-
* Calculate the minimum CIDR subnet containing the IP range (using the library: <a href="https://github.com/seancfoley/IPAddress">IPAddress</a>)
159-
* From: <a href="https://github.com/seancfoley/IPAddress/wiki/Code-Examples-3:-Subnetting-and-Other-Subnet-Operations#from-start-and-end-address-get-single-cidr-block-covering-both">Example</a>
160-
* @param ipRange format: startIP-endIP
161-
* @return the minimum CIDR containing the IP range
162-
*/
163-
protected String calculateSubnetCidrFromIpRange(String ipRange) {
164-
if (StringUtils.isBlank(ipRange) || !ipRange.contains("-")) {
165-
return null;
166-
}
167-
String[] rangeArray = ipRange.split("-");
168-
String startIp = rangeArray[0];
169-
String endIp = rangeArray[1];
170-
IPAddress startIpAddress = new IPAddressString(startIp).getAddress();
171-
IPAddress endIpAddress = new IPAddressString(endIp).getAddress();
172-
return startIpAddress.coverWithPrefixBlock(endIpAddress).toPrefixLengthString();
173-
}
174-
175-
/**
176-
* Prepare the Netris Public Range to be used by CloudStack after the zone is created and the Netris provider is added
177-
*/
178-
public SetupNetrisPublicRangeCommand createSetupPublicRangeCommand(long zoneId, String gateway, String netmask, String ipRange) {
179-
String superCidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, netmask);
180-
String subnetNatCidr = calculateSubnetCidrFromIpRange(ipRange);
181-
return new SetupNetrisPublicRangeCommand(zoneId, superCidr, subnetNatCidr);
182-
}
183-
184-
protected void createNetrisPublicIpRangesOnNetrisProvider(long zoneId, NetrisResource netrisResource) {
185-
List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZoneAndTrafficType(zoneId, Networks.TrafficType.Public);
186-
physicalNetworks = physicalNetworks.stream().filter(x -> x.getIsolationMethods().contains(Network.Provider.Netris.getName())).collect(Collectors.toList());
187-
if (CollectionUtils.isEmpty(physicalNetworks)) {
188-
return;
189-
}
190-
for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
191-
List<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
192-
List<Long> vlanDbIds = publicIps.stream()
193-
.filter(x -> !x.isForSystemVms())
194-
.map(IPAddressVO::getVlanId)
195-
.collect(Collectors.toList());
196-
if (CollectionUtils.isEmpty(vlanDbIds)) {
197-
String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
198-
logger.error(msg);
199-
throw new CloudRuntimeException(msg);
200-
}
201-
for (Long vlanDbId : vlanDbIds) {
202-
VlanVO vlanRecord = vlanDao.findById(vlanDbId);
203-
if (vlanRecord == null) {
204-
logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
205-
return;
206-
}
207-
VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
208-
if (vlanDetail == null) {
209-
logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
210-
continue;
211-
}
212-
String gateway = vlanRecord.getVlanGateway();
213-
String netmask = vlanRecord.getVlanNetmask();
214-
String ipRange = vlanRecord.getIpRange();
215-
SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
216-
Answer answer = netrisResource.executeRequest(cmd);
217-
boolean result = answer != null && answer.getResult();
218-
if (!result) {
219-
throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
220-
}
221-
}
222-
}
223-
}
224-
225140
@Override
226141
public List<BaseResponse> listNetrisProviders(Long zoneId) {
227142
List<BaseResponse> netrisControllersResponseList = new ArrayList<>();

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,29 @@
1818

1919
import com.cloud.agent.AgentManager;
2020
import com.cloud.agent.api.Answer;
21+
import com.cloud.dc.VlanDetailsVO;
22+
import com.cloud.dc.VlanVO;
23+
import com.cloud.dc.dao.VlanDao;
24+
import com.cloud.dc.dao.VlanDetailsDao;
2125
import com.cloud.exception.InvalidParameterValueException;
2226
import com.cloud.network.IpAddress;
27+
import com.cloud.network.Network;
2328
import com.cloud.network.Networks;
2429
import com.cloud.network.SDNProviderNetworkRule;
30+
import com.cloud.network.dao.IPAddressDao;
31+
import com.cloud.network.dao.IPAddressVO;
2532
import com.cloud.network.dao.NetrisProviderDao;
2633
import com.cloud.network.dao.NetworkDao;
2734
import com.cloud.network.dao.NetworkVO;
35+
import com.cloud.network.dao.PhysicalNetworkDao;
36+
import com.cloud.network.dao.PhysicalNetworkVO;
2837
import com.cloud.network.element.NetrisProviderVO;
2938
import com.cloud.network.netris.NetrisService;
3039
import com.cloud.network.vpc.Vpc;
40+
import com.cloud.utils.exception.CloudRuntimeException;
41+
import com.cloud.utils.net.NetUtils;
42+
import inet.ipaddr.IPAddress;
43+
import inet.ipaddr.IPAddressString;
3144
import io.netris.model.NatPostBody;
3245
import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
3346
import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
@@ -37,15 +50,21 @@
3750
import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
3851
import org.apache.cloudstack.agent.api.NetrisAnswer;
3952
import org.apache.cloudstack.agent.api.NetrisCommand;
53+
import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
54+
import org.apache.cloudstack.api.ApiConstants;
4055
import org.apache.cloudstack.framework.config.ConfigKey;
4156
import org.apache.cloudstack.framework.config.Configurable;
4257
import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
58+
import org.apache.commons.collections.CollectionUtils;
59+
import org.apache.commons.lang3.StringUtils;
4360
import org.apache.logging.log4j.LogManager;
4461
import org.apache.logging.log4j.Logger;
4562

4663
import javax.inject.Inject;
64+
import java.util.List;
4765
import java.util.Locale;
4866
import java.util.Objects;
67+
import java.util.stream.Collectors;
4968

5069
public class NetrisServiceImpl implements NetrisService, Configurable {
5170

@@ -57,6 +76,14 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
5776
private NetworkDao networkDao;
5877
@Inject
5978
private AgentManager agentMgr;
79+
@Inject
80+
private PhysicalNetworkDao physicalNetworkDao;
81+
@Inject
82+
private IPAddressDao ipAddressDao;
83+
@Inject
84+
private VlanDao vlanDao;
85+
@Inject
86+
private VlanDetailsDao vlanDetailsDao;
6087

6188
@Override
6289
public String getConfigComponentName() {
@@ -87,6 +114,75 @@ private NetrisAnswer sendNetrisCommand(NetrisCommand cmd, long zoneId) {
87114
return (NetrisAnswer) answer;
88115
}
89116

117+
/**
118+
* Calculate the minimum CIDR subnet containing the IP range (using the library: <a href="https://github.com/seancfoley/IPAddress">IPAddress</a>)
119+
* From: <a href="https://github.com/seancfoley/IPAddress/wiki/Code-Examples-3:-Subnetting-and-Other-Subnet-Operations#from-start-and-end-address-get-single-cidr-block-covering-both">Example</a>
120+
* @param ipRange format: startIP-endIP
121+
* @return the minimum CIDR containing the IP range
122+
*/
123+
protected String calculateSubnetCidrFromIpRange(String ipRange) {
124+
if (StringUtils.isBlank(ipRange) || !ipRange.contains("-")) {
125+
return null;
126+
}
127+
String[] rangeArray = ipRange.split("-");
128+
String startIp = rangeArray[0];
129+
String endIp = rangeArray[1];
130+
IPAddress startIpAddress = new IPAddressString(startIp).getAddress();
131+
IPAddress endIpAddress = new IPAddressString(endIp).getAddress();
132+
return startIpAddress.coverWithPrefixBlock(endIpAddress).toPrefixLengthString();
133+
}
134+
135+
/**
136+
* Prepare the Netris Public Range to be used by CloudStack after the zone is created and the Netris provider is added
137+
*/
138+
public SetupNetrisPublicRangeCommand createSetupPublicRangeCommand(long zoneId, String gateway, String netmask, String ipRange) {
139+
String superCidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, netmask);
140+
String subnetNatCidr = calculateSubnetCidrFromIpRange(ipRange);
141+
return new SetupNetrisPublicRangeCommand(zoneId, superCidr, subnetNatCidr);
142+
}
143+
144+
@Override
145+
public boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId) {
146+
List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZoneAndTrafficType(zoneId, Networks.TrafficType.Public);
147+
physicalNetworks = physicalNetworks.stream().filter(x -> x.getIsolationMethods().contains(Network.Provider.Netris.getName())).collect(Collectors.toList());
148+
if (CollectionUtils.isEmpty(physicalNetworks)) {
149+
return false;
150+
}
151+
for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
152+
List<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
153+
List<Long> vlanDbIds = publicIps.stream()
154+
.filter(x -> !x.isForSystemVms())
155+
.map(IPAddressVO::getVlanId)
156+
.collect(Collectors.toList());
157+
if (CollectionUtils.isEmpty(vlanDbIds)) {
158+
String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
159+
logger.error(msg);
160+
throw new CloudRuntimeException(msg);
161+
}
162+
for (Long vlanDbId : vlanDbIds) {
163+
VlanVO vlanRecord = vlanDao.findById(vlanDbId);
164+
if (vlanRecord == null) {
165+
logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
166+
return false;
167+
}
168+
VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
169+
if (vlanDetail == null) {
170+
logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
171+
continue;
172+
}
173+
String gateway = vlanRecord.getVlanGateway();
174+
String netmask = vlanRecord.getVlanNetmask();
175+
String ipRange = vlanRecord.getIpRange();
176+
SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
177+
NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
178+
if (!answer.getResult()) {
179+
throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
180+
}
181+
}
182+
}
183+
return true;
184+
}
185+
90186
@Override
91187
public boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpc) {
92188
CreateNetrisVpcCommand cmd = new CreateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, cidr, vpcId, isVpc);
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
import org.junit.Assert;
2020
import org.junit.Test;
2121

22-
public class NetrisProviderServiceImplTest {
22+
public class NetrisServiceImplTest {
2323

24-
private NetrisProviderServiceImpl service = new NetrisProviderServiceImpl();
24+
private NetrisServiceImpl service = new NetrisServiceImpl();
2525

2626
@Test
2727
public void testCalculateSubnetCidrFromIpRange() {

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
import com.cloud.network.dao.NetrisProviderDao;
4949
import com.cloud.network.element.NetrisProviderVO;
50+
import com.cloud.network.netris.NetrisService;
5051
import org.apache.cloudstack.acl.SecurityChecker;
5152
import org.apache.cloudstack.affinity.AffinityGroup;
5253
import org.apache.cloudstack.affinity.AffinityGroupService;
@@ -474,6 +475,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
474475
NsxProviderDao nsxProviderDao;
475476
@Inject
476477
NetrisProviderDao netrisProviderDao;
478+
@Inject
479+
NetrisService netrisService;
477480

478481
// FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao?
479482
@Inject
@@ -4577,6 +4580,17 @@ public Vlan createVlanAndPublicIpRange(final CreateVlanIpRangeCmd cmd) throws In
45774580
throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
45784581
}
45794582

4583+
// If external provider is provided, verify zone has that provider enabled
4584+
Provider provider = cmd.getProvider();
4585+
if (Objects.nonNull(provider)) {
4586+
boolean unsupported =
4587+
(Provider.Nsx == provider && nsxProviderDao.findByZoneId(zoneId) == null) ||
4588+
(Provider.Netris == provider && netrisProviderDao.findByZoneId(zoneId) == null);
4589+
if (unsupported) {
4590+
throw new InvalidParameterValueException(String.format("Cannot add public IP range as the zone does not support provider: %s", provider.getName()));
4591+
}
4592+
}
4593+
45804594
// verify that physical network exists
45814595
PhysicalNetworkVO pNtwk = null;
45824596
if (physicalNetworkId != null) {
@@ -4748,6 +4762,9 @@ public Vlan doInTransaction(final TransactionStatus status) {
47484762
}
47494763
});
47504764

4765+
if (provider == Provider.Netris) {
4766+
netrisService.createIPAMAllocationsForZoneLevelPublicRanges(zoneId);
4767+
}
47514768
messageBus.publish(_name, MESSAGE_CREATE_VLAN_IP_RANGE_EVENT, PublishScope.LOCAL, vlan);
47524769

47534770
return vlan;

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,16 @@
244244
<a-form-item name="endip" ref="endip" :label="$t('label.endip')" class="form__item">
245245
<a-input v-model:value="form.endip" />
246246
</a-form-item>
247+
<a-form-item name="provider" ref="provider">
248+
<template #label>
249+
<tooltip-label :title="$t('label.provider')"/>
250+
</template>
251+
<a-select v-model:value="form.provider">
252+
<a-select-option value=""></a-select-option>
253+
<a-select-option value="NSX">{{ $t('label.nsx') }}</a-select-option>
254+
<a-select-option value="Netris">{{ $t('label.netris') }}</a-select-option>
255+
</a-select>
256+
</a-form-item>
247257
</div>
248258
<div class="form__item" v-if="!basicGuestNetwork && form.iptype != 'ip6'">
249259
<tooltip-label :title="$t('label.set.reservation')" :tooltip="$t('label.set.reservation.desc')" class="tooltip-label-wrapper"/>
@@ -633,6 +643,7 @@ export default {
633643
params.podid = values.podid
634644
params.networkid = this.network.id
635645
}
646+
params.provider = values.provider
636647
api('createVlanIpRange', params).then(() => {
637648
this.$notification.success({
638649
message: this.$t('message.success.add.iprange')

0 commit comments

Comments
 (0)