Add support to delete VNets and Subnets (#13)

* Add support to delete VNets and Subnets

* Add support to delete vnet resources

* Add support to delete vnet resources

* extract code to method

---------

Co-authored-by: nvazquez <nicovazquez90@gmail.com>
This commit is contained in:
Pearl Dsilva 2024-10-23 10:01:35 -04:00 committed by GitHub
parent 78efab46d5
commit 0f913b48a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 237 additions and 8 deletions

View File

@ -22,4 +22,5 @@ public interface NetrisService {
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);
boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
}

View File

@ -0,0 +1,42 @@
// 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 DeleteNetrisVnetCommand extends NetrisCommand {
private String vpcName;
private Long vpcId;
private final String vNetCidr;
public DeleteNetrisVnetCommand(long zoneId, long accountId, long domainId, String name, long id, String vpcName, Long vpcId, String vNetCidr, boolean isVpc) {
super(zoneId, accountId, domainId, name, id, isVpc);
this.vpcName = vpcName;
this.vpcId = vpcId;
this.vNetCidr = vNetCidr;
}
public String getVpcName() {
return vpcName;
}
public Long getVpcId() {
return vpcId;
}
public String getVNetCidr() {
return vNetCidr;
}
}

View File

@ -30,6 +30,7 @@ import com.cloud.resource.ServerResource;
import com.cloud.utils.exception.CloudRuntimeException;
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.NetrisAnswer;
import org.apache.cloudstack.StartupNetrisCommand;
@ -91,6 +92,8 @@ public class NetrisResource implements ServerResource {
return executeRequest((DeleteNetrisVpcCommand) cmd);
} else if (cmd instanceof CreateNetrisVnetCommand) {
return executeRequest((CreateNetrisVnetCommand) cmd);
} else if (cmd instanceof DeleteNetrisVnetCommand) {
return executeRequest((DeleteNetrisVnetCommand) cmd);
} else {
return Answer.createUnsupportedCommandAnswer(cmd);
}
@ -224,6 +227,21 @@ public class NetrisResource implements ServerResource {
}
}
private Answer executeRequest(DeleteNetrisVnetCommand cmd) {
try {
String networkName = cmd.getName();
boolean result = netrisApiClient.deleteVnet(cmd);
if (!result) {
return new NetrisAnswer(cmd, false, String.format("Netris vNet: %s deletion failed", networkName));
}
return new NetrisAnswer(cmd, true, "OK");
} catch (CloudRuntimeException e) {
String msg = String.format("Error deleting Netris vNet: %s", e.getMessage());
logger.error(msg, e);
return new NetrisAnswer(cmd, new CloudRuntimeException(msg));
}
}
private Answer executeRequest(DeleteNetrisVpcCommand cmd) {
boolean result = netrisApiClient.deleteVpc(cmd);
if (!result) {

View File

@ -46,9 +46,11 @@ public class NetrisResourceObjectUtils {
}
break;
case IPAM_ALLOCATION:
case IPAM_SUBNET:
stringBuilder.append(String.format("%s%s", prefix, objectId));
break;
case IPAM_SUBNET:
stringBuilder.append(String.format("-N%s", objectId));
break;
case VNET:
break;
default:

View File

@ -22,6 +22,7 @@ import io.netris.model.VPCListing;
import io.netris.model.response.TenantResponse;
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 java.util.List;
@ -47,4 +48,6 @@ public interface NetrisApiClient {
boolean deleteVpc(DeleteNetrisVpcCommand cmd);
boolean createVnet(CreateNetrisVnetCommand cmd);
boolean deleteVnet(DeleteNetrisVnetCommand cmd);
}

View File

@ -29,12 +29,16 @@ import io.netris.api.v2.VNetApi;
import io.netris.api.v2.VpcApi;
import io.netris.model.AllocationBody;
import io.netris.model.AllocationBodyVpc;
import io.netris.model.FilterBySites;
import io.netris.model.FilterByVpc;
import io.netris.model.GetSiteBody;
import io.netris.model.InlineResponse2004;
import io.netris.model.IpTreeAllocationTenant;
import io.netris.model.IpTreeSubnet;
import io.netris.model.IpTreeSubnetSites;
import io.netris.model.SitesResponseOK;
import io.netris.model.SubnetBody;
import io.netris.model.SubnetResBody;
import io.netris.model.VPCAdminTenant;
import io.netris.model.VPCCreate;
import io.netris.model.VPCListing;
@ -49,11 +53,15 @@ import io.netris.model.VnetAddBodyDhcpOptionSet;
import io.netris.model.VnetAddBodyGateways;
import io.netris.model.VnetAddBodyVpc;
import io.netris.model.VnetResAddBody;
import io.netris.model.VnetResDeleteBody;
import io.netris.model.VnetResListBody;
import io.netris.model.VnetsBody;
import io.netris.model.response.AuthResponse;
import io.netris.model.response.TenantResponse;
import io.netris.model.response.TenantsResponse;
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.resource.NetrisResourceObjectUtils;
import org.apache.commons.collections.CollectionUtils;
@ -63,6 +71,7 @@ import org.apache.logging.log4j.Logger;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class NetrisApiClientImpl implements NetrisApiClient {
@ -238,6 +247,10 @@ public class NetrisApiClientImpl implements NetrisApiClient {
VpcApi vpcApi = apiClient.getApiStubForMethod(VpcApi.class);
VPCResponseResourceOK vpcResourcesResponse = vpcApi.apiV2VpcVpcIdResourcesGet(vpcResource.getId());
VPCResourceIpam vpcAllocationResource = getVpcAllocationResource(vpcResourcesResponse);
if (Objects.isNull(vpcAllocationResource)) {
logger.info("No VPC IPAM Allocation found for VPC {}", vpcCidr);
return;
}
IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
logger.debug("Removing the IPAM allocation {} with ID {}", vpcAllocationResource.getName(), vpcAllocationResource.getId());
ipamApi.apiV2IpamTypeIdDelete("allocation", vpcAllocationResource.getId());
@ -304,11 +317,11 @@ public class NetrisApiClientImpl implements NetrisApiClient {
String vnetCidr = cmd.getCidr();
boolean isVpc = cmd.isVpc();
String suffix = String.format("%s-%s", vpcId, vpcName);
String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
VPCListing associatedVpc = getVpcByNameAndTenant(netrisVpcName);
if (associatedVpc == null) {
logger.error(String.format("Failed to find Netris VPC with name: %s, to create the corresponding vNet for network %s", vpcName, networkName));
logger.error("Failed to find Netris VPC with name: {}, to create the corresponding vNet for network {}", netrisVpcName, networkName);
return false;
}
@ -324,20 +337,102 @@ public class NetrisApiClientImpl implements NetrisApiClient {
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 VPC {} for network {} creation failed: {}", vnetCidr, vpcName, networkName, reason);
logger.debug("The Netris Subnet {} for network {} creation failed: {}", vnetCidr, networkName, reason);
return false;
}
logger.debug("Successfully created VPC {} and its IPAM Subnet {} on Netris", vpcName, vnetCidr);
logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr);
if (vnetResponse == null || !vnetResponse.isIsSuccess()) {
String reason = vnetResponse == null ? "Empty response" : "Operation failed on Netris";
logger.debug("The Netris vNet creation {} for VPC {} failed: {}", vNetName, vpcName, reason);
logger.debug("The Netris vNet creation {} failed: {}", vNetName, reason);
return false;
}
return true;
}
@Override
public boolean deleteVnet(DeleteNetrisVnetCommand cmd) {
String vpcName = cmd.getVpcName();
Long vpcId = cmd.getVpcId();
String networkName = cmd.getName();
Long networkId = cmd.getId();
boolean isVpc = cmd.isVpc();
String vnetCidr = cmd.getVNetCidr();
try {
String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
VPCListing associatedVpc = getVpcByNameAndTenant(netrisVpcName);
if (associatedVpc == null) {
logger.error("Failed to find Netris VPC with name: {}, to create the corresponding vNet for network {}", netrisVpcName, networkName);
return false;
}
String vNetName;
if (isVpc) {
vNetName = String.format("V%s-N%s-%s", vpcId, networkId, networkName);
} else {
vNetName = String.format("N%s-%s", networkId, networkName);
}
String netrisVnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VNET, vNetName) ;
String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
FilterByVpc vpcFilter = new FilterByVpc();
vpcFilter.add(associatedVpc.getId());
FilterBySites siteFilter = new FilterBySites();
siteFilter.add(siteId);
deleteVnetInternal(associatedVpc, siteFilter, vpcFilter, netrisVnetName, vNetName);
logger.debug("Successfully deleted vNet {}", vNetName);
deleteSubnetInternal(vpcFilter, netrisVnetName, netrisSubnetName);
} catch (Exception e) {
throw new CloudRuntimeException(String.format("Failed to delete Netris vNet %s", networkName), e);
}
return true;
}
private void deleteVnetInternal(VPCListing associatedVpc, FilterBySites siteFilter, FilterByVpc vpcFilter, String netrisVnetName, String vNetName) {
try {
VNetApi vNetApi = apiClient.getApiStubForMethod(VNetApi.class);
VnetResListBody vnetList = vNetApi.apiV2VnetGet(siteFilter, vpcFilter);
if (vnetList == null || !vnetList.isIsSuccess()) {
throw new CloudRuntimeException(String.format("Failed to list vNets for the given VPC: %s and site: %s", associatedVpc.getName(), siteName));
}
List<VnetsBody> vnetsList = vnetList.getData().stream().filter(vnet -> vnet.getName().equals(netrisVnetName)).collect(Collectors.toList());
if (CollectionUtils.isEmpty(vnetsList)) {
logger.debug("vNet: {} for the given VPC: {} appears to already be deleted on Netris", vNetName, associatedVpc.getName());
return;
}
VnetsBody vnetsBody = vnetsList.get(0);
VnetResDeleteBody deleteVnetResponse = vNetApi.apiV2VnetIdDelete(vnetsBody.getId().intValue());
if (deleteVnetResponse == null || !deleteVnetResponse.isIsSuccess()) {
throw new CloudRuntimeException(String.format("Failed to delete vNet: %s", vNetName));
}
} catch (ApiException e) {
logAndThrowException(String.format("Failed to delete vNet: %s", netrisVnetName), e);
}
}
private void deleteSubnetInternal(FilterByVpc vpcFilter, String netrisVnetName, String netrisSubnetName) {
try {
logger.debug("Deleting Netris VPC IPAM Subnet {} for vNet: {}", netrisSubnetName, netrisVnetName);
IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
SubnetResBody subnetsResponse = ipamApi.apiV2IpamSubnetsGet(vpcFilter);
List<IpTreeSubnet> subnets = subnetsResponse.getData();
List<IpTreeSubnet> matchedSubnets = subnets.stream().filter(subnet -> subnet.getName().equals(netrisSubnetName)).collect(Collectors.toList());
if (CollectionUtils.isEmpty(matchedSubnets)) {
logger.debug("IPAM subnet: {} for the given vNet: {} appears to already be deleted on Netris", netrisSubnetName, netrisVnetName);
return;
}
ipamApi.apiV2IpamTypeIdDelete("subnet", matchedSubnets.get(0).getId().intValue());
} catch (ApiException e) {
logAndThrowException(String.format("Failed to delete vNet: %s", netrisVnetName), e);
}
}
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);
try {
@ -427,4 +522,13 @@ public class NetrisApiClientImpl implements NetrisApiClient {
return null;
}
}
private String getNetrisVpcNameSuffix(Long vpcId, String vpcName, Long networkId, String networkName, boolean isVpc) {
String suffix = null;
if (isVpc) {
suffix = String.format("%s-%s", vpcId, vpcName);
} else {
suffix = String.format("%s-%s", networkId, networkName);
}
}
}

