This is an automated email from the ASF dual-hosted git repository. pearl11594 pushed a commit to branch netris-integration-upstream in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit b9400836d9382f8f48dd634d6a4c318efd461b3e Author: Pearl Dsilva <pearl1...@gmail.com> AuthorDate: Fri Dec 6 11:48:45 2024 -0500 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 <nicovazque...@gmail.com> --- .../com/cloud/network/netris/NetrisService.java | 1 + .../service/NetrisProviderServiceImpl.java | 91 +------------------- .../cloudstack/service/NetrisServiceImpl.java | 96 ++++++++++++++++++++++ ...iceImplTest.java => NetrisServiceImplTest.java} | 4 +- .../configuration/ConfigurationManagerImpl.java | 17 ++++ ui/src/views/infra/network/IpRangesTabPublic.vue | 11 +++ 6 files changed, 130 insertions(+), 90 deletions(-) diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java index ea2f73383e0..d989a53fcdb 100644 --- a/api/src/main/java/com/cloud/network/netris/NetrisService.java +++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java @@ -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); diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java index 119a4c80f94..a18251cee11 100644 --- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java +++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java @@ -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<>(); diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java index b1e00f771cd..a8fe3ae2054 100644 --- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java +++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java @@ -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); diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisServiceImplTest.java similarity index 90% rename from plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java rename to plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisServiceImplTest.java index 233787d9004..807324f404a 100644 --- a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java +++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisServiceImplTest.java @@ -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() { diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 17c2385cdaa..9b00a53e969 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -52,6 +52,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; @@ -477,6 +478,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 @@ -4664,6 +4667,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) { @@ -4833,6 +4847,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; diff --git a/ui/src/views/infra/network/IpRangesTabPublic.vue b/ui/src/views/infra/network/IpRangesTabPublic.vue index 3955b8c0359..0f3610f5e05 100644 --- a/ui/src/views/infra/network/IpRangesTabPublic.vue +++ b/ui/src/views/infra/network/IpRangesTabPublic.vue @@ -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')