mirror of https://github.com/apache/cloudstack.git
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 <nicovazquez90@gmail.com>
This commit is contained in:
parent
aef61973f3
commit
4bf4dafcb3
|
|
@ -21,6 +21,7 @@ import com.cloud.network.SDNProviderNetworkRule;
|
|||
import com.cloud.network.vpc.Vpc;
|
||||
|
||||
public interface NetrisService {
|
||||
boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId);
|
||||
boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
|
||||
boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
|
||||
boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
|
||||
|
|
|
|||
|
|
@ -16,21 +16,14 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.service;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.VlanDetailsVO;
|
||||
import com.cloud.dc.VlanVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.VlanDao;
|
||||
import com.cloud.dc.dao.VlanDetailsDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.host.DetailVO;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.dao.HostDetailsDao;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.Networks;
|
||||
import com.cloud.network.dao.IPAddressDao;
|
||||
import com.cloud.network.dao.IPAddressVO;
|
||||
import com.cloud.network.dao.NetrisProviderDao;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
|
|
@ -38,16 +31,12 @@ import com.cloud.network.dao.PhysicalNetworkDao;
|
|||
import com.cloud.network.dao.PhysicalNetworkVO;
|
||||
import com.cloud.network.element.NetrisProviderVO;
|
||||
import com.cloud.network.netris.NetrisProvider;
|
||||
import com.cloud.network.netris.NetrisService;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.IPAddressString;
|
||||
import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
|
||||
import org.apache.cloudstack.api.command.DeleteNetrisProviderCmd;
|
||||
|
|
@ -56,7 +45,6 @@ import org.apache.cloudstack.api.response.NetrisProviderResponse;
|
|||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.resource.NetrisResource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
|
@ -68,7 +56,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NetrisProviderServiceImpl implements NetrisProviderService {
|
||||
|
||||
|
|
@ -87,11 +74,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
|
|||
@Inject
|
||||
NetworkDao networkDao;
|
||||
@Inject
|
||||
private IPAddressDao ipAddressDao;
|
||||
@Inject
|
||||
private VlanDao vlanDao;
|
||||
@Inject
|
||||
private VlanDetailsDao vlanDetailsDao;
|
||||
private NetrisService netrisService;
|
||||
|
||||
@Override
|
||||
public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
|
||||
|
|
@ -147,81 +130,13 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
|
|||
} else {
|
||||
throw new CloudRuntimeException("Failed to add Netris controller due to internal error.");
|
||||
}
|
||||
createNetrisPublicIpRangesOnNetrisProvider(zoneId, netrisResource);
|
||||
netrisService.createIPAMAllocationsForZoneLevelPublicRanges(zoneId);
|
||||
} catch (ConfigurationException e) {
|
||||
throw new CloudRuntimeException(e.getMessage());
|
||||
}
|
||||
return netrisProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the minimum CIDR subnet containing the IP range (using the library: <a href="https://github.com/seancfoley/IPAddress">IPAddress</a>)
|
||||
* 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>
|
||||
* @param ipRange format: startIP-endIP
|
||||
* @return the minimum CIDR containing the IP range
|
||||
*/
|
||||
protected String calculateSubnetCidrFromIpRange(String ipRange) {
|
||||
if (StringUtils.isBlank(ipRange) || !ipRange.contains("-")) {
|
||||
return null;
|
||||
}
|
||||
String[] rangeArray = ipRange.split("-");
|
||||
String startIp = rangeArray[0];
|
||||
String endIp = rangeArray[1];
|
||||
IPAddress startIpAddress = new IPAddressString(startIp).getAddress();
|
||||
IPAddress endIpAddress = new IPAddressString(endIp).getAddress();
|
||||
return startIpAddress.coverWithPrefixBlock(endIpAddress).toPrefixLengthString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the Netris Public Range to be used by CloudStack after the zone is created and the Netris provider is added
|
||||
*/
|
||||
public SetupNetrisPublicRangeCommand createSetupPublicRangeCommand(long zoneId, String gateway, String netmask, String ipRange) {
|
||||
String superCidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, netmask);
|
||||
String subnetNatCidr = calculateSubnetCidrFromIpRange(ipRange);
|
||||
return new SetupNetrisPublicRangeCommand(zoneId, superCidr, subnetNatCidr);
|
||||
}
|
||||
|
||||
protected void createNetrisPublicIpRangesOnNetrisProvider(long zoneId, NetrisResource netrisResource) {
|
||||
List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZoneAndTrafficType(zoneId, Networks.TrafficType.Public);
|
||||
physicalNetworks = physicalNetworks.stream().filter(x -> x.getIsolationMethods().contains(Network.Provider.Netris.getName())).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(physicalNetworks)) {
|
||||
return;
|
||||
}
|
||||
for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
|
||||
List<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
|
||||
List<Long> vlanDbIds = publicIps.stream()
|
||||
.filter(x -> !x.isForSystemVms())
|
||||
.map(IPAddressVO::getVlanId)
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(vlanDbIds)) {
|
||||
String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
|
||||
logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
for (Long vlanDbId : vlanDbIds) {
|
||||
VlanVO vlanRecord = vlanDao.findById(vlanDbId);
|
||||
if (vlanRecord == null) {
|
||||
logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
|
||||
return;
|
||||
}
|
||||
VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
|
||||
if (vlanDetail == null) {
|
||||
logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
|
||||
continue;
|
||||
}
|
||||
String gateway = vlanRecord.getVlanGateway();
|
||||
String netmask = vlanRecord.getVlanNetmask();
|
||||
String ipRange = vlanRecord.getIpRange();
|
||||
SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
|
||||
Answer answer = netrisResource.executeRequest(cmd);
|
||||
boolean result = answer != null && answer.getResult();
|
||||
if (!result) {
|
||||
throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BaseResponse> listNetrisProviders(Long zoneId) {
|
||||
List<BaseResponse> netrisControllersResponseList = new ArrayList<>();
|
||||
|
|
|
|||
|
|
@ -18,16 +18,29 @@ package org.apache.cloudstack.service;
|
|||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.dc.VlanDetailsVO;
|
||||
import com.cloud.dc.VlanVO;
|
||||
import com.cloud.dc.dao.VlanDao;
|
||||
import com.cloud.dc.dao.VlanDetailsDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.network.IpAddress;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.Networks;
|
||||
import com.cloud.network.SDNProviderNetworkRule;
|
||||
import com.cloud.network.dao.IPAddressDao;
|
||||
import com.cloud.network.dao.IPAddressVO;
|
||||
import com.cloud.network.dao.NetrisProviderDao;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.network.dao.PhysicalNetworkDao;
|
||||
import com.cloud.network.dao.PhysicalNetworkVO;
|
||||
import com.cloud.network.element.NetrisProviderVO;
|
||||
import com.cloud.network.netris.NetrisService;
|
||||
import com.cloud.network.vpc.Vpc;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.IPAddressString;
|
||||
import io.netris.model.NatPostBody;
|
||||
import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
|
||||
import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
|
||||
|
|
@ -37,15 +50,21 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
|
|||
import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
|
||||
import org.apache.cloudstack.agent.api.NetrisAnswer;
|
||||
import org.apache.cloudstack.agent.api.NetrisCommand;
|
||||
import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NetrisServiceImpl implements NetrisService, Configurable {
|
||||
|
||||
|
|
@ -57,6 +76,14 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
|
|||
private NetworkDao networkDao;
|
||||
@Inject
|
||||
private AgentManager agentMgr;
|
||||
@Inject
|
||||
private PhysicalNetworkDao physicalNetworkDao;
|
||||
@Inject
|
||||
private IPAddressDao ipAddressDao;
|
||||
@Inject
|
||||
private VlanDao vlanDao;
|
||||
@Inject
|
||||
private VlanDetailsDao vlanDetailsDao;
|
||||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
|
|
@ -87,6 +114,75 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
|
|||
return (NetrisAnswer) answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the minimum CIDR subnet containing the IP range (using the library: <a href="https://github.com/seancfoley/IPAddress">IPAddress</a>)
|
||||
* 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>
|
||||
* @param ipRange format: startIP-endIP
|
||||
* @return the minimum CIDR containing the IP range
|
||||
*/
|
||||
protected String calculateSubnetCidrFromIpRange(String ipRange) {
|
||||
if (StringUtils.isBlank(ipRange) || !ipRange.contains("-")) {
|
||||
return null;
|
||||
}
|
||||
String[] rangeArray = ipRange.split("-");
|
||||
String startIp = rangeArray[0];
|
||||
String endIp = rangeArray[1];
|
||||
IPAddress startIpAddress = new IPAddressString(startIp).getAddress();
|
||||
IPAddress endIpAddress = new IPAddressString(endIp).getAddress();
|
||||
return startIpAddress.coverWithPrefixBlock(endIpAddress).toPrefixLengthString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the Netris Public Range to be used by CloudStack after the zone is created and the Netris provider is added
|
||||
*/
|
||||
public SetupNetrisPublicRangeCommand createSetupPublicRangeCommand(long zoneId, String gateway, String netmask, String ipRange) {
|
||||
String superCidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, netmask);
|
||||
String subnetNatCidr = calculateSubnetCidrFromIpRange(ipRange);
|
||||
return new SetupNetrisPublicRangeCommand(zoneId, superCidr, subnetNatCidr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId) {
|
||||
List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZoneAndTrafficType(zoneId, Networks.TrafficType.Public);
|
||||
physicalNetworks = physicalNetworks.stream().filter(x -> x.getIsolationMethods().contains(Network.Provider.Netris.getName())).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(physicalNetworks)) {
|
||||
return false;
|
||||
}
|
||||
for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
|
||||
List<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
|
||||
List<Long> vlanDbIds = publicIps.stream()
|
||||
.filter(x -> !x.isForSystemVms())
|
||||
.map(IPAddressVO::getVlanId)
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(vlanDbIds)) {
|
||||
String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
|
||||
logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
for (Long vlanDbId : vlanDbIds) {
|
||||
VlanVO vlanRecord = vlanDao.findById(vlanDbId);
|
||||
if (vlanRecord == null) {
|
||||
logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
|
||||
return false;
|
||||
}
|
||||
VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
|
||||
if (vlanDetail == null) {
|
||||
logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
|
||||
continue;
|
||||
}
|
||||
String gateway = vlanRecord.getVlanGateway();
|
||||
String netmask = vlanRecord.getVlanNetmask();
|
||||
String ipRange = vlanRecord.getIpRange();
|
||||
SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
|
||||
NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
|
||||
if (!answer.getResult()) {
|
||||
throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpc) {
|
||||
CreateNetrisVpcCommand cmd = new CreateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, cidr, vpcId, isVpc);
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package org.apache.cloudstack.service;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NetrisProviderServiceImplTest {
|
||||
public class NetrisServiceImplTest {
|
||||
|
||||
private NetrisProviderServiceImpl service = new NetrisProviderServiceImpl();
|
||||
private NetrisServiceImpl service = new NetrisServiceImpl();
|
||||
|
||||
@Test
|
||||
public void testCalculateSubnetCidrFromIpRange() {
|
||||
|
|
@ -47,6 +47,7 @@ import javax.naming.ConfigurationException;
|
|||
|
||||
import com.cloud.network.dao.NetrisProviderDao;
|
||||
import com.cloud.network.element.NetrisProviderVO;
|
||||
import com.cloud.network.netris.NetrisService;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||
|
|
@ -474,6 +475,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
NsxProviderDao nsxProviderDao;
|
||||
@Inject
|
||||
NetrisProviderDao netrisProviderDao;
|
||||
@Inject
|
||||
NetrisService netrisService;
|
||||
|
||||
// FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao?
|
||||
@Inject
|
||||
|
|
@ -4577,6 +4580,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
|
||||
}
|
||||
|
||||
// If external provider is provided, verify zone has that provider enabled
|
||||
Provider provider = cmd.getProvider();
|
||||
if (Objects.nonNull(provider)) {
|
||||
boolean unsupported =
|
||||
(Provider.Nsx == provider && nsxProviderDao.findByZoneId(zoneId) == null) ||
|
||||
(Provider.Netris == provider && netrisProviderDao.findByZoneId(zoneId) == null);
|
||||
if (unsupported) {
|
||||
throw new InvalidParameterValueException(String.format("Cannot add public IP range as the zone does not support provider: %s", provider.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
// verify that physical network exists
|
||||
PhysicalNetworkVO pNtwk = null;
|
||||
if (physicalNetworkId != null) {
|
||||
|
|
@ -4748,6 +4762,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
}
|
||||
});
|
||||
|
||||
if (provider == Provider.Netris) {
|
||||
netrisService.createIPAMAllocationsForZoneLevelPublicRanges(zoneId);
|
||||
}
|
||||
messageBus.publish(_name, MESSAGE_CREATE_VLAN_IP_RANGE_EVENT, PublishScope.LOCAL, vlan);
|
||||
|
||||
return vlan;
|
||||
|
|
|
|||
|
|
@ -244,6 +244,16 @@
|
|||
<a-form-item name="endip" ref="endip" :label="$t('label.endip')" class="form__item">
|
||||
<a-input v-model:value="form.endip" />
|
||||
</a-form-item>
|
||||
<a-form-item name="provider" ref="provider">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.provider')"/>
|
||||
</template>
|
||||
<a-select v-model:value="form.provider">
|
||||
<a-select-option value=""></a-select-option>
|
||||
<a-select-option value="NSX">{{ $t('label.nsx') }}</a-select-option>
|
||||
<a-select-option value="Netris">{{ $t('label.netris') }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="form__item" v-if="!basicGuestNetwork && form.iptype != 'ip6'">
|
||||
<tooltip-label :title="$t('label.set.reservation')" :tooltip="$t('label.set.reservation.desc')" class="tooltip-label-wrapper"/>
|
||||
|
|
@ -633,6 +643,7 @@ export default {
|
|||
params.podid = values.podid
|
||||
params.networkid = this.network.id
|
||||
}
|
||||
params.provider = values.provider
|
||||
api('createVlanIpRange', params).then(() => {
|
||||
this.$notification.success({
|
||||
message: this.$t('message.success.add.iprange')
|
||||
|
|
|
|||
Loading…
Reference in New Issue