View File

@ -23,7 +23,11 @@ import com.cloud.agent.api.AgentControlCommand;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.StartupCommand;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.deploy.DeployDestination;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.ConnectionException;
import com.cloud.exception.InsufficientCapacityException;
@ -35,8 +39,11 @@ import com.cloud.network.IpAddress;
import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
import com.cloud.network.PhysicalNetworkServiceProvider;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.element.DhcpServiceProvider;
import com.cloud.network.element.DnsServiceProvider;
import com.cloud.network.element.NetworkACLServiceProvider;
import com.cloud.network.element.VirtualRouterElement;
import com.cloud.network.element.VpcProvider;
import com.cloud.network.netris.NetrisService;
@ -45,12 +52,17 @@ import com.cloud.network.vpc.NetworkACLItem;
import com.cloud.network.vpc.PrivateGateway;
import com.cloud.network.vpc.StaticRouteProfile;
import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.offering.NetworkOffering;
import com.cloud.resource.ResourceManager;
import com.cloud.resource.ResourceStateAdapter;
import com.cloud.resource.ServerResource;
import com.cloud.resource.UnableDeleteHostException;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.component.AdapterBase;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.NicProfile;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.VirtualMachineProfile;
@ -64,11 +76,12 @@ import javax.naming.ConfigurationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@Component
public class NetrisElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, ResourceStateAdapter,
Listener, VpcProvider {
public class NetrisElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider,
NetworkACLServiceProvider, ResourceStateAdapter, Listener {
@Inject
NetworkModel networkModel;
@ -78,6 +91,16 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
ResourceManager resourceManager;
@Inject
private NetrisService netrisService;
@Inject
private AccountManager accountManager;
@Inject
private DataCenterDao dataCenterDao;
@Inject
private NetworkDao networkDao;
@Inject
private DomainDao domainDao;
@Inject
private VpcDao vpcDao;
protected Logger logger = LogManager.getLogger(getClass());
@ -268,6 +291,24 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
@Override
public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
Account account = accountManager.getAccount(network.getAccountId());
NetworkVO networkVO = networkDao.findById(network.getId());
DataCenterVO zone = dataCenterDao.findById(network.getDataCenterId());
DomainVO domain = domainDao.findById(account.getDomainId());
if (Objects.isNull(zone)) {
String msg = String.format("Cannot find zone with ID %s", network.getDataCenterId());
logger.error(msg);
throw new CloudRuntimeException(msg);
}
String vpcName = null;
Long vpcId = network.getVpcId();
if (Objects.nonNull(vpcId)) {
VpcVO vpc = vpcDao.findById(vpcId);
if (Objects.nonNull(vpc)) {
vpcName = vpc.getName();
}
}
netrisService.deleteVnetResource(zone.getId(), account.getId(), domain.getId(), vpcName, vpcId, networkVO.getName(), network.getId(), network.getCidr());
return true;
}
@ -347,4 +388,14 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
return true;
}
@Override
public boolean applyNetworkACLs(Network config, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException {
return true;
}
@Override
public boolean reorderAclRules(Vpc vpc, List<? extends Network> networks, List<? extends NetworkACLItem> networkACLItems) {
return false;
}
}

View File

@ -25,6 +25,7 @@ import com.cloud.network.netris.NetrisService;
import com.cloud.network.vpc.Vpc;
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.NetrisAnswer;
import org.apache.cloudstack.agent.api.NetrisCommand;
@ -94,4 +95,11 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
return answer.getResult();
}
@Override
public boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
DeleteNetrisVnetCommand cmd = new DeleteNetrisVnetCommand(zoneId, accountId, domainId, networkName, networkId, vpcName, vpcId, cidr, Objects.nonNull(vpcName));
NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
return answer.getResult();
}
}