mirror of https://github.com/apache/cloudstack.git
Set up Netris Public range on new zone addition (#15)
* Set up Netris Public range on new zone addition * Add dependency to calculate subnet containing a start and end IP * Remove unused import * Move dependency to the netris module * Rename Netris IP range * Refactor logic * Revert "Refactor logic" This reverts commit 7ec36a81320444c37e7bb914dd895060b663411b. * Fix setup range after adding Netris Provider * Fix VXLAN range adding on zone creation
This commit is contained in:
parent
6e41063808
commit
9b4d95780a
|
|
@ -35,5 +35,10 @@
|
|||
<artifactId>netris-java-sdk</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.seancfoley</groupId>
|
||||
<artifactId>ipaddress</artifactId>
|
||||
<version>5.5.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ import com.cloud.agent.api.Command;
|
|||
|
||||
public class NetrisCommand extends Command {
|
||||
private final long zoneId;
|
||||
private final long accountId;
|
||||
private final long domainId;
|
||||
private final Long accountId;
|
||||
private final Long domainId;
|
||||
private final String name;
|
||||
private final long id;
|
||||
private final boolean isVpc;
|
||||
|
||||
public NetrisCommand(long zoneId, long accountId, long domainId, String name, long id, boolean isVpc) {
|
||||
public NetrisCommand(long zoneId, Long accountId, Long domainId, String name, long id, boolean isVpc) {
|
||||
this.zoneId = zoneId;
|
||||
this.accountId = accountId;
|
||||
this.domainId = domainId;
|
||||
|
|
@ -48,11 +48,11 @@ public class NetrisCommand extends Command {
|
|||
return zoneId;
|
||||
}
|
||||
|
||||
public long getAccountId() {
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public long getDomainId() {
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.agent.api;
|
||||
|
||||
public class SetupNetrisPublicRangeCommand extends NetrisCommand {
|
||||
|
||||
private final String superCidr;
|
||||
private final String exactCidr;
|
||||
|
||||
public SetupNetrisPublicRangeCommand(long zoneId, String superCidr, String exactCidr) {
|
||||
super(zoneId, null, null, null, zoneId, false);
|
||||
this.superCidr = superCidr;
|
||||
this.exactCidr = exactCidr;
|
||||
}
|
||||
|
||||
public String getSuperCidr() {
|
||||
return superCidr;
|
||||
}
|
||||
|
||||
public String getExactCidr() {
|
||||
return exactCidr;
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@ 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.StartupNetrisCommand;
|
||||
import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
|
||||
import org.apache.cloudstack.service.NetrisApiClient;
|
||||
import org.apache.cloudstack.service.NetrisApiClientImpl;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
|
@ -94,6 +95,8 @@ public class NetrisResource implements ServerResource {
|
|||
return executeRequest((CreateNetrisVnetCommand) cmd);
|
||||
} else if (cmd instanceof DeleteNetrisVnetCommand) {
|
||||
return executeRequest((DeleteNetrisVnetCommand) cmd);
|
||||
} else if (cmd instanceof SetupNetrisPublicRangeCommand) {
|
||||
return executeRequest((SetupNetrisPublicRangeCommand) cmd);
|
||||
} else {
|
||||
return Answer.createUnsupportedCommandAnswer(cmd);
|
||||
}
|
||||
|
|
@ -250,6 +253,14 @@ public class NetrisResource implements ServerResource {
|
|||
return new NetrisAnswer(cmd, true, "OK");
|
||||
}
|
||||
|
||||
private Answer executeRequest(SetupNetrisPublicRangeCommand cmd) {
|
||||
boolean result = netrisApiClient.setupZoneLevelPublicRange(cmd);
|
||||
if (!result) {
|
||||
return new NetrisAnswer(cmd, false, "Netris Setup for Public Range failed");
|
||||
}
|
||||
return new NetrisAnswer(cmd, true, "OK");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -27,14 +27,19 @@ public class NetrisResourceObjectUtils {
|
|||
|
||||
public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, NetrisObjectType netrisObjectType, String... suffixes) {
|
||||
long zoneId = cmd.getZoneId();
|
||||
long accountId = cmd.getAccountId();
|
||||
long domainId = cmd.getDomainId();
|
||||
Long accountId = cmd.getAccountId();
|
||||
Long domainId = cmd.getDomainId();
|
||||
long objectId = cmd.getId();
|
||||
String objectName = cmd.getName();
|
||||
boolean isVpc = cmd.isVpc();
|
||||
boolean isZoneLevel = accountId == null && domainId == null;
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(String.format("D%s-A%s-Z%s", domainId, accountId, zoneId));
|
||||
if (isZoneLevel) {
|
||||
stringBuilder.append(String.format("Z%s", zoneId));
|
||||
} else {
|
||||
stringBuilder.append(String.format("D%s-A%s-Z%s", domainId, accountId, zoneId));
|
||||
}
|
||||
String prefix = isVpc ? "-V" : "-N";
|
||||
switch (netrisObjectType) {
|
||||
case VPC:
|
||||
|
|
@ -46,10 +51,14 @@ public class NetrisResourceObjectUtils {
|
|||
}
|
||||
break;
|
||||
case IPAM_ALLOCATION:
|
||||
stringBuilder.append(String.format("%s%s", prefix, objectId));
|
||||
if (!isZoneLevel) {
|
||||
stringBuilder.append(String.format("%s%s", prefix, objectId));
|
||||
}
|
||||
break;
|
||||
case IPAM_SUBNET:
|
||||
stringBuilder.append(String.format("-N%s", objectId));
|
||||
if (!isZoneLevel) {
|
||||
stringBuilder.append(String.format("-N%s", objectId));
|
||||
}
|
||||
break;
|
||||
case VNET:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
|
|||
import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
|
||||
import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
|
||||
import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
|
||||
import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -50,4 +51,11 @@ public interface NetrisApiClient {
|
|||
boolean createVnet(CreateNetrisVnetCommand cmd);
|
||||
|
||||
boolean deleteVnet(DeleteNetrisVnetCommand cmd);
|
||||
|
||||
/**
|
||||
* Check and create zone level Netris Public range in the following manner:
|
||||
* - Check the IPAM allocation for the zone super CIDR. In case it doesn't exist, create it
|
||||
* - Check the IPAM subnet for NAT purpose for the range start-end. In case it doesn't exist, create it
|
||||
*/
|
||||
boolean setupZoneLevelPublicRange(SetupNetrisPublicRangeCommand cmd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ import io.netris.model.FilterBySites;
|
|||
import io.netris.model.FilterByVpc;
|
||||
import io.netris.model.GetSiteBody;
|
||||
import io.netris.model.InlineResponse2004;
|
||||
import io.netris.model.InlineResponse2004Data;
|
||||
import io.netris.model.IpTree;
|
||||
import io.netris.model.IpTreeAllocation;
|
||||
import io.netris.model.IpTreeAllocationTenant;
|
||||
import io.netris.model.IpTreeSubnet;
|
||||
import io.netris.model.IpTreeSubnetSites;
|
||||
|
|
@ -63,6 +66,7 @@ import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
|
|||
import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
|
||||
import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
|
||||
import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
|
||||
import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
|
||||
import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
|
@ -196,25 +200,31 @@ public class NetrisApiClientImpl implements NetrisApiClient {
|
|||
return response;
|
||||
}
|
||||
|
||||
private InlineResponse2004 createVpcAllocationInternal(VPCResponseObjectOK createdVpc, String cidr, int adminTenantId,
|
||||
String adminTenantName, String netrisIpamAllocationName) {
|
||||
logger.debug(String.format("Creating Netris VPC Allocation %s for VPC %s", cidr, createdVpc.getData().getName()));
|
||||
private InlineResponse2004Data createIpamAllocationInternal(String ipamName, String ipamPrefix, VPCListing vpc) {
|
||||
logger.debug(String.format("Creating Netris IPAM Allocation %s for VPC %s", ipamPrefix, vpc.getName()));
|
||||
try {
|
||||
IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
|
||||
AllocationBody body = new AllocationBody();
|
||||
AllocationBodyVpc allocationBodyVpc = new AllocationBodyVpc();
|
||||
allocationBodyVpc.setId(createdVpc.getData().getId());
|
||||
allocationBodyVpc.setName(createdVpc.getData().getName());
|
||||
allocationBodyVpc.setId(vpc.getId());
|
||||
allocationBodyVpc.setName(vpc.getName());
|
||||
body.setVpc(allocationBodyVpc);
|
||||
body.setName(netrisIpamAllocationName);
|
||||
body.setPrefix(cidr);
|
||||
body.setName(ipamName);
|
||||
body.setPrefix(ipamPrefix);
|
||||
IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
|
||||
allocationTenant.setId(new BigDecimal(adminTenantId));
|
||||
allocationTenant.setName(adminTenantName);
|
||||
allocationTenant.setId(new BigDecimal(tenantId));
|
||||
allocationTenant.setName(tenantName);
|
||||
body.setTenant(allocationTenant);
|
||||
return ipamApi.apiV2IpamAllocationPost(body);
|
||||
InlineResponse2004 ipamResponse = ipamApi.apiV2IpamAllocationPost(body);
|
||||
if (ipamResponse == null || !ipamResponse.isIsSuccess()) {
|
||||
String reason = ipamResponse == null ? "Empty response" : "Operation failed on Netris";
|
||||
logger.debug("The Netris Allocation {} for VPC {} creation failed: {}", ipamPrefix, vpc.getName(), reason);
|
||||
return null;
|
||||
}
|
||||
logger.debug(String.format("Successfully created VPC %s and its IPAM Allocation %s on Netris", vpc.getName(), ipamPrefix));
|
||||
return ipamResponse.getData();
|
||||
} catch (ApiException e) {
|
||||
logAndThrowException(String.format("Error creating Netris Allocation %s for VPC %s", cidr, createdVpc.getData().getName()), e);
|
||||
logAndThrowException(String.format("Error creating Netris IPAM Allocation %s for VPC %s", ipamPrefix, vpc.getName()), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -231,14 +241,8 @@ public class NetrisApiClientImpl implements NetrisApiClient {
|
|||
|
||||
String netrisIpamAllocationName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, cmd.getCidr());
|
||||
String vpcCidr = cmd.getCidr();
|
||||
InlineResponse2004 ipamResponse = createVpcAllocationInternal(createdVpc, vpcCidr, tenantId, tenantName, netrisIpamAllocationName);
|
||||
if (ipamResponse == null || !ipamResponse.isIsSuccess()) {
|
||||
String reason = ipamResponse == null ? "Empty response" : "Operation failed on Netris";
|
||||
logger.debug("The Netris Allocation {} for VPC {} creation failed: {}", vpcCidr, cmd.getName(), reason);
|
||||
return false;
|
||||
}
|
||||
logger.debug(String.format("Successfully created VPC %s and its IPAM Allocation %s on Netris", cmd.getName(), vpcCidr));
|
||||
return true;
|
||||
InlineResponse2004Data createdIpamAllocation = createIpamAllocationInternal(netrisIpamAllocationName, vpcCidr, createdVpc.getData());
|
||||
return createdIpamAllocation != null;
|
||||
}
|
||||
|
||||
private void deleteVpcIpamAllocationInternal(VPCListing vpcResource, String vpcCidr) {
|
||||
|
|
@ -334,12 +338,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
|
|||
String netrisVnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VNET, vNetName) ;
|
||||
String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
|
||||
|
||||
InlineResponse2004 subnetResponse = createVpcSubnetInternal(associatedVpc, vNetName, vnetCidr, netrisSubnetName);
|
||||
if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
|
||||
String reason = subnetResponse == null ? "Empty response" : "Operation failed on Netris";
|
||||
logger.debug("The Netris Subnet {} for network {} creation failed: {}", vnetCidr, networkName, reason);
|
||||
return false;
|
||||
}
|
||||
createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
|
||||
logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
|
||||
|
||||
VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr);
|
||||
|
|
@ -392,6 +391,71 @@ public class NetrisApiClientImpl implements NetrisApiClient {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected VPCListing getSystemVpc() throws ApiException {
|
||||
List<VPCListing> systemVpcList = listVPCs().stream().filter(VPCListing::isIsSystem).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(systemVpcList)) {
|
||||
String msg = "Cannot find any system VPC";
|
||||
logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
return systemVpcList.get(0);
|
||||
}
|
||||
|
||||
private BigDecimal getIpamAllocationIdByPrefixAndVpc(String superCidrPrefix, VPCListing vpc) throws ApiException {
|
||||
IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
|
||||
FilterBySites filterBySites = new FilterBySites();
|
||||
filterBySites.add(siteId);
|
||||
FilterByVpc filterByVpc = new FilterByVpc();
|
||||
filterByVpc.add(vpc.getId());
|
||||
IpTree ipamTree = ipamApi.apiV2IpamGet(filterBySites, filterByVpc);
|
||||
List<IpTreeAllocation> superCidrList = ipamTree.getData().stream()
|
||||
.filter(x -> x.getPrefix().equals(superCidrPrefix))
|
||||
.collect(Collectors.toList());
|
||||
return CollectionUtils.isEmpty(superCidrList) ? null : superCidrList.get(0).getId();
|
||||
}
|
||||
|
||||
private IpTreeSubnet getIpamSubnetByAllocationAndPrefixAndPurposeAndVpc(BigDecimal ipamAllocationId, String exactCidr, IpTreeSubnet.PurposeEnum purpose, VPCListing vpc) throws ApiException {
|
||||
IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
|
||||
FilterByVpc filterByVpc = new FilterByVpc();
|
||||
filterByVpc.add(vpc.getId());
|
||||
SubnetResBody subnetResBody = ipamApi.apiV2IpamSubnetsGet(filterByVpc);
|
||||
List<IpTreeSubnet> exactSubnetList = subnetResBody.getData().stream()
|
||||
.filter(x -> x.getAllocationID().equals(ipamAllocationId) && x.getPrefix().equals(exactCidr) && x.getPurpose() == purpose)
|
||||
.collect(Collectors.toList());
|
||||
return CollectionUtils.isEmpty(exactSubnetList) ? null : exactSubnetList.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setupZoneLevelPublicRange(SetupNetrisPublicRangeCommand cmd) {
|
||||
String superCidr = cmd.getSuperCidr();
|
||||
String exactCidr = cmd.getExactCidr();
|
||||
try {
|
||||
VPCListing systemVpc = getSystemVpc();
|
||||
logger.debug("Checking if the Netris Public Super CIDR {} exists", superCidr);
|
||||
BigDecimal ipamAllocationId = getIpamAllocationIdByPrefixAndVpc(superCidr, systemVpc);
|
||||
if (ipamAllocationId == null) {
|
||||
String ipamName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, superCidr);
|
||||
InlineResponse2004Data ipamAllocation = createIpamAllocationInternal(ipamName, superCidr, systemVpc);
|
||||
if (ipamAllocation == null) {
|
||||
String msg = String.format("Could not create the zone level super CIDR %s for the system VPC", superCidr);
|
||||
logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
ipamAllocationId = new BigDecimal(ipamAllocation.getId());
|
||||
}
|
||||
IpTreeSubnet exactSubnet = getIpamSubnetByAllocationAndPrefixAndPurposeAndVpc(ipamAllocationId, exactCidr, IpTreeSubnet.PurposeEnum.NAT, systemVpc);
|
||||
if (exactSubnet == null) {
|
||||
String ipamSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, exactCidr);
|
||||
createIpamSubnetInternal(ipamSubnetName, exactCidr, SubnetBody.PurposeEnum.NAT, systemVpc);
|
||||
}
|
||||
} catch (ApiException e) {
|
||||
String msg = String.format("Error setting up the Netris Public Range %s on super CIDR %s", exactCidr, superCidr);
|
||||
logAndThrowException(msg, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteVnetInternal(VPCListing associatedVpc, FilterBySites siteFilter, FilterByVpc vpcFilter, String netrisVnetName, String vNetName) {
|
||||
try {
|
||||
VNetApi vNetApi = apiClient.getApiStubForMethod(VNetApi.class);
|
||||
|
|
@ -433,16 +497,16 @@ public class NetrisApiClientImpl implements NetrisApiClient {
|
|||
}
|
||||
}
|
||||
|
||||
private InlineResponse2004 createVpcSubnetInternal(VPCListing associatedVpc, String vNetName, String vNetCidr, String netrisSubnetName) {
|
||||
logger.debug("Creating Netris VPC Subnet {} for VPC {} for vNet {}", vNetCidr, associatedVpc.getName(), vNetName);
|
||||
private InlineResponse2004Data createIpamSubnetInternal(String subnetName, String subnetPrefix, SubnetBody.PurposeEnum purpose, VPCListing vpc) {
|
||||
logger.debug("Creating Netris IPAM Subnet {} for VPC {}", subnetPrefix, vpc.getName());
|
||||
try {
|
||||
|
||||
SubnetBody subnetBody = new SubnetBody();
|
||||
subnetBody.setName(netrisSubnetName);
|
||||
subnetBody.setName(subnetName);
|
||||
|
||||
AllocationBodyVpc vpcAllocationBody = new AllocationBodyVpc();
|
||||
vpcAllocationBody.setName(associatedVpc.getName());
|
||||
vpcAllocationBody.setId(associatedVpc.getId());
|
||||
vpcAllocationBody.setName(vpc.getName());
|
||||
vpcAllocationBody.setId(vpc.getId());
|
||||
subnetBody.setVpc(vpcAllocationBody);
|
||||
|
||||
IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
|
||||
|
|
@ -455,12 +519,18 @@ public class NetrisApiClientImpl implements NetrisApiClient {
|
|||
subnetSites.setName(siteName);
|
||||
subnetBody.setSites(List.of(subnetSites));
|
||||
|
||||
subnetBody.setPurpose(SubnetBody.PurposeEnum.COMMON);
|
||||
subnetBody.setPrefix(vNetCidr);
|
||||
subnetBody.setPurpose(purpose);
|
||||
subnetBody.setPrefix(subnetPrefix);
|
||||
IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
|
||||
return ipamApi.apiV2IpamSubnetPost(subnetBody);
|
||||
InlineResponse2004 subnetResponse = ipamApi.apiV2IpamSubnetPost(subnetBody);
|
||||
if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
|
||||
String reason = subnetResponse == null ? "Empty response" : "Operation failed on Netris";
|
||||
logger.debug("The Netris IPAM Subnet {} creation failed: {}", subnetName, reason);
|
||||
throw new CloudRuntimeException(reason);
|
||||
}
|
||||
return subnetResponse.getData();
|
||||
} catch (ApiException e) {
|
||||
logAndThrowException(String.format("Error creating Netris Subnet %s for VPC %s", vNetCidr, associatedVpc.getName()), e);
|
||||
logAndThrowException(String.format("Error creating Netris IPAM Subnet %s for VPC %s", subnetPrefix, vpc.getName()), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,19 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.service;
|
||||
|
||||
import com.amazonaws.util.CollectionUtils;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.VlanVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.VlanDao;
|
||||
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;
|
||||
|
|
@ -36,7 +40,11 @@ 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.BaseResponse;
|
||||
import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
|
||||
import org.apache.cloudstack.api.command.DeleteNetrisProviderCmd;
|
||||
|
|
@ -44,6 +52,10 @@ import org.apache.cloudstack.api.command.ListNetrisProvidersCmd;
|
|||
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;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
|
@ -53,9 +65,12 @@ 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 {
|
||||
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
||||
@Inject
|
||||
DataCenterDao dataCenterDao;
|
||||
@Inject
|
||||
|
|
@ -68,6 +83,10 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
|
|||
PhysicalNetworkDao physicalNetworkDao;
|
||||
@Inject
|
||||
NetworkDao networkDao;
|
||||
@Inject
|
||||
private IPAddressDao ipAddressDao;
|
||||
@Inject
|
||||
private VlanDao vlanDao;
|
||||
|
||||
@Override
|
||||
public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
|
||||
|
|
@ -123,12 +142,77 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
|
|||
} else {
|
||||
throw new CloudRuntimeException("Failed to add Netris controller due to internal error.");
|
||||
}
|
||||
createNetrisPublicIpRangesOnNetrisProvider(zoneId, netrisResource);
|
||||
} 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);
|
||||
}
|
||||
if (vlanDbIds.size() > 1) {
|
||||
logger.warn("Expected one Netris Public IP range but found {}. Using the first one to create the Netris IPAM allocation and NAT subnet", vlanDbIds.size());
|
||||
}
|
||||
VlanVO vlanRecord = vlanDao.findById(vlanDbIds.get(0));
|
||||
if (vlanRecord == null) {
|
||||
logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
|
||||
return;
|
||||
}
|
||||
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<>();
|
||||
|
|
@ -158,7 +242,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
|
|||
List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZone(zoneId);
|
||||
for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
|
||||
List<NetworkVO> networkList = networkDao.listByPhysicalNetwork(physicalNetwork.getId());
|
||||
if (!CollectionUtils.isNullOrEmpty(networkList)) {
|
||||
if (!CollectionUtils.isEmpty(networkList)) {
|
||||
validateNetworkState(networkList);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.service;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NetrisProviderServiceImplTest {
|
||||
|
||||
private NetrisProviderServiceImpl service = new NetrisProviderServiceImpl();
|
||||
|
||||
@Test
|
||||
public void testCalculateSubnetCidrFromIpRange() {
|
||||
String ipRange = "100.99.18.140-100.99.18.159";
|
||||
String expectedCidr = "100.99.18.128/27";
|
||||
String cidr = service.calculateSubnetCidrFromIpRange(ipRange);
|
||||
Assert.assertEquals(expectedCidr, cidr);
|
||||
}
|
||||
}
|
||||
|
|
@ -4450,7 +4450,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
if (network.getIsolationMethods().contains("GRE")) {
|
||||
minVnet = MIN_GRE_KEY;
|
||||
maxVnet = MAX_GRE_KEY;
|
||||
} else if (network.getIsolationMethods().contains("VXLAN")) {
|
||||
} else if (network.getIsolationMethods().contains("VXLAN") || network.getIsolationMethods().contains("Netris")) {
|
||||
minVnet = MIN_VXLAN_VNI;
|
||||
maxVnet = MAX_VXLAN_VNI;
|
||||
// fail if zone already contains VNI, need to be unique per zone.
|
||||
|
|
|
|||
|
|
@ -1772,7 +1772,7 @@
|
|||
"label.public.ips": "Public IP addresses",
|
||||
"label.public.lb": "Public LB",
|
||||
"label.public.traffic": "Public traffic",
|
||||
"label.public.traffic.netris": "Netris Public traffic",
|
||||
"label.public.traffic.netris": "Netris Public IP Pool",
|
||||
"label.public.traffic.nsx": "NSX Public traffic",
|
||||
"label.publicinterface": "Public interface",
|
||||
"label.publicip": "IP address",
|
||||
|
|
|
|||
Loading…
Reference in New Issue