sureshanaparti commented on code in PR #10458:
URL: https://github.com/apache/cloudstack/pull/10458#discussion_r2222826604


##########
plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java:
##########
@@ -0,0 +1,2004 @@
+// 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 com.cloud.domain.Domain;
+import com.cloud.network.netris.NetrisLbBackend;
+import com.cloud.network.netris.NetrisNetworkRule;
+import com.cloud.network.vpc.StaticRoute;
+import com.cloud.network.vpc.StaticRouteVO;
+import com.cloud.user.Account;
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
+import inet.ipaddr.IPAddress;
+import inet.ipaddr.IPAddressString;
+import io.netris.ApiClient;
+import io.netris.ApiException;
+import io.netris.ApiResponse;
+import io.netris.api.v1.AclApi;
+import io.netris.api.v1.AuthenticationApi;
+import io.netris.api.v1.RoutesApi;
+import io.netris.api.v1.SitesApi;
+import io.netris.api.v1.TenantsApi;
+import io.netris.api.v2.IpamApi;
+import io.netris.api.v2.L4LoadBalancerApi;
+import io.netris.api.v2.NatApi;
+import io.netris.api.v2.VNetApi;
+import io.netris.api.v2.VpcApi;
+import io.netris.model.AclAddItem;
+import io.netris.model.AclBodyVpc;
+import io.netris.model.AclDeleteItem;
+import io.netris.model.AclEditItem;
+import io.netris.model.AclGetBody;
+import io.netris.model.AclResponseGetOk;
+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.InlineResponse20015;
+import io.netris.model.InlineResponse20016;
+import io.netris.model.InlineResponse2003;
+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;
+import io.netris.model.L4LBSite;
+import io.netris.model.L4LbAddItem;
+import io.netris.model.L4LbEditItem;
+import io.netris.model.L4LbTenant;
+import io.netris.model.L4LbVpc;
+import io.netris.model.L4LoadBalancerBackendItem;
+import io.netris.model.L4LoadBalancerItem;
+import io.netris.model.L4lbAddOrUpdateItem;
+import io.netris.model.L4lbresBody;
+import io.netris.model.NatBodySiteSite;
+import io.netris.model.NatBodyVpcVpc;
+import io.netris.model.NatGetBody;
+import io.netris.model.NatPostBody;
+import io.netris.model.NatPutBody;
+import io.netris.model.NatResponseGetOk;
+import io.netris.model.ResAddEditBody;
+import io.netris.model.RoutesBody;
+import io.netris.model.RoutesBodyId;
+import io.netris.model.RoutesBodyVpcVpc;
+import io.netris.model.RoutesGetBody;
+import io.netris.model.RoutesPostBody;
+import io.netris.model.RoutesPutBody;
+import io.netris.model.RoutesResponseGetOk;
+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;
+import io.netris.model.VPCResource;
+import io.netris.model.VPCResourceIpam;
+import io.netris.model.VPCResponseOK;
+import io.netris.model.VPCResponseObjectOK;
+import io.netris.model.VPCResponseResourceOK;
+import io.netris.model.VnetAddBody;
+import io.netris.model.VnetAddBodyDhcp;
+import io.netris.model.VnetAddBodyDhcpOptionSet;
+import io.netris.model.VnetAddBodyGateways;
+import io.netris.model.VnetAddBodyVpc;
+import io.netris.model.VnetEditBody;
+import io.netris.model.VnetEditBodyDhcp;
+import io.netris.model.VnetEditBodyGateways;
+import io.netris.model.VnetResAddBody;
+import io.netris.model.VnetResDeleteBody;
+import io.netris.model.VnetResListBody;
+import io.netris.model.VnetsBody;
+import io.netris.model.VpcEditResponseOK;
+import io.netris.model.VpcVpcIdBody;
+import io.netris.model.response.AuthResponse;
+import io.netris.model.response.L4LbEditResponse;
+import io.netris.model.response.TenantResponse;
+import io.netris.model.response.TenantsResponse;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisACLCommand;
+import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
+import 
org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisACLCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisLoadBalancerRuleCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisStaticRouteCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.ListNetrisStaticRoutesCommand;
+import org.apache.cloudstack.agent.api.NetrisCommand;
+import org.apache.cloudstack.agent.api.ReleaseNatIpCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
+import org.apache.cloudstack.agent.api.UpdateNetrisVnetCommand;
+import org.apache.cloudstack.agent.api.UpdateNetrisVpcCommand;
+import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+public class NetrisApiClientImpl implements NetrisApiClient {
+
+    private final Logger logger = LogManager.getLogger(getClass());
+    private static final String ANY_IP = "0.0.0.0/0";
+    private static final String[] PROTOCOL_LIST = new String[]{"TCP", "UDP", 
"ICMP", "ALL"};
+
+    protected ApiClient apiClient;
+
+    protected final int siteId;
+    private final String siteName;
+    protected final int tenantId;
+    private final String tenantName;
+
+    public NetrisApiClientImpl(String endpointBaseUrl, String username, String 
password, String siteName, String adminTenantName) {
+        try {
+            apiClient = new ApiClient(endpointBaseUrl, username, password, 1L);
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error creating the Netris API 
Client for %s", endpointBaseUrl), e);
+        }
+        Pair<Integer, String> sitePair = getNetrisSitePair(siteName);
+        Pair<Integer, String> tenantPair = 
getNetrisTenantPair(adminTenantName);
+        this.siteId = sitePair.first();
+        this.siteName = sitePair.second();
+        this.tenantId = tenantPair.first();
+        this.tenantName = tenantPair.second();
+    }
+
+    private Pair<Integer, String> getNetrisSitePair(String siteName) {
+        List<GetSiteBody> sites = listSites();
+        if (CollectionUtils.isEmpty(sites)) {
+            throw new CloudRuntimeException("There are no Netris sites, please 
check the Netris endpoint");
+        }
+        List<GetSiteBody> filteredSites = sites.stream().filter(x -> 
x.getName().equals(siteName)).collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(filteredSites)) {
+            throw new CloudRuntimeException(String.format("Cannot find a site 
matching name %s on Netris, please check the Netris endpoint", siteName));
+        }
+        return new Pair<>(filteredSites.get(0).getId(), siteName);
+    }
+
+    private Pair<Integer, String> getNetrisTenantPair(String adminTenantName) {
+        List<TenantResponse> tenants = listTenants();
+        if (CollectionUtils.isEmpty(tenants)) {
+            throw new CloudRuntimeException("There are no Netris tenants, 
please check the Netris endpoint");
+        }
+        List<TenantResponse> filteredTenants = tenants.stream().filter(x -> 
x.getName().equals(adminTenantName)).collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(filteredTenants)) {
+            throw new CloudRuntimeException(String.format("Cannot find a site 
matching name %s on Netris, please check the Netris endpoint", 
adminTenantName));
+        }
+        return new Pair<>(filteredTenants.get(0).getId().intValue(), 
adminTenantName);
+    }
+
+    protected void logAndThrowException(String prefix, ApiException e) throws 
CloudRuntimeException {
+        String msg = String.format("%s: (%s, %s, %s)", prefix, e.getCode(), 
e.getMessage(), e.getResponseBody());
+        logger.error(msg, e);
+        throw new CloudRuntimeException(msg);
+    }
+
+    @Override
+    public boolean isSessionAlive() {
+        ApiResponse<AuthResponse> response = null;
+        try {
+            AuthenticationApi api = 
apiClient.getApiStubForMethod(AuthenticationApi.class);
+            response = api.apiAuthGet();
+        } catch (ApiException e) {
+            logAndThrowException("Error checking the Netris API session is 
alive", e);
+        }
+        return response != null && response.getStatusCode() == 200;
+    }
+
+    @Override
+    public List<GetSiteBody> listSites() {
+        SitesResponseOK response = null;
+        try {
+            SitesApi api = apiClient.getApiStubForMethod(SitesApi.class);
+            response = api.apiSitesGet();
+        } catch (ApiException e) {
+            logAndThrowException("Error listing Netris Sites", e);
+        }
+        return response != null ? response.getData() : null;
+    }
+
+    @Override
+    public List<VPCListing> listVPCs() {
+        VPCResponseOK response = null;
+        try {
+            VpcApi api = apiClient.getApiStubForMethod(VpcApi.class);
+            response = api.apiV2VpcGet();
+        } catch (ApiException e) {
+            logAndThrowException("Error listing Netris VPCs", e);
+        }
+        return response != null ? response.getData() : null;
+    }
+
+    @Override
+    public List<TenantResponse> listTenants() {
+        ApiResponse<TenantsResponse> response = null;
+        try {
+            TenantsApi api = apiClient.getApiStubForMethod(TenantsApi.class);
+            response = api.apiTenantsGet();
+        } catch (ApiException e) {
+            logAndThrowException("Error listing Netris Tenants", e);
+        }
+        return (response != null && response.getData() != null) ? 
response.getData().getData() : null;
+    }
+
+    private VPCResponseObjectOK createVpcInternal(String vpcName, int 
adminTenantId, String adminTenantName) {
+        VPCResponseObjectOK response;
+        logger.debug(String.format("Creating Netris VPC %s", vpcName));
+        try {
+            VpcApi vpcApi = apiClient.getApiStubForMethod(VpcApi.class);
+            VPCCreate body = new VPCCreate();
+            body.setName(vpcName);
+            VPCAdminTenant vpcAdminTenant = new VPCAdminTenant();
+            vpcAdminTenant.setId(adminTenantId);
+            vpcAdminTenant.name(adminTenantName);
+            body.setAdminTenant(vpcAdminTenant);
+            response = vpcApi.apiV2VpcPost(body);
+        } catch (ApiException e) {
+            logAndThrowException("Error creating Netris VPC", e);
+            return null;
+        }
+        return response;
+    }
+
+    private VpcEditResponseOK updateVpcInternal(String vpcName, String 
prevVpcName, int adminTenantId, String adminTenantName) {
+        VpcEditResponseOK response;
+        logger.debug(String.format("Updating Netris VPC name from %s to %s", 
prevVpcName, vpcName));
+        try {
+            VPCListing vpcResource = getVpcByNameAndTenant(prevVpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", prevVpcName, tenantId);
+                return null;
+            }
+            VpcApi vpcApi = apiClient.getApiStubForMethod(VpcApi.class);
+            VpcVpcIdBody body = new VpcVpcIdBody();
+            body.setName(vpcName);
+            VPCAdminTenant vpcAdminTenant = new VPCAdminTenant();
+            vpcAdminTenant.setId(adminTenantId);
+            vpcAdminTenant.name(adminTenantName);
+            body.setAdminTenant(vpcAdminTenant);
+            response = vpcApi.apiV2VpcVpcIdPut(body, vpcResource.getId());
+        } catch (ApiException e) {
+            logAndThrowException("Error updating Netris VPC", e);
+            return null;
+        }
+        return response;
+    }
+
+    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(vpc.getId());
+            allocationBodyVpc.setName(vpc.getName());
+            body.setVpc(allocationBodyVpc);
+            body.setName(ipamName);
+            body.setPrefix(ipamPrefix);
+            IpTreeAllocationTenant allocationTenant = new 
IpTreeAllocationTenant();
+            allocationTenant.setId(new BigDecimal(tenantId));
+            allocationTenant.setName(tenantName);
+            body.setTenant(allocationTenant);
+            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 IPAM 
Allocation %s for VPC %s", ipamPrefix, vpc.getName()), e);
+            return null;
+        }
+    }
+
+    @Override
+    public boolean createVpc(CreateNetrisVpcCommand cmd) {
+        String netrisVpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC);
+        VPCResponseObjectOK createdVpc = createVpcInternal(netrisVpcName, 
tenantId, tenantName);
+        if (createdVpc == null || !createdVpc.isIsSuccess()) {
+            String reason = createdVpc == null ? "Empty response" : "Operation 
failed on Netris";
+            logger.debug("The Netris VPC {} creation failed: {}", 
cmd.getName(), reason);
+            return false;
+        }
+
+        String netrisIpamAllocationName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, cmd.getCidr());
+        String vpcCidr = cmd.getCidr();
+        InlineResponse2004Data createdIpamAllocation = 
createIpamAllocationInternal(netrisIpamAllocationName, vpcCidr, 
createdVpc.getData());
+        return createdIpamAllocation != null;
+    }
+
+    @Override
+    public boolean updateVpc(UpdateNetrisVpcCommand cmd) {
+        Long domainId = cmd.getDomainId();
+        Long zoneId = cmd.getZoneId();
+        Long accountId = cmd.getAccountId();
+        Long vpcId = cmd.getId();
+        String prevVpcName = cmd.getPreviousVpcName();
+        String netrisVpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC);
+        String netrisPrevVpcName = String.format("D%s-A%s-Z%s-V%s-%s", 
domainId, accountId, zoneId, vpcId, prevVpcName);
+        VpcEditResponseOK updatedVpc = updateVpcInternal(netrisVpcName, 
netrisPrevVpcName, tenantId, tenantName);
+        if (updatedVpc == null || !updatedVpc.isIsSuccess()) {
+            String reason = updatedVpc == null ? "Empty response" : "Operation 
failed on Netris";
+            logger.debug("The update of Netris VPC {} failed: {}", 
cmd.getPreviousVpcName(), reason);
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public boolean deleteNatRule(DeleteNetrisNatRuleCommand cmd) {
+        try {
+            String suffix = getNetrisVpcNameSuffix(cmd.getVpcId(), 
cmd.getVpcName(), cmd.getId(), cmd.getName(), cmd.isVpc());
+            String vpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
+            VPCListing vpcResource = getVpcByNameAndTenant(vpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", vpcName, tenantId);
+                return false;
+            }
+            String natRuleName = cmd.getNatRuleName();
+            NatGetBody existingNatRule = netrisNatRuleExists(natRuleName);
+            boolean ruleExists = Objects.nonNull(existingNatRule);
+            if (ruleExists) {
+                deleteNatRule(natRuleName, existingNatRule.getId(), 
vpcResource.getName());
+                if (cmd.getNatRuleType().equals("STATICNAT")) {
+                    String natIp = cmd.getNatIp();
+                    String netrisSubnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, 
String.valueOf(cmd.getVpcId()), natIp);
+                    deleteNatSubnet(netrisSubnetName, vpcResource.getId(), 
natIp);
+                }
+            }
+        } catch (Exception e) {
+            throw new CloudRuntimeException("Error deleting Netris NAT Rule", 
e);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean addOrUpdateAclRule(CreateOrUpdateNetrisACLCommand cmd, 
boolean forLb) {
+        String aclName = cmd.getNetrisAclName();
+        try {
+            AclApi aclApi = apiClient.getApiStubForMethod(AclApi.class);
+            VPCListing vpcResource;
+            String netrisVpcName;
+            if (forLb) {
+                vpcResource = getSystemVpc();
+                netrisVpcName = vpcResource.getName();
+
+            } else {
+                netrisVpcName = getNetrisVpcName(cmd, cmd.getVpcId(), 
cmd.getVpcName());
+                vpcResource = getNetrisVpcResource(netrisVpcName);
+                if (Objects.isNull(vpcResource)) {
+                    return false;
+                }
+            }
+            AclBodyVpc vpc = new AclBodyVpc().id(vpcResource.getId());
+            List<String> aclNames = List.of(aclName);
+            Pair<Boolean, List<BigDecimal>> resultAndMatchingAclIds = 
getMatchingAclIds(aclNames, netrisVpcName);
+            List<BigDecimal> aclIdList = resultAndMatchingAclIds.second();
+            if (!aclIdList.isEmpty()) {
+                logger.debug("Netris ACL rule: {} already exists, updating 
it...", aclName);
+                AclEditItem aclEditItem = getAclEditItem(cmd, aclName, 
aclIdList.get(0));
+                aclEditItem.setVpc(vpc);
+                try {
+                    aclApi.apiAclPut(aclEditItem);
+                } catch (ApiException e) {
+                    if (e.getResponseBody().contains("This kind of acl already 
exists")) {
+                        logger.info("Netris ACL rule: {} already exists and 
doesn't need to be updated", aclName);
+                        return true;
+                    }
+                    throw new CloudRuntimeException("Error updating Netris ACL 
rule", e);
+                }
+                return true;
+            }
+            AclAddItem aclAddItem = getAclAddItem(cmd, aclName);
+            aclAddItem.setVpc(vpc);
+            aclApi.apiAclPost(aclAddItem);
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to create Netris ACL: 
%s", cmd.getNetrisAclName()), e);
+        }
+        return true;
+    }
+
+    AclAddItem getAclAddItem(CreateOrUpdateNetrisACLCommand cmd, String 
aclName) throws ApiException {
+        AclAddItem aclAddItem = new AclAddItem();
+        aclAddItem.setAction(cmd.getAction());
+        aclAddItem.setComment(String.format("ACL rule: %s. %s", 
cmd.getNetrisAclName(), cmd.getReason()));
+        aclAddItem.setName(aclName);
+        String protocol = cmd.getProtocol();
+        if ("TCP".equals(protocol)) {
+            aclAddItem.setEstablished(new BigDecimal(1));
+        } else {
+            aclAddItem.setReverse("yes");
+        }
+        if (!Arrays.asList(PROTOCOL_LIST).contains(protocol)) {
+            aclAddItem.setProto("ip");
+            aclAddItem.setSrcPortTo(cmd.getIcmpType());
+            // TODO: set proto number: where should the protocol number be set 
- API sets the protocol number to Src-from & to and Dest-from & to fields
+        } else if ("ICMP".equals(protocol)) {
+            aclAddItem.setProto("icmp");
+            if (cmd.getIcmpType() != -1) {
+                aclAddItem.setIcmpType(cmd.getIcmpType());
+            }
+        } else {
+            aclAddItem.setProto(protocol.toLowerCase(Locale.ROOT));
+        }
+
+        aclAddItem.setDstPortFrom(cmd.getDestPortStart());
+        aclAddItem.setDstPortTo(cmd.getDestPortEnd());
+        aclAddItem.setDstPrefix(cmd.getDestPrefix());
+        aclAddItem.setSrcPrefix(cmd.getSourcePrefix());
+        aclAddItem.setSrcPortFrom(1);
+        aclAddItem.setSrcPortTo(65535);
+        if (NatPutBody.ProtocolEnum.ICMP.name().equalsIgnoreCase(protocol)) {
+            aclAddItem.setIcmpType(cmd.getIcmpType());
+        }
+
+        return aclAddItem;
+    }
+
+    AclEditItem getAclEditItem(CreateOrUpdateNetrisACLCommand cmd, String 
aclName, BigDecimal aclId) throws ApiException {
+        AclEditItem aclEditItem = new AclEditItem();
+        aclEditItem.setId(aclId);
+        aclEditItem.setAction(cmd.getAction());
+        aclEditItem.setComment(String.format("ACL rule: %s. %s", 
cmd.getNetrisAclName(), cmd.getReason()));
+        aclEditItem.setName(aclName);
+        String protocol = cmd.getProtocol();
+        if ("TCP".equals(protocol)) {
+            aclEditItem.setEstablished(new BigDecimal(1));
+        } else {
+            aclEditItem.setReverse("yes");
+        }
+        if (!Arrays.asList(PROTOCOL_LIST).contains(protocol)) {
+            aclEditItem.setProto("ip");
+            aclEditItem.setSrcPortTo(cmd.getIcmpType());
+            // TODO: set proto number: where should the protocol number be set 
- API sets the protocol number to Src-from & to and Dest-from & to fields
+        } else if ("ICMP".equals(protocol)) {
+            aclEditItem.setProto("icmp");
+            if (cmd.getIcmpType() != -1) {
+                aclEditItem.setIcmpType(cmd.getIcmpType());
+            }
+        } else {
+            aclEditItem.setProto(protocol.toLowerCase(Locale.ROOT));
+        }
+
+        aclEditItem.setDstPortFrom(cmd.getDestPortStart());
+        aclEditItem.setDstPortTo(cmd.getDestPortEnd());
+        aclEditItem.setDstPrefix(cmd.getDestPrefix());
+        aclEditItem.setSrcPrefix(cmd.getSourcePrefix());
+        aclEditItem.setSrcPortFrom(1);
+        aclEditItem.setSrcPortTo(65535);
+        if (NatPutBody.ProtocolEnum.ICMP.name().equalsIgnoreCase(protocol)) {
+            aclEditItem.setIcmpType(cmd.getIcmpType());
+        }
+
+        return aclEditItem;
+    }
+
+    @Override
+    public boolean deleteAclRule(DeleteNetrisACLCommand cmd, boolean forLb) {
+        List<String> aclNames = cmd.getAclRuleNames();
+        try {
+            AclApi aclApi = apiClient.getApiStubForMethod(AclApi.class);
+
+            String vpcName;
+            if (!forLb) {
+                String suffix = getNetrisVpcNameSuffix(cmd.getVpcId(), 
cmd.getVpcName(), cmd.getId(), cmd.getName(), cmd.isVpc());
+                vpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
+            } else {
+                VPCListing vpcResource = getSystemVpc();
+                vpcName = vpcResource.getName();
+            }
+            Pair<Boolean, List<BigDecimal>> resultAndMatchingAclIds = 
getMatchingAclIds(aclNames, vpcName);
+            Boolean result = resultAndMatchingAclIds.first();
+            List<BigDecimal> matchingAclIds = resultAndMatchingAclIds.second();
+            if (!result) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", vpcName, tenantId);
+                return false;
+            }
+            if (matchingAclIds.isEmpty()) {
+                logger.warn("There doesn't seem to be any ACLs on Netris 
matching {}", aclNames.size() > 1 ? String.join(",", aclNames) : aclNames);
+                return true;
+            }
+            AclDeleteItem aclDeleteItem = new AclDeleteItem();
+            aclDeleteItem.setId(matchingAclIds);
+            aclDeleteItem.setTenantsID(String.valueOf(tenantId));
+            aclApi.apiAclDelete(aclDeleteItem);
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to delete Netris ACLs: 
%s", String.join(",", cmd.getAclRuleNames())), e);
+        }
+        return true;
+    }
+
+    Pair<Boolean, List<BigDecimal>> getMatchingAclIds(List<String> aclNames, 
String vpcName) {
+        try {
+            AclApi aclApi = apiClient.getApiStubForMethod(AclApi.class);
+            VPCListing vpcResource = getVpcByNameAndTenant(vpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", vpcName, tenantId);
+                return new Pair<>(false, Collections.emptyList());
+            }
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(vpcResource.getId());
+            FilterBySites siteFilter = new FilterBySites();
+            siteFilter.add(siteId);
+            AclResponseGetOk aclGetResponse = aclApi.apiAclGet(siteFilter, 
vpcFilter);
+            if (aclGetResponse == null || !aclGetResponse.isIsSuccess()) {
+                logger.warn("No ACLs were found to be present for the specific 
Netris VPC resource {}." +
+                        " Netris ACLs may have been deleted out of band.", 
vpcName);
+                return new Pair<>(true, Collections.emptyList());
+            }
+            List<AclGetBody> aclList = aclGetResponse.getData();
+            return new Pair<>(true, aclList.stream()
+                    .filter(acl -> aclNames.contains(acl.getName()))
+                    .map(acl -> BigDecimal.valueOf(acl.getId()))
+                    .collect(Collectors.toList()));
+        } catch (ApiException e) {
+            logAndThrowException("Failed to retrieve Netris ACLs", e);
+        }
+        return new Pair<>(true, Collections.emptyList());
+    }
+
+    public boolean addOrUpdateStaticRoute(AddOrUpdateNetrisStaticRouteCommand 
cmd) {
+        String prefix = cmd.getPrefix();
+        String nextHop = cmd.getNextHop();
+        Long vpcId = cmd.getId();
+        String vpcName = cmd.getName();
+        boolean updateRoute = cmd.isUpdateRoute();
+        try {
+            String vpcSuffix = getNetrisVpcNameSuffix(vpcId, vpcName, null, 
null, true);
+            String netrisVpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, vpcSuffix);
+            VPCListing vpcResource = getVpcByNameAndTenant(netrisVpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", netrisVpcName, tenantId);
+                return false;
+            }
+
+            String[] suffixes = new String[2];
+            suffixes[0] = vpcId.toString();
+            suffixes[1] = cmd.getRouteId().toString();
+            String staticRouteId = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, suffixes);
+
+            Pair<Boolean, RoutesGetBody> existingStaticRoute = 
staticRouteExists(vpcResource.getId(), prefix, null, staticRouteId);
+            if (updateRoute) {
+                if (!existingStaticRoute.first()) {
+                    logger.error("The Netris static route {} does not exist 
for VPC {}, adding it", prefix, netrisVpcName);
+                    return addStaticRouteInternal(vpcResource, netrisVpcName, 
prefix, nextHop, staticRouteId);
+                }
+                return 
updateStaticRouteInternal(existingStaticRoute.second().getId(), netrisVpcName, 
prefix, nextHop, staticRouteId);
+            } else {
+                if (existingStaticRoute.first()) {
+                    String existingNextHop = 
existingStaticRoute.second().getNextHop();
+                    if (existingNextHop != null && 
existingNextHop.equals(nextHop)) {
+                        logger.debug("The Netris static route {} already 
exists for VPC {}", prefix, netrisVpcName);
+                        return true;
+                    } else {
+                        logger.debug("The Netris static route {} already 
exists but has different next hop {} for VPC {}", prefix, nextHop, 
netrisVpcName);
+                        return false;
+                    }
+                }
+                return addStaticRouteInternal(vpcResource, netrisVpcName, 
prefix, nextHop, staticRouteId);
+            }
+        } catch (Exception e) {
+            throw new CloudRuntimeException("Error adding Netris static 
route", e);
+        }
+    }
+
+    private boolean addStaticRouteInternal(VPCListing vpcResource, String 
netrisVpcName, String prefix, String nextHop, String staticRouteId) {
+        try {
+            RoutesApi routesApi = 
apiClient.getApiStubForMethod(RoutesApi.class);
+            RoutesPostBody routesPostBody = new RoutesPostBody();
+            routesPostBody.setPrefix(prefix);
+            routesPostBody.setNextHop(nextHop);
+            routesPostBody.setSiteId(new BigDecimal(siteId));
+            routesPostBody.setStateStatus(RoutesBody.StateStatusEnum.ACTIVE);
+
+            RoutesBodyVpcVpc vpcBody = new RoutesBodyVpcVpc();
+            vpcBody.setId(vpcResource.getId());
+            vpcBody.setName(vpcResource.getName());
+            vpcBody.setIsDefault(vpcResource.isIsDefault());
+            vpcBody.setIsSystem(vpcResource.isIsSystem());
+            routesPostBody.setVpc(vpcBody);
+
+            routesPostBody.setDescription(staticRouteId);
+            routesPostBody.setStateStatus(RoutesBody.StateStatusEnum.ACTIVE);
+            routesPostBody.setSwitches(Collections.emptyList());
+
+            InlineResponse2004 routeResponse = 
routesApi.apiRoutesPost(routesPostBody);
+            if (routeResponse == null || !routeResponse.isIsSuccess()) {
+                String reason = routeResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("The Netris static route creation failed for 
netris VPC - {}: {}", netrisVpcName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+        } catch (ApiException e) {
+            logAndThrowException("Error adding Netris static route", e);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean updateStaticRouteInternal(Integer id, String 
netrisVpcName, String prefix, String nextHop, String staticRouteId) {
+        try {
+            RoutesApi routesApi = 
apiClient.getApiStubForMethod(RoutesApi.class);
+            RoutesPutBody routesPutBody = new RoutesPutBody();
+            routesPutBody.setId(id);
+            routesPutBody.setPrefix(prefix);
+            routesPutBody.setNextHop(nextHop);
+            routesPutBody.setSiteId(new BigDecimal(siteId));
+            routesPutBody.setStateStatus(RoutesPutBody.StateStatusEnum.ACTIVE);
+
+            routesPutBody.setDescription(staticRouteId);
+            routesPutBody.setSwitches(Collections.emptyList());
+
+            InlineResponse2003 routeResponse = 
routesApi.apiRoutesPut(routesPutBody);
+            if (routeResponse == null || !routeResponse.isIsSuccess()) {
+                String reason = routeResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("Failed to update Netris static route for netris 
VPC - {}: {}", netrisVpcName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+        } catch (ApiException e) {
+            logAndThrowException("Error updating Netris static route", e);
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public boolean deleteStaticRoute(DeleteNetrisStaticRouteCommand cmd) {
+        Long vpcId = cmd.getId();
+        String vpcName = cmd.getName();
+        String prefix = cmd.getPrefix();
+        String nextHop = cmd.getNextHop();
+        try {
+            String vpcSuffix = getNetrisVpcNameSuffix(vpcId, vpcName, null, 
null, true);
+            String netrisVpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, vpcSuffix);
+            VPCListing vpcResource = getVpcByNameAndTenant(netrisVpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", netrisVpcName, tenantId);
+                return false;
+            }
+
+            String[] suffixes = new String[2];
+            suffixes[0] = vpcId.toString();
+            suffixes[1] = cmd.getRouteId().toString();
+            String staticRouteId = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, suffixes);
+            Pair<Boolean, RoutesGetBody> existingStaticRoute = 
staticRouteExists(vpcResource.getId(), prefix, nextHop, staticRouteId);
+
+            if (Boolean.FALSE.equals(existingStaticRoute.first())) {
+                logger.debug("The Netris static route {} does not exist for 
VPC {}", prefix, netrisVpcName);
+                return true;
+            }
+            RoutesGetBody existingRoute = existingStaticRoute.second();
+            RoutesApi routesApi = 
apiClient.getApiStubForMethod(RoutesApi.class);
+            RoutesBodyId id = new RoutesBodyId();
+            id.setId(existingRoute.getId());
+            InlineResponse2003 routeDeleteResponse = 
routesApi.apiRoutesDelete(id);
+            if (routeDeleteResponse == null || 
!routeDeleteResponse.isIsSuccess()) {
+                String reason = routeDeleteResponse == null ? "Empty response" 
: "Operation failed on Netris";
+                logger.debug("The Netris static route deletion failed for 
netris VPC - {}: {}", netrisVpcName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+            return true;
+        } catch (ApiException e) {
+            logAndThrowException("Error deleting Netris static route", e);
+        }
+        return false;
+    }
+
+    @Override
+    public List<StaticRoute> listStaticRoutes(ListNetrisStaticRoutesCommand 
cmd) {
+        Long vpcId = cmd.getId();
+        String vpcName = cmd.getName();
+        String prefix = cmd.getPrefix();
+        String nextHop = cmd.getNextHop();
+        String vpcSuffix = getNetrisVpcNameSuffix(vpcId, vpcName, null, null, 
true);
+        String netrisVpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, vpcSuffix);
+        VPCListing vpcResource = getVpcByNameAndTenant(netrisVpcName);
+        if (vpcResource == null) {
+            logger.error("Could not find the Netris VPC resource with name {} 
and tenant ID {}", netrisVpcName, tenantId);
+            return new ArrayList<>();
+        }
+
+        List<RoutesGetBody> staticRoutes = 
listStaticRoutes(vpcResource.getId(), prefix, nextHop);
+        if (CollectionUtils.isEmpty(staticRoutes)) {
+            return new ArrayList<>();
+        }
+        List<StaticRoute> result = new ArrayList<>();
+        for (RoutesGetBody staticRoute : staticRoutes) {
+            // All static routes belong the SYSTEM account, which does not 
matter
+            result.add(new StaticRouteVO(null, staticRoute.getName(), vpcId, 
Account.ACCOUNT_ID_SYSTEM, Domain.ROOT_DOMAIN, staticRoute.getNextHop()));
+        }
+        return result;
+    }
+
+    @Override
+    public boolean releaseNatIp(ReleaseNatIpCommand cmd) {
+        String natIp = cmd.getNatIp() + "/32";
+        try {
+            VPCListing systemVpc = getSystemVpc();
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(systemVpc.getId());
+            SubnetResBody subnetResponse = 
ipamApi.apiV2IpamSubnetsGet(vpcFilter);
+            if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
+                String reason = subnetResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("Failed to retrieve Netris Public NAT IPs due to 
{}", reason);
+                throw new CloudRuntimeException(reason);
+            }
+            List<IpTreeSubnet> natIps = 
subnetResponse.getData().stream().filter(ip -> 
ip.getPrefix().equals(natIp)).collect(Collectors.toList());
+            if (!natIps.isEmpty()) {
+                ipamApi.apiV2IpamTypeIdDelete("subnet", 
natIps.get(0).getId().intValue());
+            }
+
+        } catch (ApiException e) {
+            logAndThrowException("Failed to release Netris IP", e);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean 
createOrUpdateLbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd) {
+        boolean isVpc = cmd.isVpc();
+        Long networkResourceId = cmd.getId();
+        String networkResourceName = cmd.getName();
+        Long domainId = cmd.getDomainId();
+        Long accountId = cmd.getAccountId();
+        Long zoneId = cmd.getZoneId();
+        Long lbId = cmd.getLbId();
+        String publicIp = cmd.getPublicIp();
+        List<NetrisLbBackend> lbBackends = cmd.getLbBackends();
+
+        try {
+            String resourcePrefix = isVpc ? "V" : "N";
+            String netrisResourceName = String.format("D%s-A%s-Z%s-%s%s-%s", 
domainId, accountId, zoneId, resourcePrefix, networkResourceId, 
networkResourceName);
+            VPCListing vpcResource = getNetrisVpcResource(netrisResourceName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", netrisResourceName, tenantId);
+                return false;
+            }
+            createLBSubnet(cmd, publicIp + "/32", vpcResource.getId());
+
+            String suffix = String.format("LB%s", lbId);
+            String lbName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.LB, suffix);
+            Pair<Boolean, List<BigDecimal>> resultAndMatchingLbId = 
getMatchingLbRule(lbName, vpcResource.getName());
+            Boolean result = resultAndMatchingLbId.first();
+            List<BigDecimal> matchingLbId = resultAndMatchingLbId.second();
+            if (Boolean.FALSE.equals(result)) {
+                logger.warn("Could not find the Netris LB rule with name {}", 
lbName);
+            }
+            boolean updateRule = !matchingLbId.isEmpty();
+            L4lbAddOrUpdateItem l4lbAddItem = getL4LbRule(cmd, vpcResource, 
lbName, publicIp, lbBackends, updateRule);
+            L4LoadBalancerApi loadBalancerApi = 
apiClient.getApiStubForMethod(L4LoadBalancerApi.class);
+            boolean success;
+            L4LbEditResponse editResponse = null;
+            ResAddEditBody createResponse = null;
+            if (updateRule) {
+                editResponse = loadBalancerApi.apiV2L4lbIdPut((L4LbEditItem) 
l4lbAddItem, matchingLbId.get(0).intValue());
+                success = editResponse.isIsSuccess();
+            } else {
+                createResponse = loadBalancerApi.apiV2L4lbPost((L4LbAddItem) 
l4lbAddItem);
+                success = createResponse.isIsSuccess();
+            }
+            if (ObjectUtils.allNull(editResponse, createResponse) || 
Boolean.FALSE.equals(success)) {
+                throw new CloudRuntimeException(String.format("Failed to %s 
Netris LB rule", updateRule ? "update" : "create"));
+            }
+            if (Objects.nonNull(cmd.getCidrList()) && 
!cmd.getCidrList().isEmpty()) {
+                applyAclRulesForLb(cmd, lbName);
+            }
+        } catch (ApiException e) {
+            logAndThrowException("Failed to create Netris load balancer rule", 
e);
+        }
+        return true;
+    }
+
+    private void 
applyAclRulesForLb(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd, String 
lbName) {
+        // Add deny all rule first
+        addOrUpdateAclRule(createNetrisACLRuleCommand(cmd, lbName, "ANY",
+                
NetrisNetworkRule.NetrisRuleAction.DENY.name().toLowerCase(Locale.ROOT), 0), 
true);
+        AtomicInteger cidrIndex = new AtomicInteger(1);
+        for (String cidr : cmd.getCidrList().split(" ")) {
+            try {
+                addOrUpdateAclRule(createNetrisACLRuleCommand(cmd, lbName, 
cidr,
+                        
NetrisNetworkRule.NetrisRuleAction.PERMIT.name().toLowerCase(Locale.ROOT),
+                        cidrIndex.getAndIncrement()), true);
+            } catch (Exception e) {
+                throw new CloudRuntimeException(String.format("Failed to add 
Netris ACL rule for LB CIDR %s", cidr), e);
+            }
+        }
+    }
+
+    private CreateOrUpdateNetrisACLCommand 
createNetrisACLRuleCommand(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd, 
String netrisLbName, String cidr, String action, int index) {
+        Long zoneId = cmd.getZoneId();
+        Long accountId = cmd.getAccountId();
+        Long domainId = cmd.getDomainId();
+        String networkName = null;
+        Long networkId = null;
+        String vpcName = null;
+        Long vpcId = null;
+        boolean isVpc = cmd.isVpc();
+        if (isVpc) {
+            vpcId = cmd.getId();
+            vpcName = cmd.getName();
+        } else {
+            networkName = cmd.getName();
+            networkId = cmd.getId();
+        }
+        String destinationPrefix = cmd.getPublicIp() + "/32";
+        String srcPort = cmd.getPublicPort();
+        String dstPort = cmd.getPublicPort();
+        CreateOrUpdateNetrisACLCommand aclCommand = new 
CreateOrUpdateNetrisACLCommand(zoneId, accountId, domainId, networkName, 
networkId,
+                vpcName, vpcId, Objects.nonNull(vpcId), action, 
NetrisServiceImpl.getPrefix(cidr), 
NetrisServiceImpl.getPrefix(destinationPrefix),
+                Integer.parseInt(srcPort), Integer.parseInt(dstPort), 
cmd.getProtocol());
+        String aclName;
+        if (isVpc) {
+            aclName =  String.format("V%s-LBACL%s-%s", vpcId, index, 
cmd.getRuleName());
+        } else {
+            aclName = String.format("N%s-LBACL%s-%s", networkId, index, 
cmd.getRuleName());
+        }
+        String netrisAclName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.ACL, aclName);
+        aclCommand.setNetrisAclName(netrisAclName);
+        aclCommand.setReason(String.format("ACL Rule for CIDR %s of LB %s ", 
aclName, netrisLbName));
+        return aclCommand;
+    }
+
+    private L4lbAddOrUpdateItem 
getL4LbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd, VPCListing 
vpcResource, String lbName,
+                                            String publicIp, 
List<NetrisLbBackend> lbBackends, boolean updateRule) {
+        L4lbAddOrUpdateItem l4lbAddItem = updateRule ? new L4LbEditItem() : 
new L4LbAddItem();
+        try {
+            l4lbAddItem.setName(lbName);
+
+            String protocol = cmd.getProtocol().toUpperCase(Locale.ROOT);
+            if (!Arrays.asList("TCP", "UDP").contains(protocol)) {
+                throw new CloudRuntimeException("Invalid protocol " + 
protocol);
+            }
+            
l4lbAddItem.setProtocol(cmd.getProtocol().toUpperCase(Locale.ROOT));
+            L4LBSite site = new L4LBSite();
+            site.setId(siteId);
+            site.setName(siteName);
+            l4lbAddItem.setSite(site);
+            l4lbAddItem.setSiteID(new BigDecimal(siteId));
+
+            L4LbTenant tenant = new L4LbTenant();
+            tenant.setId(tenantId);
+            tenant.setName(tenantName);
+            l4lbAddItem.setTenant(tenant);
+
+            L4LbVpc vpc = new L4LbVpc();
+            vpc.setId(vpcResource.getId());
+            l4lbAddItem.setVpc(vpc);
+
+            l4lbAddItem.setAutomatic(false);
+            l4lbAddItem.setIpFamily(NetUtils.isIpv4(publicIp) ? 
L4lbAddOrUpdateItem.IpFamilyEnum.IPv4 : L4lbAddOrUpdateItem.IpFamilyEnum.IPv6);
+            l4lbAddItem.setIp(publicIp);
+            l4lbAddItem.setStatus("enable");
+
+            List<L4LoadBalancerBackendItem> backends = new ArrayList<>();
+            for (NetrisLbBackend backend : lbBackends) {
+                L4LoadBalancerBackendItem backendItem = new 
L4LoadBalancerBackendItem();
+                backendItem.setIp(backend.getVmIp());
+                backendItem.setPort(backend.getPort());
+                backends.add(backendItem);
+            }
+            l4lbAddItem.setBackend(backends);
+            l4lbAddItem.setPort(Integer.valueOf(cmd.getPublicPort()));
+            
l4lbAddItem.setHealthCheck(L4lbAddOrUpdateItem.HealthCheckEnum.NONE);
+        } catch (Exception e) {
+            throw new CloudRuntimeException("Failed to create Netris load 
balancer rule", e);
+        }
+        return  l4lbAddItem;
+    }
+
+    @Override
+    public boolean deleteLbRule(DeleteNetrisLoadBalancerRuleCommand cmd) {
+        boolean isVpc = cmd.isVpc();
+        String vpcName = null;
+        String networkName = null;
+        Long vpcId = null;
+        Long networkId = null;
+        if (isVpc) {
+            vpcName = cmd.getName();
+            vpcId = cmd.getId();
+        } else {
+            networkName = cmd.getName();
+            networkId = cmd.getId();
+        }
+        Long lbId = cmd.getLbId();
+        String cidrList = cmd.getCidrList();
+        try {
+            String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, 
networkName, isVpc);
+            String netrisVpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
+            suffix = String.format("LB%s", lbId);
+            String lbName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.LB, suffix);
+            Pair<Boolean, List<BigDecimal>> resultAndMatchingLbId = 
getMatchingLbRule(lbName, netrisVpcName);
+            Boolean result = resultAndMatchingLbId.first();
+            List<BigDecimal> matchingLbId = resultAndMatchingLbId.second();
+            if (!result) {
+                logger.error("Could not find the Netris LB rule with name {}", 
lbName);
+                return false;
+            }
+            if (matchingLbId.isEmpty()) {
+                logger.warn("There doesn't seem to be any LB rule on Netris 
matching {}", lbName);
+                return true;
+            }
+
+            L4LoadBalancerApi lbApi = 
apiClient.getApiStubForMethod(L4LoadBalancerApi.class);
+            lbApi.apiV2L4lbIdDelete(matchingLbId.get(0).intValue());
+            if (Objects.nonNull(cidrList)) {
+                deleteAclRulesForLb(cmd);
+            }
+        } catch (ApiException e) {
+            logAndThrowException("Failed to delete Netris load balancer rule", 
e);
+        }
+        return true;
+    }
+
+    private void deleteAclRulesForLb(DeleteNetrisLoadBalancerRuleCommand cmd) {
+        // delete the deny rule
+        deleteAclRule(deleteNetrisACLCommand(cmd, 0), true);
+        AtomicInteger cidrIndex = new AtomicInteger(1);
+        for (String cidr : cmd.getCidrList().split(" ")) {
+            try {
+                deleteAclRule(deleteNetrisACLCommand(cmd, 
cidrIndex.getAndIncrement()), true);
+            } catch (Exception e) {
+                throw new CloudRuntimeException(String.format("Failed to 
delete Netris ACL rule for LB CIDR %s", cidr), e);
+            }
+        }
+    }
+
+    private DeleteNetrisACLCommand 
deleteNetrisACLCommand(DeleteNetrisLoadBalancerRuleCommand cmd, int index) {
+        Long zoneId = cmd.getZoneId();
+        Long accountId = cmd.getAccountId();
+        Long domainId = cmd.getDomainId();
+        String networkName = null;
+        Long networkId = null;
+        String vpcName = null;
+        Long vpcId = null;
+        boolean isVpc = cmd.isVpc();
+        if (isVpc) {
+            vpcId = cmd.getId();
+            vpcName = cmd.getName();
+        } else {
+            networkName = cmd.getName();
+            networkId = cmd.getId();
+        }
+        DeleteNetrisACLCommand deleteAclCommand = new 
DeleteNetrisACLCommand(zoneId, accountId, domainId, networkName, networkId, 
isVpc, vpcId, vpcName);
+        String aclName;
+        if (isVpc) {
+            aclName =  String.format("V%s-LBACL%s-%s", vpcId, index, 
cmd.getRuleName());
+        } else {
+            aclName = String.format("N%s-LBACL%s-%s", networkId, index, 
cmd.getRuleName());
+        }
+        String netrisAclName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.ACL, aclName);
+        
deleteAclCommand.setAclRuleNames(Collections.singletonList(netrisAclName));
+        return deleteAclCommand;
+    }
+
+    private Pair<Boolean, List<BigDecimal>> getMatchingLbRule(String lbName, 
String vpcName) {
+        try {
+            VPCListing vpcResource = getVpcByNameAndTenant(vpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", vpcName, tenantId);
+                return new Pair<>(false, Collections.emptyList());
+            }
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(vpcResource.getId());
+            FilterBySites siteFilter = new FilterBySites();
+            siteFilter.add(siteId);
+            L4LoadBalancerApi lbApi = 
apiClient.getApiStubForMethod(L4LoadBalancerApi.class);
+            L4lbresBody lbGetResponse = lbApi.apiV2L4lbGet(siteFilter, 
vpcFilter);
+            if (lbGetResponse == null || !lbGetResponse.isIsSuccess()) {
+                logger.warn("No LB rules were found to be present for the 
specific Netris VPC resource {}." +
+                        " Netris LB rules may have been deleted out of band.", 
vpcName);
+                return new Pair<>(true, Collections.emptyList());
+            }
+            List<L4LoadBalancerItem> lbList = lbGetResponse.getData();
+            return new Pair<>(true, lbList.stream()
+                    .filter(lb -> lbName.equals(lb.getName()))
+                    .map(acl -> BigDecimal.valueOf(acl.getId()))
+                    .collect(Collectors.toList()));
+        } catch (ApiException e) {
+            logAndThrowException("Failed to retrieve Netris LB rules", e);
+        }
+        return new Pair<>(true, Collections.emptyList());
+    }
+
+    private List<RoutesGetBody> listStaticRoutes(Integer netrisVpcId, String 
prefix, String nextHop) {
+        try {
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(netrisVpcId);
+            FilterBySites sitesFilter = new FilterBySites();
+            sitesFilter.add(siteId);
+            RoutesApi routesApi = 
apiClient.getApiStubForMethod(RoutesApi.class);
+            RoutesResponseGetOk routesResponseGetOk = 
routesApi.apiRoutesGet(sitesFilter, vpcFilter);
+            if (Objects.isNull(routesResponseGetOk) || 
Boolean.FALSE.equals(routesResponseGetOk.isIsSuccess())) {
+                logger.warn("Failed to retrieve static routes");
+                return null;
+            }
+            List<RoutesGetBody> routesList = routesResponseGetOk.getData();
+            return routesList.stream()
+                    .filter(x -> (Objects.isNull(prefix) || 
x.getName().equals(prefix)) &&
+                            (Objects.isNull(nextHop) || 
x.getNextHop().equals(nextHop)))
+                    .collect(Collectors.toList());
+        } catch (ApiException e) {
+            logAndThrowException("Error listing Netris static routes", e);
+        }
+        return null;
+    }
+
+    private Pair<Boolean, RoutesGetBody> staticRouteExists(Integer 
netrisVpcId, String prefix, String nextHop, String description) {
+        List<RoutesGetBody> staticRoutes = listStaticRoutes(netrisVpcId, 
prefix, nextHop);
+        if (staticRoutes == null) {
+            return new Pair<>(false, null);
+        }
+        return new Pair<>(!staticRoutes.isEmpty(), staticRoutes.isEmpty() ? 
null : staticRoutes.get(0));
+    }
+
+    public void deleteNatRule(String natRuleName, Integer snatRuleId, String 
netrisVpcName) {
+        logger.debug("Deleting NAT rule on Netris: {} for VPC {}", 
natRuleName, netrisVpcName);
+        try {
+            NatApi natApi = apiClient.getApiStubForMethod(NatApi.class);
+            natApi.apiV2NatIdDelete(snatRuleId);
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to delete NAT rule: %s 
for VPC: %s", natRuleName, netrisVpcName), e);
+        }
+    }
+
+    private void deleteVpcIpamAllocationInternal(VPCListing vpcResource, 
String allocationName) {
+        logger.debug("Deleting Netris VPC IPAM Allocation {} for VPC {}", 
allocationName, vpcResource.getName());
+        try {
+            VpcApi vpcApi = apiClient.getApiStubForMethod(VpcApi.class);
+            VPCResponseResourceOK vpcResourcesResponse = 
vpcApi.apiV2VpcVpcIdResourcesGet(vpcResource.getId());
+            VPCResourceIpam vpcAllocationResource = 
getVpcAllocationResource(vpcResourcesResponse, allocationName);
+            if (Objects.isNull(vpcAllocationResource)) {
+                logger.info("No VPC IPAM Allocation found for VPC {}", 
allocationName);
+                return;
+            }
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            logger.debug("Removing the IPAM allocation {} with ID {}", 
vpcAllocationResource.getName(), vpcAllocationResource.getId());
+            ipamApi.apiV2IpamTypeIdDelete("allocation", 
vpcAllocationResource.getId());
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error removing IPAM Allocation 
%s for VPC %s", allocationName, vpcResource.getName()), e);
+        }
+    }
+
+    private VPCResourceIpam getVpcAllocationResource(VPCResponseResourceOK 
vpcResourcesResponse, String allocationName) {
+        VPCResource resource = vpcResourcesResponse.getData().get(0);
+        List<VPCResourceIpam> vpcAllocations = resource.getAllocation();
+        if (CollectionUtils.isNotEmpty(vpcAllocations)) {
+            vpcAllocations = vpcAllocations.stream().filter(x -> 
x.getName().equalsIgnoreCase(allocationName)).collect(Collectors.toList());
+            return CollectionUtils.isNotEmpty(vpcAllocations) ? 
vpcAllocations.get(0) : null;
+        }
+        return null;
+    }
+
+    private VPCListing getVpcByNameAndTenant(String vpcName) {
+        try {
+            List<VPCListing> vpcListings = listVPCs();
+            List<VPCListing> vpcs = vpcListings.stream()
+                    .filter(x -> x.getName().equals(vpcName) && 
x.getAdminTenant().getId().equals(tenantId))
+                    .collect(Collectors.toList());
+            return vpcs.isEmpty() ? null : vpcs.get(0);
+        } catch (Exception e) {
+            throw new CloudRuntimeException(String.format("Error getting VPC 
%s information: %s", vpcName, e.getMessage()), e);
+        }
+    }
+
+    private VPCResponseObjectOK deleteVpcInternal(VPCListing vpcResource) {
+        try {
+            VpcApi vpcApi = apiClient.getApiStubForMethod(VpcApi.class);
+            logger.debug("Removing the VPC {} with ID {}", 
vpcResource.getName(), vpcResource.getId());
+            return vpcApi.apiV2VpcVpcIdDelete(vpcResource.getId());
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error deleting VPC %s: %s", 
vpcResource.getName(), e.getResponseBody()), e);
+            return null;
+        }
+    }
+
+    @Override
+    public boolean deleteVpc(DeleteNetrisVpcCommand cmd) {
+        String suffix = String.valueOf(cmd.getId());
+        String vpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC);
+        VPCListing vpcResource = getVpcByNameAndTenant(vpcName);
+        if (vpcResource == null) {
+            logger.error("Could not find the Netris VPC resource with name {} 
and tenant ID {}", vpcName, tenantId);
+            return false;
+        }
+        String snatRuleName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.SNAT, suffix);
+        NatGetBody existingNatRule = netrisNatRuleExists(snatRuleName);
+        boolean ruleExists = Objects.nonNull(existingNatRule);
+        if (ruleExists) {
+            deleteNatRule(snatRuleName, existingNatRule.getId(), 
vpcResource.getName());
+        }
+
+        String vpcAllocationName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, cmd.getCidr());
+        deleteVpcIpamAllocationInternal(vpcResource, vpcAllocationName);
+        VPCResponseObjectOK response = deleteVpcInternal(vpcResource);
+        return response != null && response.isIsSuccess();
+    }
+
+    @Override
+    public boolean createVnet(CreateNetrisVnetCommand cmd) {
+        String vpcName = cmd.getVpcName();
+        Long vpcId = cmd.getVpcId();
+        String networkName = cmd.getName();
+        Long networkId = cmd.getId();
+        String vnetCidr = cmd.getCidr();
+        Integer vxlanId = cmd.getVxlanId();
+        String netrisTag = cmd.getNetrisTag();
+        String netmask = vnetCidr.split("/")[1];
+        String netrisGateway = cmd.getGateway() + "/" + netmask;
+        String netrisV6Cidr = cmd.getIpv6Cidr();
+        boolean isVpc = cmd.isVpc();
+        Boolean isGlobalRouting = cmd.isGlobalRouting();
+
+        try {
+            String netrisVpcName = getNetrisVpcName(cmd, vpcId, vpcName);
+            VPCListing associatedVpc = getNetrisVpcResource(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, 
String.valueOf(cmd.getVpcId()), vnetCidr) ;
+
+            createIpamSubnetInternal(netrisSubnetName, vnetCidr, 
SubnetBody.PurposeEnum.COMMON, associatedVpc, isGlobalRouting);
+            if (Objects.nonNull(netrisV6Cidr)) {
+                String netrisV6IpamAllocationName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, netrisV6Cidr);
+                String netrisV6SubnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET,  
String.valueOf(cmd.getVpcId()), netrisV6Cidr) ;
+                BigDecimal ipamAllocationId = 
getIpamAllocationIdByPrefixAndVpc(netrisV6Cidr, associatedVpc);
+                if (ipamAllocationId == null) {
+                    InlineResponse2004Data createdIpamAllocation = 
createIpamAllocationInternal(netrisV6IpamAllocationName, netrisV6Cidr, 
associatedVpc);
+                    if (Objects.isNull(createdIpamAllocation)) {
+                        throw new CloudRuntimeException(String.format("Failed 
to create Netris IPAM Allocation %s for VPC %s", netrisV6IpamAllocationName, 
netrisVpcName));
+                    }
+                }
+                createIpamSubnetInternal(netrisV6SubnetName, netrisV6Cidr, 
SubnetBody.PurposeEnum.COMMON, associatedVpc, isGlobalRouting);
+            }
+            logger.debug("Successfully created IPAM Subnet {} for network {} 
on Netris", netrisSubnetName, networkName);
+
+            VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, 
netrisVnetName, netrisGateway, netrisV6Cidr, vxlanId, netrisTag);
+            if (vnetResponse == null || !vnetResponse.isIsSuccess()) {
+                String reason = vnetResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("The Netris vNet creation {} failed: {}", 
vNetName, reason);
+                return false;
+            }
+        } catch (ApiException e) {
+            throw new CloudRuntimeException(String.format("Failed to create 
Netris vNet %s", networkName), e);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean updateVnet(UpdateNetrisVnetCommand cmd) {
+        String networkName = cmd.getName();
+        Long networkId = cmd.getId();
+        String prevNetworkName = cmd.getPrevNetworkName();
+        String vpcName = cmd.getVpcName();
+        Long vpcId = cmd.getVpcId();
+        boolean isVpc = cmd.isVpc();
+
+        String netrisVpcName = getNetrisVpcName(cmd, vpcId, vpcName);
+        VPCListing associatedVpc = getNetrisVpcResource(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;
+        String prevVnetName;
+        if (isVpc) {
+            vNetName = String.format("V%s-N%s-%s", vpcId, networkId, 
networkName);
+            prevVnetName = String.format("V%s-N%s-%s", vpcId, networkId, 
prevNetworkName);
+        } else {
+            vNetName = String.format("N%s-%s", networkId, networkName);
+            prevVnetName = String.format("N%s-%s", networkId, prevNetworkName);
+        }
+        String netrisVnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VNET, vNetName) ;
+        String prevNetrisVnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VNET, prevVnetName) ;
+
+        VnetResAddBody response = updateVnetInternal(associatedVpc, 
netrisVnetName, prevNetrisVnetName);
+        if (response == null || !response.isIsSuccess()) {
+            String reason = response == null ? "Empty response" : "Operation 
failed on Netris";
+            logger.debug("Netris vNet: {} update failed: {}", vNetName, 
reason);
+            return false;
+        }
+        return true;
+    }
+
+
+    private VnetResAddBody updateVnetInternal(VPCListing associatedVpc, String 
netrisVnetName, String prevVnetName) {
+        logger.debug("Updating Netris vNet name from {} to {} ", 
netrisVnetName, prevVnetName);
+        try {
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(associatedVpc.getId());
+            FilterBySites siteFilter = new FilterBySites();
+            siteFilter.add(siteId);
+            List<VnetsBody> vnetsList = getVnets(associatedVpc, prevVnetName, 
siteFilter, vpcFilter);
+            if (CollectionUtils.isEmpty(vnetsList)) {
+                String errorMsg = String.format("Could not find vNet with 
name: %s", prevVnetName);
+                logger.error(errorMsg);
+                throw new CloudRuntimeException(errorMsg);
+            }
+            VnetsBody vnetsBody = vnetsList.get(0);
+
+            VnetEditBody vnetBody = new VnetEditBody();
+
+            vnetBody.setCustomAnycastMac(vnetBody.getCustomAnycastMac());
+
+            VnetEditBodyGateways gatewayV4 = new VnetEditBodyGateways();
+            gatewayV4.prefix(vnetsBody.getGateways().get(0).getPrefix());
+            gatewayV4.setDhcpEnabled(false);
+            VnetEditBodyDhcp dhcp = new VnetEditBodyDhcp();
+            dhcp.setEnd("");
+            dhcp.setStart("");
+            dhcp.setOptionSet(new VnetAddBodyDhcpOptionSet());
+            gatewayV4.setDhcp(dhcp);
+            List<VnetEditBodyGateways> gatewaysList = new ArrayList<>();
+            gatewaysList.add(gatewayV4);
+
+
+            if (vnetsBody.getGateways().size() > 1 && 
Objects.nonNull(vnetsBody.getGateways().get(1))) {
+                String netrisV6Gateway = 
vnetsBody.getGateways().get(1).getPrefix();
+                VnetEditBodyGateways gatewayV6 = new VnetEditBodyGateways();
+                gatewayV6.prefix(netrisV6Gateway);
+                gatewayV6.setDhcpEnabled(false);
+                gatewayV6.setDhcp(dhcp);
+                gatewaysList.add(gatewayV6);
+            }
+
+            vnetBody.setGateways(gatewaysList);
+            vnetBody.setGuestTenants(new ArrayList<>());
+            vnetBody.setL3vpn(false);
+            vnetBody.setName(netrisVnetName);
+            vnetBody.setNativeVlan(0);
+            vnetBody.setVxlanID(vnetsBody.getVxlanID());
+            vnetBody.setPorts(new ArrayList<>());
+
+            IpTreeSubnetSites subnetSites = new IpTreeSubnetSites();
+            subnetSites.setId(new BigDecimal(siteId));
+            subnetSites.setName(siteName);
+            List<IpTreeSubnetSites> subnetSitesList = new ArrayList<>();
+            subnetSitesList.add(subnetSites);
+            vnetBody.setSites(subnetSitesList);
+
+            vnetBody.setState(VnetEditBody.StateEnum.ACTIVE);
+
+            vnetBody.setTags(new ArrayList<>());
+
+            IpTreeAllocationTenant allocationTenant = new 
IpTreeAllocationTenant();
+            allocationTenant.setId(new BigDecimal(tenantId));
+            allocationTenant.setName(tenantName);
+            vnetBody.setTenant(allocationTenant);
+
+            vnetBody.setVlan(0);
+            vnetBody.setVlanAware(false);
+            vnetBody.setVlans("");
+
+            VnetAddBodyVpc vpc = new VnetAddBodyVpc();
+            vpc.setName(associatedVpc.getName());
+            vpc.setId(associatedVpc.getId());
+            vnetBody.setVpc(vpc);
+
+            vnetBody.setTags(vnetsBody.getTags());
+
+            VNetApi vnetApi = apiClient.getApiStubForMethod(VNetApi.class);
+            return vnetApi.apiV2VnetIdPut(vnetBody, 
vnetsBody.getId().intValue());
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error creating Netris vNet %s 
for VPC %s", netrisVnetName, associatedVpc.getName()), e);
+            return null;
+        }
+    }
+
+    @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();
+        String vnetV6Cidr = cmd.getvNetV6Cidr();
+        try {
+            String netrisVpcName = getNetrisVpcName(cmd, vpcId, vpcName);
+            VPCListing associatedVpc = getNetrisVpcResource(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, 
String.valueOf(cmd.getVpcId()), 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);
+            if (Objects.nonNull(vnetV6Cidr)) {
+                String netrisV6IpamAllocationName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, vnetV6Cidr);
+                String netrisV6SubnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, 
String.valueOf(cmd.getVpcId()), vnetV6Cidr);
+                deleteSubnetInternal(vpcFilter, netrisVnetName, 
netrisV6SubnetName);
+                deleteVpcIpamAllocationInternal(associatedVpc, 
netrisV6IpamAllocationName);
+            }
+
+        } catch (Exception e) {
+            throw new CloudRuntimeException(String.format("Failed to delete 
Netris vNet %s", networkName), e);
+        }
+        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) || 
isAllocationPartOfBiggerAllocation(x.getPrefix(), superCidrPrefix))
+                .collect(Collectors.toList());
+        return CollectionUtils.isEmpty(superCidrList) ? null : 
superCidrList.get(0).getId();
+    }
+
+    private boolean isAllocationPartOfBiggerAllocation(String 
netrisAllocation, String providedAllocation) {
+        IPAddress biggerAllocation = new 
IPAddressString(netrisAllocation).getAddress();
+        IPAddress smallerAllocation = new 
IPAddressString(providedAllocation).getAddress();
+
+        return biggerAllocation.contains(smallerAllocation);
+
+    }
+
+    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 -> ipamAllocationId != null ?
+                        x.getAllocationID().equals(ipamAllocationId) && 
x.getPrefix().equals(exactCidr) && x.getPurpose() == purpose :
+                        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.COMMON, systemVpc);
+            if (exactSubnet == null) {
+                String ipamSubnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, exactCidr);
+                createIpamSubnetInternal(ipamSubnetName, exactCidr, 
SubnetBody.PurposeEnum.COMMON, systemVpc, null);
+            }
+        } 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 boolean 
createOrUpdateNatRuleInternal(CreateOrUpdateNetrisNatCommand cmd) {
+        String ruleName = cmd.getNatRuleName();
+        long vpcId = cmd.getVpcId();
+        Long networkId = cmd.getId();
+        String networkName = cmd.getName();
+        String vpcName = cmd.getVpcName();
+        String vpcCidr = cmd.getVpcCidr();
+        boolean isVpc = cmd.isVpc();
+        NatPostBody.ActionEnum action = 
getNatActionFromRuleType(cmd.getNatRuleType());
+        NatPostBody.ProtocolEnum protocol = 
getProtocolFromString(cmd.getProtocol());
+        NatPostBody.StateEnum state = getStateFromString(cmd.getState());
+
+        String vNetName = isVpc ?
+                String.format("V%s-N%s-%s", vpcId, networkId, networkName) :
+                String.format("N%s-%s", networkId, networkName);
+        String netrisVpcName = getNetrisVpcName(cmd, vpcId, vpcName);
+        VPCListing vpcResource = getNetrisVpcResource(netrisVpcName);
+        if (vpcResource == null) {
+            logger.error("Could not find the Netris VPC resource with name {} 
and tenant ID {}", netrisVpcName, tenantId);
+            return false;
+        }
+
+        String targetIpSubnet = null;
+        if (NatPostBody.ActionEnum.SNAT == action) {
+            targetIpSubnet = cmd.getNatIp() + "/32";
+        } else if (NatPostBody.ActionEnum.DNAT == action) {
+            targetIpSubnet = cmd.getDestinationAddress() + "/32";
+        }
+
+        if (StringUtils.isNotBlank(targetIpSubnet) && 
existsDestinationSubnet(targetIpSubnet)) {
+            logger.debug("Creating subnet with NAT purpose for {}}", 
targetIpSubnet);
+            createNatSubnet(cmd, targetIpSubnet, vpcResource.getId());
+        }
+
+        NatGetBody existingNatRule = netrisNatRuleExists(ruleName);
+        boolean ruleExists = Objects.nonNull(existingNatRule);
+        if (!ruleExists) {
+            String destinationAddress = action == NatPostBody.ActionEnum.SNAT 
? ANY_IP : cmd.getDestinationAddress() + "/32";
+            String destinationPort = cmd.getDestinationPort();
+            String sourceAddress = action == NatPostBody.ActionEnum.SNAT ? 
vpcCidr : ANY_IP;
+            String sourcePort = "1-65535";
+            String snatToIp = action == NatPostBody.ActionEnum.SNAT ? 
targetIpSubnet : null;
+            String dnatToIp = action == NatPostBody.ActionEnum.DNAT ? 
cmd.getSourceAddress() + "/32" : null;
+            String dnatToPort = action == NatPostBody.ActionEnum.DNAT ? 
cmd.getSourcePort() : null;
+            return createNatRuleInternal(ruleName, action, protocol, state, 
destinationAddress, destinationPort,
+                    sourceAddress, sourcePort, snatToIp, dnatToIp, dnatToPort, 
netrisVpcName, networkName, vNetName);
+        } else if (NatPostBody.ActionEnum.SNAT == action) {
+            return updateSnatRuleInternal(ruleName, targetIpSubnet, 
netrisVpcName, networkName, vNetName, existingNatRule.getId(), vpcCidr);
+        }
+        return true;
+    }
+
+    private NatPostBody.StateEnum getStateFromString(String state) {
+        return NatPostBody.StateEnum.fromValue(state);
+    }
+
+    private NatPostBody.ActionEnum getNatActionFromRuleType(String 
natRuleType) {
+        return NatPostBody.ActionEnum.fromValue(natRuleType);
+    }
+
+    @Override
+    public boolean createOrUpdateSNATRule(CreateOrUpdateNetrisNatCommand cmd) {
+        return createOrUpdateNatRuleInternal(cmd);
+    }
+
+    private boolean existsDestinationSubnet(String destinationSubnet) {
+        try {
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(getSystemVpc().getId());
+            List<IpTreeSubnet> targetSubnetList = getSubnet(vpcFilter, 
destinationSubnet);
+            return targetSubnetList != null;
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error checking if subnet %s 
exists: %s", destinationSubnet, e.getMessage()), e);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean createStaticNatRule(CreateOrUpdateNetrisNatCommand cmd) {
+        String staticNatRuleName = cmd.getNatRuleName();
+        String natIP = cmd.getNatIp() + "/32";
+        String vmIp = cmd.getVmIp() + "/32";
+        String vpcName = cmd.getVpcName();
+        String vpcCidr = cmd.getVpcCidr();
+        Long vpcId = cmd.getVpcId();
+        Long networkId = cmd.getId();
+        String networkName = cmd.getName();
+        boolean isVpc = cmd.isVpc();
+
+        try {
+            String netrisVpcName = getNetrisVpcName(cmd, vpcId, vpcName);
+            VPCListing vpcResource = getNetrisVpcResource(netrisVpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", netrisVpcName, tenantId);
+                return false;
+            }
+            // Create a /32 subnet for the DNAT IP
+            createNatSubnet(cmd, natIP, vpcResource.getId());
+            NatApi natApi = apiClient.getApiStubForMethod(NatApi.class);
+            NatPostBody natBody = new NatPostBody();
+            natBody.setAction(NatPostBody.ActionEnum.DNAT);
+            natBody.setDestinationAddress(natIP);
+            natBody.setName(staticNatRuleName);
+            natBody.setProtocol(NatPostBody.ProtocolEnum.ALL);
+            natBody.setState(NatPostBody.StateEnum.ENABLED);
+            natBody.setComment(String.format("Static NAT rule for %s", 
netrisVpcName));
+
+            NatBodySiteSite site = new NatBodySiteSite();
+            site.setId(siteId);
+            site.setName(siteName);
+            natBody.setSite(site);
+            natBody.setSourceAddress(ANY_IP);
+            natBody.setDnatToIP(vmIp);
+
+            NatBodyVpcVpc vpc = new NatBodyVpcVpc();
+            vpc.setId(vpcResource.getId());
+            vpc.setName(vpcResource.getName());
+            natBody.setVpc(vpc);
+
+            InlineResponse20015 natResponse = natApi.apiV2NatPost(natBody);
+            if (natResponse == null || !natResponse.isIsSuccess()) {
+                String reason = natResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("The Netris static NAT (DNAT) rule creation 
failed for netris VPC - {}: {}", netrisVpcName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to create Static NAT 
(DNAT) rule for network : %s", Objects.nonNull(vpcName) ? vpcName : 
networkName), e);
+        }
+        return true;
+    }
+
+    private void createNatSubnet(NetrisCommand cmd, String natIp, Integer 
netrisVpcId) {
+        try {
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(netrisVpcId);
+            String netrisSubnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, 
String.valueOf(((CreateOrUpdateNetrisNatCommand)cmd).getVpcId()), natIp);
+            List<IpTreeSubnet> matchedSubnets = getSubnet(vpcFilter, 
netrisSubnetName);
+            if (matchedSubnets.isEmpty()) {
+                VPCListing systemVpc = getSystemVpc();
+                createIpamSubnetInternal(netrisSubnetName, natIp, 
SubnetBody.PurposeEnum.NAT, systemVpc, null);
+            } else {
+                IpTreeSubnet existingSubnet = matchedSubnets.stream().filter(x 
-> x.getPrefix().equals(natIp)).collect(Collectors.toList()).get(0);
+                if (existingSubnet.getPurpose() != 
IpTreeSubnet.PurposeEnum.NAT) {
+                    VPCListing systemVpc = getSystemVpc();
+                    logger.debug("Subnet: {} already exists, but purpose is 
not NAT, updating its purpose to 'nat'", natIp);
+                    
updateIpamSubnetInternal(existingSubnet.getId().intValue(), netrisSubnetName, 
natIp, SubnetBody.PurposeEnum.NAT, systemVpc, null);
+                }
+            }
+        } catch (ApiException e) {
+            throw new CloudRuntimeException(String.format("Failed to create 
subnet for %s with NAT purpose", natIp));
+        }
+    }
+
+    private void createLBSubnet(NetrisCommand cmd, String lbIp, Integer 
netrisVpcId) {
+        try {
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(netrisVpcId);
+            String netrisSubnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd,
+                    NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET,
+                    String.valueOf(cmd.getId()), lbIp);
+            List<IpTreeSubnet> matchedSubnets = getSubnet(vpcFilter, 
netrisSubnetName);
+            VPCListing systemVpc = getSystemVpc();
+            if (matchedSubnets.isEmpty()) {
+                createIpamSubnetInternal(netrisSubnetName, lbIp, 
SubnetBody.PurposeEnum.LOAD_BALANCER, systemVpc, null);
+            } else if (IpTreeSubnet.PurposeEnum.LOAD_BALANCER != 
matchedSubnets.get(0).getPurpose()){
+                logger.debug("Updating existing NAT subnet {} to have load 
balancer purpose", netrisSubnetName);
+                
updateIpamSubnetInternal(matchedSubnets.get(0).getId().intValue(), 
netrisSubnetName, lbIp, SubnetBody.PurposeEnum.LOAD_BALANCER, systemVpc, null);
+            }
+            logger.debug("LB subnet: {} already exists", netrisSubnetName);
+        } catch (ApiException e) {
+            throw new CloudRuntimeException(String.format("Failed to create 
subnet for %s with LB purpose", lbIp));
+        }
+    }
+
+    private NatPostBody.ProtocolEnum getProtocolFromString(String protocol) {
+        return NatPostBody.ProtocolEnum.fromValue(protocol);
+    }
+
+    private NatPostBody createNatRulePostBody(String ruleName, 
NatPostBody.ActionEnum action, NatPostBody.ProtocolEnum protocol, 
NatPostBody.StateEnum state,
+                                              String destinationAddress, 
String destinationPort,
+                                              String sourceAddress, String 
sourcePort,
+                                              String dnatToIp, String 
dnatToPort,
+                                              String netrisVpcName, String 
snatIP, String comment) {
+        NatPostBody natBody = new NatPostBody();
+        natBody.setAction(action);
+        natBody.setName(ruleName);
+        natBody.setProtocol(protocol);
+        natBody.setState(state);
+        if (StringUtils.isNotBlank(comment)) {
+            natBody.setComment(comment);
+        }
+
+        natBody.setDestinationAddress(destinationAddress);
+        if (StringUtils.isNotBlank(destinationPort)) {
+            natBody.setDestinationPort(destinationPort);
+        }
+
+        if (StringUtils.isNotBlank(sourceAddress)) {
+            natBody.setSourceAddress(sourceAddress);
+        }
+        if (StringUtils.isNotBlank(sourcePort)) {
+            natBody.setSourcePort(sourcePort);
+        }
+
+        NatBodySiteSite site = new NatBodySiteSite();
+        site.setId(siteId);
+        site.setName(siteName);
+        natBody.setSite(site);
+
+        if (StringUtils.isNotBlank(snatIP)) {
+            natBody.setSnatToIP(snatIP);
+        }
+
+        if (StringUtils.isNotBlank(dnatToIp)) {
+            natBody.setDnatToIP(dnatToIp);
+        }
+        if (StringUtils.isNotBlank(dnatToPort)) {
+            natBody.setDnatToPort(Integer.valueOf(dnatToPort));
+        }
+
+        NatBodyVpcVpc vpc = new NatBodyVpcVpc();
+        VPCListing vpcResource = getVpcByNameAndTenant(netrisVpcName);
+        if (vpcResource == null) {
+            logger.error("Could not find the Netris VPC resource with name {} 
and tenant ID {}", netrisVpcName, tenantId);
+            return null;
+        }
+        vpc.setId(vpcResource.getId());
+        vpc.setName(vpcResource.getName());
+        natBody.setVpc(vpc);
+        return natBody;
+    }
+
+    @Override
+    public boolean createOrUpdateDNATRule(CreateOrUpdateNetrisNatCommand cmd) {
+        return createOrUpdateNatRuleInternal(cmd);
+    }
+
+    private boolean createNatRuleInternal(String ruleName, 
NatPostBody.ActionEnum action, NatPostBody.ProtocolEnum protocol, 
NatPostBody.StateEnum state,
+                                          String destinationAddress, String 
destinationPort, String sourceAddress, String sourcePort,
+                                          String sNatToIp, String dNatToIp, 
String dNatToPort,
+                                          String netrisVpcName, String 
networkName, String vNetName) {
+        try {
+            NatApi natApi = apiClient.getApiStubForMethod(NatApi.class);
+            String comment = String.format("NAT rule for %s with action %s", 
netrisVpcName, action.name());
+            NatPostBody natBody = createNatRulePostBody(ruleName, action, 
protocol, state,
+                    destinationAddress, destinationPort, sourceAddress, 
sourcePort,
+                    dNatToIp, dNatToPort, netrisVpcName, sNatToIp, comment);
+            if (natBody == null) {
+                return false;
+            }
+            InlineResponse20015 natResponse = natApi.apiV2NatPost(natBody);
+            if (natResponse == null || !natResponse.isIsSuccess()) {
+                String reason = natResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("The Netris NAT rule {} creation failed for 
network(vNet) - {}({}): {}", action.name(), networkName, vNetName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to create NAT rule %s 
for network(vNet): %s(%s)", action.name(), networkName, vNetName), e);
+        }
+        return true;
+    }
+
+    private boolean updateSnatRuleInternal(String snatRuleName, String snatIP, 
String netrisVpcName, String networkName,
+                                           String vNetName, Integer 
netisSnatId, String vpcCidr) {
+        try {
+            NatApi natApi = apiClient.getApiStubForMethod(NatApi.class);
+            NatPutBody natBody = new NatPutBody();
+            natBody.setAction(NatPutBody.ActionEnum.SNAT);
+            natBody.setDestinationAddress(ANY_IP);
+            natBody.setName(snatRuleName);
+            natBody.setProtocol(NatPutBody.ProtocolEnum.ALL);
+
+            NatBodySiteSite site = new NatBodySiteSite();
+            site.setId(siteId);
+            site.setName(siteName);
+            natBody.setSite(site);
+            natBody.setSourceAddress(vpcCidr);
+            natBody.setSnatToIP(snatIP);
+
+            NatBodyVpcVpc vpc = new NatBodyVpcVpc();
+            VPCListing vpcResource = getVpcByNameAndTenant(netrisVpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", netrisVpcName, tenantId);
+                return false;
+            }
+            vpc.setId(vpcResource.getId());
+            vpc.setName(vpcResource.getName());
+            natBody.setVpc(vpc);
+
+            InlineResponse20016 natUpdateResponse = 
natApi.apiV2NatIdPut(natBody, netisSnatId);
+            if (natUpdateResponse == null || !natUpdateResponse.isIsSuccess()) 
{
+                String reason = natUpdateResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("Update of Netris SNAT rule failed for 
network(vNet) - {}({}): {}", networkName, vNetName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to create SNAT rule for 
network(vNet): %s(%s)", networkName, vNetName), e);
+        }
+        return true;
+    }
+
+    private List<VnetsBody> getVnets(VPCListing associatedVpc, String 
netrisVnetName, FilterBySites siteFilter, FilterByVpc vpcFilter) {
+        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));
+            }
+            return vnetList.getData().stream().filter(vnet -> 
vnet.getName().equals(netrisVnetName)).collect(Collectors.toList());
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to get vNets: %s", 
netrisVnetName), e);
+        }
+        return Collections.emptyList();
+    }
+
+    private void deleteVnetInternal(VPCListing associatedVpc, FilterBySites 
siteFilter, FilterByVpc vpcFilter, String netrisVnetName, String vNetName) {
+        try {
+            VNetApi vNetApi = apiClient.getApiStubForMethod(VNetApi.class);
+            List<VnetsBody> vnetsList = getVnets(associatedVpc, 
netrisVnetName, siteFilter, vpcFilter);
+            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 List<IpTreeSubnet> getSubnet(FilterByVpc vpcFilter, String 
netrisSubnetName) {
+        try {
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            SubnetResBody subnetsResponse = 
ipamApi.apiV2IpamSubnetsGet(vpcFilter);
+            List<IpTreeSubnet> subnets = subnetsResponse.getData();
+            return subnets.stream().filter(subnet -> 
subnet.getName().equals(netrisSubnetName)).collect(Collectors.toList());
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to get IPAM subnet: 
%s", netrisSubnetName), e);
+        }
+        return new ArrayList<>();
+    }
+
+    private void deleteSubnetInternal(FilterByVpc vpcFilter, String 
netrisVnetName, String netrisSubnetName) {
+        try {
+            String logString = "";
+            if (Objects.nonNull(netrisVnetName)) {
+                logString = String.format("for vNet: %s ", netrisVnetName);
+            }
+            logger.debug("Deleting Netris VPC IPAM Subnet {} {}", 
netrisSubnetName, logString);
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            List<IpTreeSubnet> matchedSubnets = getSubnet(vpcFilter, 
netrisSubnetName);
+            if (CollectionUtils.isEmpty(matchedSubnets)) {
+                logger.debug("IPAM subnet: {} {} appears to already be deleted 
on Netris", netrisSubnetName, logString);
+                return;
+            }
+
+            ipamApi.apiV2IpamTypeIdDelete("subnet", 
matchedSubnets.get(0).getId().intValue());
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to delete subnet: %s", 
netrisSubnetName), e);
+        }
+    }
+
+    private SubnetBody getIpamSubnetBody(VPCListing vpc, 
SubnetBody.PurposeEnum purpose, String subnetName, String subnetPrefix, Boolean 
isGlobalRouting) {
+        SubnetBody subnetBody = new SubnetBody();
+        subnetBody.setName(subnetName);
+
+        AllocationBodyVpc vpcAllocationBody = new AllocationBodyVpc();
+        vpcAllocationBody.setName(vpc.getName());
+        vpcAllocationBody.setId(vpc.getId());
+        subnetBody.setVpc(vpcAllocationBody);
+
+        IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
+        allocationTenant.setId(new BigDecimal(tenantId));
+        allocationTenant.setName(tenantName);
+        subnetBody.setTenant(allocationTenant);
+
+        IpTreeSubnetSites subnetSites = new IpTreeSubnetSites();
+        subnetSites.setId(new BigDecimal(siteId));
+        subnetSites.setName(siteName);
+        subnetBody.setSites(List.of(subnetSites));
+
+        subnetBody.setPurpose(purpose);
+        subnetBody.setPrefix(subnetPrefix);
+        if (isGlobalRouting != null) {
+            subnetBody.setGlobalRouting(isGlobalRouting);
+        }
+        return subnetBody;
+    }
+
+    private InlineResponse2004Data createIpamSubnetInternal(String subnetName, 
String subnetPrefix, SubnetBody.PurposeEnum purpose, VPCListing vpc, Boolean 
isGlobalRouting) {
+        logger.debug("Creating Netris IPAM Subnet {} for VPC {}", 
subnetPrefix, vpc.getName());
+        try {
+
+            SubnetBody subnetBody = getIpamSubnetBody(vpc, purpose, 
subnetName, subnetPrefix, isGlobalRouting);
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            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 IPAM 
Subnet %s for VPC %s", subnetPrefix, vpc.getName()), e);
+            return null;
+        }
+    }
+
+    private InlineResponse2004Data updateIpamSubnetInternal(Integer 
netrisSubnetId, String subnetName, String subnetPrefix,
+                                                            
SubnetBody.PurposeEnum purpose, VPCListing vpc, Boolean isGlobalRouting) {
+        logger.debug("Updating Netris IPAM Subnet {} for VPC {}", 
subnetPrefix, vpc.getName());
+        try {
+
+            SubnetBody subnetBody = getIpamSubnetBody(vpc, purpose, 
subnetName, subnetPrefix, isGlobalRouting);
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            InlineResponse2004 subnetResponse = 
ipamApi.apiV2IpamSubnetIdPut(subnetBody, netrisSubnetId);
+            if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
+                String reason = subnetResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("The Netris IPAM Subnet {} update failed: {}", 
subnetName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+            return subnetResponse.getData();
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error Updating Netris IPAM 
Subnet %s for VPC %s", subnetPrefix, vpc.getName()), e);
+            return null;
+        }
+    }
+
+    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String 
netrisVnetName, String netrisGateway, String netrisV6Cidr, Integer vxlanId, 
String netrisTag) {
+        logger.debug("Creating Netris VPC vNet {} for CIDR {}", 
netrisVnetName, netrisGateway);
+        try {
+            VnetAddBody vnetBody = new VnetAddBody();
+
+            vnetBody.setCustomAnycastMac("");
+
+            VnetAddBodyGateways gatewayV4 = new VnetAddBodyGateways();
+            gatewayV4.prefix(netrisGateway);
+            gatewayV4.setDhcpEnabled(false);
+            VnetAddBodyDhcp dhcp = new VnetAddBodyDhcp();
+            dhcp.setEnd("");
+            dhcp.setStart("");
+            dhcp.setOptionSet(new VnetAddBodyDhcpOptionSet());
+            gatewayV4.setDhcp(dhcp);
+            List<VnetAddBodyGateways> gatewaysList = new ArrayList<>();
+            gatewaysList.add(gatewayV4);
+
+            if (Objects.nonNull(netrisV6Cidr)) {
+                VnetAddBodyGateways gatewayV6 = new VnetAddBodyGateways();
+                gatewayV6.prefix(NetUtils.getIpv6Gateway(netrisV6Cidr));
+                gatewayV6.setDhcpEnabled(false);
+                gatewayV6.setDhcp(dhcp);
+                gatewaysList.add(gatewayV6);
+            }
+
+            vnetBody.setGateways(gatewaysList);
+            vnetBody.setGuestTenants(new ArrayList<>());
+            vnetBody.setL3vpn(false);
+            vnetBody.setName(netrisVnetName);
+            vnetBody.setNativeVlan(0);
+            vnetBody.setVxlanID(vxlanId);
+            vnetBody.setPorts(new ArrayList<>());
+
+            IpTreeSubnetSites subnetSites = new IpTreeSubnetSites();
+            subnetSites.setId(new BigDecimal(siteId));
+            subnetSites.setName(siteName);
+            List<IpTreeSubnetSites> subnetSitesList = new ArrayList<>();
+            subnetSitesList.add(subnetSites);
+            vnetBody.setSites(subnetSitesList);
+
+            vnetBody.setState(VnetAddBody.StateEnum.ACTIVE);
+
+            vnetBody.setTags(new ArrayList<>());
+
+            IpTreeAllocationTenant allocationTenant = new 
IpTreeAllocationTenant();
+            allocationTenant.setId(new BigDecimal(tenantId));
+            allocationTenant.setName(tenantName);
+            vnetBody.setTenant(allocationTenant);
+
+            vnetBody.setVlan(0);
+            vnetBody.setVlanAware(false);
+            vnetBody.setVlans("");
+
+            VnetAddBodyVpc vpc = new VnetAddBodyVpc();
+            vpc.setName(associatedVpc.getName());
+            vpc.setId(associatedVpc.getId());
+            vnetBody.setVpc(vpc);
+
+            vnetBody.setTags(Collections.singletonList(netrisTag));
+
+            VNetApi vnetApi = apiClient.getApiStubForMethod(VNetApi.class);
+            return vnetApi.apiV2VnetPost(vnetBody);
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error creating Netris vNet %s 
for VPC %s", netrisVnetName, associatedVpc.getName()), e);
+            return null;
+        }
+    }
+

Review Comment:
   ```suggestion
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@cloudstack.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to