You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pe...@apache.org on 2025/03/05 22:51:30 UTC

(cloudstack) branch netris-update-vpc-and-tier-names created (now 793f1f2958c)

This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a change to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


      at 793f1f2958c add license

This branch includes the following new commits:

     new 09afd261e86 Support for listing VPCs (#5)
     new b3c89c62e90 List tenants
     new 9f76e377d49 Delegate API classes creation to the SDK and simply invoke the desired API class through CloudStack (#7)
     new bfb017426d5 Support adding netris provider to CloudStack and Netris VPC Creation (#6)
     new 78efab46d51 Fix unit tests
     new 0f913b48a72 Add support to delete VNets and Subnets (#13)
     new 6e41063808f Add missing suffix return (#14)
     new 9b4d95780a0 Set up Netris Public range on new zone addition (#15)
     new c8eb0f37792 Pass VXLAN ID during creation of Netris vNets (#16)
     new b497c65ff0e Update Subnet purpose for Netris Public Traffic (#17)
     new 3ee9ed9233e Fix VR Public IP address (#20)
     new 8163d3278c5 Pass network gateway instead of network CIDR for Netris vNet creation (#21)
     new 4c07565bf04 Run moodifyvxlan script if broadcast domain type is Netris (#18)
     new 7b0d982a448 Add support to create Netris VPC / Network offerings (#22)
     new a9982c4d4d2 Merge tag '4.20.0.0' into netris-420-backport
     new d24b6f229d6 Merge pull request #25 from shapeblue/netris-420-backport
     new b70f72abec2 Merge branch 'netris-release' into netris-phase2
     new ce9cbb2ff88 Phase4 - Add support for Source NAT, Static NAT and Port Forwarding (#19)
     new aef61973f3e List only Netris Public IPs for NAT operations (#26)
     new 4bf4dafcb3e Support to pass provider when creating public ip range and create IPAM on Netris (#28)
     new 6157c8f70c5  Revert UI filtration for public IPs (#29)
     new aa222a51ab9 Fix unit tests (#30)
     new 554ed63440d Add Netris Tag parameter to the Network provider and fix zone creation wizard (#33)
     new 444fb7324d2 Fix SourceAddress for SNAt to VPC cidr (#35)
     new bbb7acccbee Fix VPC/network offering service list for external network providers in Routed mode (#32)
     new 5ac35a2d8bd Add support to add and delete and update static routes on Netris (#37)
     new 8659d9691b8 Netris FR1b: Support Remote Access VPN and Site-to-Site VPN in VPC VR (#41)
     new 610fd911d62 Add support for Gateway service for Netris VPC and network offerings (#39)
     new b525255e851 Netris VPN: Fix s2s vpn status update and isolated network implementation (#42)
     new f54cb50d178 Release NAT IP subnet when VPC is removed or IP is released (#44)
     new 57609c79c42 Add support to add IPv6 Public IP range as IPAM Allocation / Subnet on Netris (#36)
     new f70cc1c3b72 Phase5 - Support for ACLs in Netris (#31)
     new 48481945d1f Add global routing flag on subnet creation (#45)
     new ada3200a051 Support change snat ip (#46)
     new 427d7328c14 Fix naming convention for NAT subnets to follow other resources (#47)
     new 5289fe4b625 Keep Vpn service to default VPC offering with Natted mode only (#50)
     new 7f318032328 Validate if given CIDR belongs to a bigger allocation in Netris before creating the zone-level allocation (#48)
     new ca4c13c4a09 Phase5 - Support for LB - create, delete and Update operations (#49)
     new bd4e652128c Enable Autoscaling on Netris for CPU and memory (#51)
     new f5a96e77a5a Fix the test failures noticed on #44 (#52)
     new af27e88c828 Increase code coverage (#54)
     new 8572d6a7762 Update netris VPC and tier name
     new 1ee69a923ab add support to update vpc tier name
     new 793f1f2958c add license

The 44 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(cloudstack) 34/44: Support change snat ip (#46)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit ada3200a05149dbf79325f4552e3b3b7cf897c47
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Tue Feb 4 12:20:54 2025 -0500

    Support change snat ip (#46)
    
    * Support updating VPC Source NAT IP
    
    * Optimize code
    
    * Update source NAT IP
---
 .../java/org/apache/cloudstack/service/NetrisElement.java   |  2 +-
 .../org/apache/cloudstack/service/NetrisServiceImpl.java    |  3 ++-
 .../src/main/java/com/cloud/network/vpc/VpcManagerImpl.java | 13 +++++++------
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 7ffa3363046..0f25d216719 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -438,7 +438,7 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
 
     @Override
     public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
-        return true;
+        return netrisService.updateVpcSourceNatIp(vpc, address);
     }
 
     @Override
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 9a4f5d972ab..337ea849dcb 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -292,7 +292,8 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
 
         logger.debug("Updating the source NAT IP for Netris VPC {} to IP: {}", vpc.getName(), address.getAddress().addr());
 
-        CreateOrUpdateNetrisNatCommand cmd = new CreateOrUpdateNetrisNatCommand(zoneId, accountId, domainId, vpcName, vpcId, null, null, true, address.getAddress().addr());
+        CreateOrUpdateNetrisNatCommand cmd = new CreateOrUpdateNetrisNatCommand(zoneId, accountId, domainId, vpcName, vpcId, null, null, true, vpc.getCidr());
+        cmd.setNatIp(address.getAddress().addr());
         cmd.setNatRuleType("SNAT");
         String snatRuleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.SNAT, String.valueOf(vpcId));
         cmd.setNatRuleName(snatRuleName);
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index d34cba5aeb6..dd49bbd79f9 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -1640,13 +1640,14 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
             try {
                 _ipAddrMgr.updateSourceNatIpAddress(requestedIp, userIps);
                 if (isVpcForProvider(Provider.Nsx, vpc) || isVpcForProvider(Provider.Netris, vpc)) {
-                    VpcProvider nsxElement = (VpcProvider) _ntwkModel.getElementImplementingProvider(Provider.Nsx.getName());
-                    if (nsxElement == null) {
-                        return true;
+                    boolean isForNsx = _vpcOffSvcMapDao.isProviderForVpcOffering(Provider.Nsx, vpc.getVpcOfferingId());
+                    String providerName = isForNsx ? Provider.Nsx.getName() : Provider.Netris.getName();
+                    VpcProvider providerElement = (VpcProvider) _ntwkModel.getElementImplementingProvider(providerName);
+                    if (Objects.nonNull(providerElement)) {
+                        providerElement.updateVpcSourceNatIp(vpc, requestedIp);
+                        return false;
                     }
-                    nsxElement.updateVpcSourceNatIp(vpc, requestedIp);
-                    // The NSX source NAT IP change does not require to update the VPC VR
-                    return false;
+                    return true;
                 }
             } catch (Exception e) { // pokemon exception from transaction
                 String msg = String.format("Update of source NAT ip to %s for network \"%s\"/%s failed due to %s",


(cloudstack) 25/44: Fix VPC/network offering service list for external network providers in Routed mode (#32)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit bbb7acccbee976fa4f1cd96f1ca94836ece0735b
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Fri Dec 13 10:43:07 2024 -0500

    Fix VPC/network offering service list for external network providers in Routed mode (#32)
    
    * Fix network offering service list for external network providers in Routed mode
    
    * filter out unsupported services based on network mode
    
    * fix supported services list for vpc offering for external providers in Routed mode
---
 .../admin/network/CreateNetworkOfferingCmd.java    | 14 +++++---
 .../command/admin/vpc/CreateVPCOfferingCmd.java    | 15 +++++---
 ui/src/views/offering/AddNetworkOffering.vue       | 40 +++++++++++++---------
 3 files changed, 44 insertions(+), 25 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
index 362e9c8b3bb..e2509b7c532 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
@@ -289,11 +289,14 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
             List<String> services = new ArrayList<>(List.of(
                     Dhcp.getName(),
                     Dns.getName(),
-                    StaticNat.getName(),
-                    SourceNat.getName(),
-                    PortForwarding.getName(),
                     UserData.getName()
             ));
+            if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) {
+                services.addAll(Arrays.asList(
+                        StaticNat.getName(),
+                        SourceNat.getName(),
+                        PortForwarding.getName()));
+            }
             if (getNsxSupportsLbService()) {
                 services.add(Lb.getName());
             }
@@ -405,8 +408,9 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
                 continue;
             if (routerSupported.contains(service))
                 serviceProviderMap.put(service, List.of(routerProvider));
-            else
-                serviceProviderMap.put(service, List.of(provider));
+            else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || NetworkACL.getName().equalsIgnoreCase(service)) {
+                    serviceProviderMap.put(service, List.of(provider));
+                }
             if (!getNsxSupportsLbService()) {
                 serviceProviderMap.remove(Lb.getName());
             }
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
index 020e28e23f4..dcceb6ada14 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
@@ -26,10 +26,12 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.network.Network;
 import com.cloud.network.VirtualRouterProvider;
+import com.cloud.offering.NetworkOffering;
 import org.apache.cloudstack.api.response.DomainResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.commons.collections.CollectionUtils;
@@ -179,12 +181,15 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
             supportedServices = new ArrayList<>(List.of(
                     Dhcp.getName(),
                     Dns.getName(),
-                    StaticNat.getName(),
-                    SourceNat.getName(),
                     NetworkACL.getName(),
-                    PortForwarding.getName(),
                     UserData.getName()
                     ));
+            if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) {
+                supportedServices.addAll(Arrays.asList(
+                        StaticNat.getName(),
+                        SourceNat.getName(),
+                        PortForwarding.getName()));
+            }
             if (getNsxSupportsLbService()) {
                 supportedServices.add(Lb.getName());
             }
@@ -243,8 +248,10 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
                 continue;
             if (routerSupported.contains(service))
                 serviceProviderMap.put(service, List.of(VirtualRouterProvider.Type.VPCVirtualRouter.name()));
-            else
+            else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) ||
+                    Stream.of(NetworkACL.getName()).anyMatch(s -> s.equalsIgnoreCase(service))) {
                 serviceProviderMap.put(service, List.of(provider));
+            }
         }
         if (!getNsxSupportsLbService()) {
             serviceProviderMap.remove(Lb.getName());
diff --git a/ui/src/views/offering/AddNetworkOffering.vue b/ui/src/views/offering/AddNetworkOffering.vue
index 7bd8fdb95a0..713c1b805c1 100644
--- a/ui/src/views/offering/AddNetworkOffering.vue
+++ b/ui/src/views/offering/AddNetworkOffering.vue
@@ -956,10 +956,12 @@ export default {
           Dhcp: this.forVpc ? this.VPCVR : this.VR,
           Dns: this.forVpc ? this.VPCVR : this.VR,
           UserData: this.forVpc ? this.VPCVR : this.VR,
-          SourceNat: this.NSX,
-          StaticNat: this.NSX,
-          PortForwarding: this.NSX,
-          Lb: this.NSX,
+          ...(this.networkmode === 'NATTED' && {
+            SourceNat: this.NSX,
+            StaticNat: this.NSX,
+            PortForwarding: this.NSX,
+            Lb: this.NSX
+          }),
           ...(forVpc && { NetworkACL: this.NSX }),
           ...(!forVpc && { Firewall: this.NSX })
         }
@@ -968,10 +970,12 @@ export default {
           Dhcp: this.forVpc ? this.VPCVR : this.VR,
           Dns: this.forVpc ? this.VPCVR : this.VR,
           UserData: this.forVpc ? this.VPCVR : this.VR,
-          SourceNat: this.Netris,
-          StaticNat: this.Netris,
-          PortForwarding: this.Netris,
-          Lb: this.Netris,
+          ...(this.networkmode === 'NATTED' && {
+            SourceNat: this.Netris,
+            StaticNat: this.Netris,
+            PortForwarding: this.Netris,
+            Lb: this.Netris
+          }),
           ...(forVpc && { NetworkACL: this.Netris }),
           ...(!forVpc && { Firewall: this.Netris })
         }
@@ -985,10 +989,12 @@ export default {
           Dhcp: this.forVpc ? this.VPCVR : this.VR,
           Dns: this.forVpc ? this.VPCVR : this.VR,
           UserData: this.forVpc ? this.VPCVR : this.VR,
-          SourceNat: this.NSX,
-          StaticNat: this.NSX,
-          PortForwarding: this.NSX,
-          Lb: this.NSX,
+          ...(this.networkmode === 'NATTED' && {
+            SourceNat: this.NSX,
+            StaticNat: this.NSX,
+            PortForwarding: this.NSX,
+            Lb: this.NSX
+          }),
           ...(this.forVpc && { NetworkACL: this.NSX }),
           ...(!this.forVpc && { Firewall: this.NSX })
         }
@@ -997,10 +1003,12 @@ export default {
           Dhcp: this.forVpc ? this.VPCVR : this.VR,
           Dns: this.forVpc ? this.VPCVR : this.VR,
           UserData: this.forVpc ? this.VPCVR : this.VR,
-          SourceNat: this.Netris,
-          StaticNat: this.Netris,
-          PortForwarding: this.Netris,
-          Lb: this.Netris,
+          ...(this.networkmode === 'NATTED' && {
+            SourceNat: this.Netris,
+            StaticNat: this.Netris,
+            PortForwarding: this.Netris,
+            Lb: this.Netris
+          }),
           ...(this.forVpc && { NetworkACL: this.Netris }),
           ...(!this.forVpc && { Firewall: this.Netris })
         }


(cloudstack) 27/44: Netris FR1b: Support Remote Access VPN and Site-to-Site VPN in VPC VR (#41)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 8659d9691b860c987231c2a75b92c2b873e9168f
Author: Wei Zhou <we...@apache.org>
AuthorDate: Fri Dec 20 14:53:48 2024 +0100

    Netris FR1b: Support Remote Access VPN and Site-to-Site VPN in VPC VR (#41)
    
    * Static Routes: support nexthop
    
    * Update api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java
    
    Co-authored-by: Pearl Dsilva <pe...@gmail.com>
    
    * PR#10064 VR: apply iptables rules when add/remove static routes
    
    * PR#10065 UI: fix cannot open 'Edit tags' modal for static routes
    
    * PR#10066 Static Routes: fix check on wrong global configuration
    
    * PR#10067 VR: fix site-2-site VPN if split connections is enabled
    
    * PR#10081 server: do not allocate nic on public network for NSX VPC VR
    
    * PR#10082 UI: create VPC network offering with conserve mode
    
    * PR#10083 VR: allow outgoing traffic from RAS/VPN clients
    
    * PR#10086 server: fix typo removeaccessvpn in VirtualRouterElement
    
    * server: Add check on Public IP for remote access VPN
    
    * Revert "PR#10083 VR: allow outgoing traffic from RAS/VPN clients"
    
    This reverts commit 2f9b9f428947cac91de322fbdf4a980902a1c0a0.
    
    * VPC: fetch same used IP for domain router if VR is not Source NAT
    
    * VR: pass has_public_network to VR and configure RA/S2S VPN left peers
    
    * Revert "PR#10081 server: do not allocate nic on public network for NSX VPC VR"
    
    This reverts commit 809e269ed6b361d9df1fcef6537762c5612863e0.
    
    * VPC: fetch same used IP for domain router if VR is not Source NAT (v2)
    
    * VR: fix /etc/hosts and nameservers in dnsmasq.conf if VPC VR is not guest gateway
    
    prior to this PR
    ```
    root@r-1167-VM:~# cat /etc/hosts
    127.0.0.1       localhost
    127.0.1.1       r-1167-VM
    ::1     localhost ip6-localhost ip6-loopback
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters
    172.21.1.33     dummy-vpc-vpn-001
    172.21.1.1      r-1167-VM data-server
    
    root@r-1167-VM:~# cat /etc/dnsmasq.d/cloud.conf
    dhcp-hostsfile=/etc/dhcphosts.txt
    listen-address=127.0.0.1,172.21.1.234
    dhcp-range=set:interface-eth1-0,172.21.1.234,static
    dhcp-option=tag:interface-eth1-0,15,cs2cloud.internal
    dhcp-option=tag:interface-eth1-0,6,172.21.1.1,10.0.32.1,8.8.8.8
    dhcp-option=tag:interface-eth1-0,3,172.21.1.1
    dhcp-option=eth1,26,1500
    dhcp-option=tag:interface-eth1-0,1,255.255.255.0
    ```
    
    the lines should be
    ```
    172.21.1.234  r-1167-VM data-server
    
    dhcp-option=tag:interface-eth1-0,6,10.0.32.1,8.8.8.8
    ```
    
    * server: Enable static NAT for Domain router if it is not Source NAT
    
    * server: Enable static NAT for Domain router on UI
    
    * server: assign Public IP to VPC VR and enable static nat if VR is not Source NAT
    
    * server: configure dns1 if VR is not Source NAT
    
    * server: remove check on Firewall service when list network service providers
    
    * UI: remove dot from message.enabled.vpn
    
    * systemvm: add default route via first guest gateway if VR does not have public IP/interface
    
    * VR: add fw_dhcpserver for shared network
    
    * VR: pass has_public_network to VR and configure RA/S2S VPN left peers (v2)
    
    * UI: fix request error when create a VPC tier in a non-Netris/NSX env
    
    * systemvm: add default route via first guest gateway (v2)
    
    * VR: configure iptables rules for S2S vpn on first guest interface
    
    * VR: allow FORWARD to guest interfaces if VR is not Public
    
    * VR: configure remote access vpn on first guest interface if not public
    
    * VR: fix error 789 in RA VPN client when both RA and S2S are configured
    
    * server: Apply Static Route for RA/S2S VPN in VPC VR
    
    * VR: do not set mark for Public interface when VR is not really public
    
    * VPN: do not disable static nat if it is used by a RA/S2S VPN
    
    * server: skip check on network conserve mode if disable/enable RA VPN on Router IP
    
    * server: set forRouter to false when release a IP
    
    * VR: diable IP spoofing protection on default guest network
    
    * VR: fix iptables rules only when only S2S vpn is enabled
    
    * UI: show 'VPN Connections' section
    
    * VPC: new methods to configure/reconfigure Static NAT for VPC VR
    
    * API: set Type in ip address response to DomainRouter if it is used by VR
    
    * server: do not allow IP release if it is used by RA or S2S VPN gateway
    
    * VR: check if interface is added
    
    * VR: add default route only when ip is associated to first guest interface
    
    * VR: fix ipsec conf for l2tp and s2s vpn
    
    * server: save placeholder IP for VPC VR to fix the new VR IP when vpc tier is auto-shutdown
    
    * server: get non-placeholder NIC for VPC VR
    
    * VR: wait 15 seconds after starting password server
    
    * server: fix unable to configure static nat due to 'invalid virtual machine id'
    
    * UI: fix link of router in info card
    
    * VPC: apply static route for VPC VPN if needed (refactoring)
    
    * server: fix VR IP of first VPC tier is the VM gateway
    
    * server: update or remove all existing static routes when shutdown a network
    
    * server: update ipaddress after disabling static nat to fix vpc deletion issue
    
    * servr: disable remote access VPN as part of VPC dstroy
    
    * server: apply static routes when implement a vpc tier
    
    * server: apply static routes even if next hop is null
    
    * server: fix Cannot invoke "com.cloud.vm.NicProfile.getRequestedIPv4()" because "requested" is null
    
    * Netris: Update Vpn provider to VpcVirtualRouter
    
    * Netris: Add Vpn service to network offerings and networks
    
    * server: fix CIDR of VPN ip range
    
    * server: set isVrGuestGateway by SoureNat/Gateway service with Provider.VPCVirtualRouter
    
    * VR: password server takes 10-15 seconds to start if VR IP is not configured in /etc/hosts
    
    * Netris: add back routesPutBody.setStateStatus
    
    * engine/schema: remove SQL changes in schema-41910to42000.sql
    
    ---------
    
    Co-authored-by: Pearl Dsilva <pe...@gmail.com>
---
 api/src/main/java/com/cloud/network/IpAddress.java |   1 +
 .../com/cloud/network/Site2SiteVpnConnection.java  |   2 +-
 .../java/com/cloud/network/vpc/StaticRoute.java    |   4 +-
 .../com/cloud/network/vpc/StaticRouteProfile.java  |  22 +-
 .../java/com/cloud/network/vpc/VpcService.java     |   2 +-
 .../org/apache/cloudstack/api/ApiConstants.java    |   3 +
 .../api/command/user/vpc/CreateStaticRouteCmd.java |  51 ++-
 .../api/response/StaticRouteResponse.java          |  24 +-
 .../service/NetworkOrchestrationService.java       |   3 +
 .../main/java/com/cloud/network/addr/PublicIp.java |   4 +
 .../java/com/cloud/network/rules/RulesManager.java |   2 +
 .../java/com/cloud/network/vpc/VpcManager.java     |  17 +
 .../engine/orchestration/NetworkOrchestrator.java  |  21 +-
 .../com/cloud/network/dao/IPAddressDaoImpl.java    |   1 +
 .../java/com/cloud/network/dao/IPAddressVO.java    |  12 +
 .../com/cloud/network/dao/RemoteAccessVpnDao.java  |   2 +
 .../cloud/network/dao/RemoteAccessVpnDaoImpl.java  |   7 +
 .../cloud/network/dao/Site2SiteVpnGatewayDao.java  |   2 +
 .../network/dao/Site2SiteVpnGatewayDaoImpl.java    |   8 +
 .../java/com/cloud/network/vpc/StaticRouteVO.java  |  35 +-
 .../network/vpc/dao/VpcServiceMapDaoImpl.java      |  11 +-
 .../src/main/java/com/cloud/vm/dao/NicDao.java     |   4 +
 .../src/main/java/com/cloud/vm/dao/NicDaoImpl.java |  20 +
 .../resources/META-INF/db/schema-41910to42000.sql  |   6 +
 .../cloudstack/service/NetrisApiClientImpl.java    |   1 +
 .../main/java/com/cloud/api/ApiResponseHelper.java |  52 ++-
 .../com/cloud/network/IpAddressManagerImpl.java    |  21 ++
 .../java/com/cloud/network/NetworkModelImpl.java   |   1 +
 .../network/element/VirtualRouterElement.java      |   2 +-
 .../network/guru/ExternalGuestNetworkGuru.java     |   5 +-
 .../cloud/network/router/CommandSetupHelper.java   |   5 +-
 .../cloud/network/router/NetworkHelperImpl.java    |   9 +-
 .../cloud/network/router/NicProfileHelperImpl.java |   4 +-
 .../router/VirtualNetworkApplianceManagerImpl.java |   8 +
 .../VpcVirtualNetworkApplianceManagerImpl.java     |  20 +-
 .../com/cloud/network/rules/RulesManagerImpl.java  |  35 +-
 .../java/com/cloud/network/vpc/VpcManagerImpl.java | 402 +++++++++++++++++++--
 .../network/vpn/RemoteAccessVpnManagerImpl.java    |  71 +++-
 .../cloud/network/vpn/Site2SiteVpnManagerImpl.java |  69 +++-
 .../com/cloud/server/ConfigurationServerImpl.java  |   1 +
 systemvm/debian/opt/cloud/bin/configure.py         |  44 ++-
 systemvm/debian/opt/cloud/bin/cs/CsAddress.py      |  91 ++++-
 systemvm/debian/opt/cloud/bin/cs/CsConfig.py       |   3 +
 systemvm/debian/opt/cloud/bin/cs/CsDhcp.py         |   4 +-
 systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py |  10 +-
 systemvm/debian/opt/cloud/bin/passwd_server_ip.py  |  13 +-
 ui/public/locales/en.json                          |   6 +-
 ui/src/components/view/InfoCard.vue                |   2 +-
 ui/src/config/section/network.js                   |   3 +-
 ui/src/views/network/PublicIpResource.vue          |  31 +-
 ui/src/views/network/StaticRoutesTab.vue           | 131 +++++--
 ui/src/views/network/VpcTab.vue                    |   5 +
 ui/src/views/network/VpcTiersTab.vue               |   2 +-
 ui/src/views/offering/AddNetworkOffering.vue       |   2 +-
 .../main/java/com/cloud/utils/net/NetUtils.java    |  16 +
 .../java/com/cloud/utils/net/NetUtilsTest.java     |  11 +
 56 files changed, 1153 insertions(+), 191 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/IpAddress.java b/api/src/main/java/com/cloud/network/IpAddress.java
index ae1af450577..70d652b54e9 100644
--- a/api/src/main/java/com/cloud/network/IpAddress.java
+++ b/api/src/main/java/com/cloud/network/IpAddress.java
@@ -99,4 +99,5 @@ public interface IpAddress extends ControlledEntity, Identity, InternalIdentity,
 
     boolean isForSystemVms();
 
+    boolean isForRouter();
 }
diff --git a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java
index 994df875f7d..51036abe060 100644
--- a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java
+++ b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java
@@ -24,7 +24,7 @@ import org.apache.cloudstack.api.InternalIdentity;
 
 public interface Site2SiteVpnConnection extends ControlledEntity, InternalIdentity, Displayable {
     enum State {
-        Pending, Connecting, Connected, Disconnected, Error,
+        Pending, Connecting, Connected, Disconnected, Error, Removed
     }
 
     @Override
diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
index b52ed980893..739fca328b8 100644
--- a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
+++ b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
@@ -33,7 +33,9 @@ public interface StaticRoute extends ControlledEntity, Identity, InternalIdentit
     /**
      * @return
      */
-    long getVpcGatewayId();
+    Long getVpcGatewayId();
+
+    String getNextHop();
 
     /**
      * @return
diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java
index cb4849f1f7b..c8fc073911f 100644
--- a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java
+++ b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java
@@ -23,7 +23,8 @@ public class StaticRouteProfile implements StaticRoute {
     private String targetCidr;
     private long accountId;
     private long domainId;
-    private long gatewayId;
+    private Long gatewayId;
+    private String nextHop;
     private StaticRoute.State state;
     private long vpcId;
     String vlanTag;
@@ -46,6 +47,18 @@ public class StaticRouteProfile implements StaticRoute {
         ipAddress = gateway.getIp4Address();
     }
 
+    public StaticRouteProfile(StaticRoute staticRoute) {
+        id = staticRoute.getId();
+        uuid = staticRoute.getUuid();
+        targetCidr = staticRoute.getCidr();
+        accountId = staticRoute.getAccountId();
+        domainId = staticRoute.getDomainId();
+        gatewayId = staticRoute.getVpcGatewayId();
+        state = staticRoute.getState();
+        vpcId = staticRoute.getVpcId();
+        gateway = staticRoute.getNextHop();
+    }
+
     @Override
     public long getAccountId() {
         return accountId;
@@ -57,10 +70,15 @@ public class StaticRouteProfile implements StaticRoute {
     }
 
     @Override
-    public long getVpcGatewayId() {
+    public Long getVpcGatewayId() {
         return gatewayId;
     }
 
+    @Override
+    public String getNextHop() {
+        return nextHop;
+    }
+
     @Override
     public String getCidr() {
         return targetCidr;
diff --git a/api/src/main/java/com/cloud/network/vpc/VpcService.java b/api/src/main/java/com/cloud/network/vpc/VpcService.java
index af2a9847a62..4477f10d1a4 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcService.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java
@@ -238,7 +238,7 @@ public interface VpcService {
      * @param cidr
      * @return
      */
-    StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException;
+    StaticRoute createStaticRoute(Long gatewayId, Long vpcId, String nextHop, String cidr) throws NetworkRuleConflictException;
 
     /**
      * Lists static routes based on parameters passed to the call
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 17fe3d624d8..744dd22aae5 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -254,6 +254,7 @@ public class ApiConstants {
     public static final String PREVIOUS_OWNER_ID = "previousownerid";
     public static final String PREVIOUS_OWNER_NAME = "previousownername";
     public static final String NEXT_ACL_RULE_ID = "nextaclruleid";
+    public static final String NEXT_HOP = "nexthop";
     public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash";
     public static final String IMAGE_PATH = "imagepath";
     public static final String INSTANCE_CONVERSION_SUPPORTED = "instanceconversionsupported";
@@ -863,6 +864,8 @@ public class ApiConstants {
     public static final String NETWORK = "network";
     public static final String VPC_ID = "vpcid";
     public static final String VPC_NAME = "vpcname";
+    public static final String VPC_GATEWAY_ID = "vpcgatewayid";
+    public static final String VPC_GATEWAY_IP = "vpcgatewayip";
     public static final String GATEWAY_ID = "gatewayid";
     public static final String CAN_USE_FOR_DEPLOY = "canusefordeploy";
     public static final String RESOURCE_IDS = "resourceids";
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java
index b28c02cb800..41779ccdeb1 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java
@@ -27,6 +27,7 @@ import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.response.PrivateGatewayResponse;
 import org.apache.cloudstack.api.response.StaticRouteResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
 import org.apache.cloudstack.context.CallContext;
 
 import com.cloud.event.EventTypes;
@@ -45,20 +46,38 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd {
     @Parameter(name = ApiConstants.GATEWAY_ID,
                type = CommandType.UUID,
                entityType = PrivateGatewayResponse.class,
-               required = true,
-               description = "the gateway id we are creating static route for")
+               description = "the gateway id we are creating static route for. Mutually exclusive with the nexthop parameter")
     private Long gatewayId;
 
+    @Parameter(name = ApiConstants.VPC_ID,
+            type = CommandType.UUID,
+            entityType = VpcResponse.class,
+            description = "the vpc id for which the static route is created. This is required for nexthop parameter")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.NEXT_HOP,
+            type = CommandType.STRING,
+            description = "the next hop of static route. Mutually exclusive with the gatewayid parameter")
+    private String nextHop;
+
     @Parameter(name = ApiConstants.CIDR, required = true, type = CommandType.STRING, description = "static route cidr")
     private String cidr;
 
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
-    public long getGatewayId() {
+    public Long getGatewayId() {
         return gatewayId;
     }
 
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public String getNextHop() {
+        return nextHop;
+    }
+
     public String getCidr() {
         return cidr;
     }
@@ -69,7 +88,7 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd {
     @Override
     public void create() throws ResourceAllocationException {
         try {
-            StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getCidr());
+            StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getVpcId(), getNextHop(), getCidr());
             setEntityId(result.getId());
             setEntityUuid(result.getUuid());
         } catch (NetworkRuleConflictException ex) {
@@ -114,11 +133,8 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd {
 
     @Override
     public long getEntityOwnerId() {
-        VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId);
-        if (gateway == null) {
-            throw new InvalidParameterValueException("Invalid gateway id is specified");
-        }
-        return _entityMgr.findById(Vpc.class, gateway.getVpcId()).getAccountId();
+        Long vpcId = getSyncObjId();
+        return _entityMgr.findById(Vpc.class, vpcId).getAccountId();
     }
 
     @Override
@@ -128,11 +144,20 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd {
 
     @Override
     public Long getSyncObjId() {
-        VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId);
-        if (gateway == null) {
-            throw new InvalidParameterValueException("Invalid id is specified for the gateway");
+        if (gatewayId != null) {
+            VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId);
+            if (gateway == null) {
+                throw new InvalidParameterValueException("Invalid id is specified for the gateway");
+            }
+            return gateway.getVpcId();
+        } else if (vpcId != null) {
+            Vpc vpc = _entityMgr.findById(Vpc.class, vpcId);
+            if (vpc == null) {
+                throw new InvalidParameterValueException("Invalid vpc id is specified");
+            }
+            return vpc.getId();
         }
-        return gateway.getVpcId();
+        throw new InvalidParameterValueException("One of vpcId or gatewayId must be specified");
     }
 
     @Override
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java
index 51f8a130383..9d0f90459a7 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java
@@ -42,9 +42,17 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit
     @Param(description = "VPC the static route belongs to")
     private String vpcId;
 
-    @SerializedName(ApiConstants.GATEWAY_ID)
+    @SerializedName(ApiConstants.VPC_GATEWAY_ID)
     @Param(description = "VPC gateway the route is created for")
-    private String gatewayId;
+    private String vpcGatewayId;
+
+    @SerializedName(ApiConstants.VPC_GATEWAY_IP)
+    @Param(description = "IP of VPC gateway the route is created for")
+    private String vpcGatewayIp;
+
+    @SerializedName(ApiConstants.NEXT_HOP)
+    @Param(description = "Next hop of the static route")
+    private String nextHop;
 
     @SerializedName(ApiConstants.CIDR)
     @Param(description = "static route CIDR")
@@ -95,8 +103,16 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit
         this.vpcId = vpcId;
     }
 
-    public void setGatewayId(String gatewayId) {
-        this.gatewayId = gatewayId;
+    public void setVpcGatewayId(String vpcGatewayId) {
+        this.vpcGatewayId = vpcGatewayId;
+    }
+
+    public void setVpcGatewayIp(String vpcGatewayIp) {
+        this.vpcGatewayIp = vpcGatewayIp;
+    }
+
+    public void setNextHop(String nextHop) {
+        this.nextHop = nextHop;
     }
 
     public void setCidr(String cidr) {
diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
index 363dcd31b52..adce5f2f8b4 100644
--- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
+++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
@@ -82,6 +82,9 @@ public interface NetworkOrchestrationService {
     ConfigKey<Integer> NetworkLockTimeout = new ConfigKey<Integer>(Integer.class, NetworkLockTimeoutCK, "Network", "600",
         "Lock wait timeout (seconds) while implementing network", true, Scope.Global, null);
 
+    ConfigKey<String> DeniedRoutes = new ConfigKey<String>(String.class, "denied.routes", "Network", "",
+            "Routes that are denied, can not be used for Static Routes creation for the VPC Private Gateway", true, ConfigKey.Scope.Zone, null);
+
     ConfigKey<String> GuestDomainSuffix = new ConfigKey<String>(String.class, GuestDomainSuffixCK, "Network", "cloud.internal",
         "Default domain name for vms inside virtualized networks fronted by router", true, ConfigKey.Scope.Zone, null);
 
diff --git a/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java b/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java
index d69a72a02c5..7132094ce11 100644
--- a/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java
+++ b/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java
@@ -275,5 +275,9 @@ public class PublicIp implements PublicIpAddress {
         return false;
     }
 
+    @Override
+    public boolean isForRouter() {
+        return _addr.isForRouter();
+    }
 
 }
diff --git a/engine/components-api/src/main/java/com/cloud/network/rules/RulesManager.java b/engine/components-api/src/main/java/com/cloud/network/rules/RulesManager.java
index c77874329fc..7bab2a26fb4 100644
--- a/engine/components-api/src/main/java/com/cloud/network/rules/RulesManager.java
+++ b/engine/components-api/src/main/java/com/cloud/network/rules/RulesManager.java
@@ -53,6 +53,8 @@ public interface RulesManager extends RulesService {
 
     boolean disableStaticNat(long ipAddressId, Account caller, long callerUserId, boolean releaseIpIfElastic) throws ResourceUnavailableException;
 
+    boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke);
+
     /**
      * @param networkId
      * @param continueOnError
diff --git a/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java b/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java
index a340f49c13f..9018c0d17a3 100644
--- a/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java
+++ b/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java
@@ -35,6 +35,7 @@ import com.cloud.network.Network.Provider;
 import com.cloud.network.Network.Service;
 import com.cloud.network.PhysicalNetwork;
 import com.cloud.network.addr.PublicIp;
+import com.cloud.network.dao.IPAddressVO;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.user.Account;
 
@@ -174,4 +175,20 @@ public interface VpcManager {
      * @return
      */
     boolean isSrcNatIpRequired(long vpcOfferingId);
+
+    boolean isSrcNatIpRequiredForVpcVr(long vpcOfferingId);
+
+    List<StaticRouteVO> getVpcStaticRoutes(Long vpcId);
+
+    List<StaticRouteProfile> getVpcStaticRoutes(List<? extends StaticRoute> routes);
+
+    boolean isProviderSupportServiceInVpc(long vpcId, Service service, Provider provider);
+
+    IPAddressVO getIpAddressForVpcVr(Vpc vpc, IPAddressVO ipAddress, boolean allocateIpIfNeeded);
+
+    boolean configStaticNatForVpcVr(Vpc vpc, IPAddressVO ipAddress);
+
+    void reconfigStaticNatForVpcVr(Long vpcId);
+
+    boolean applyStaticRouteForVpcVpnIfNeeded(Long vpcId, boolean updateAllVpn) throws ResourceUnavailableException;
 }
diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 15681b27c6f..99a3ec3caca 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -1058,7 +1058,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
         return Transaction.execute(new TransactionCallback<NicVO>() {
             @Override
             public NicVO doInTransaction(TransactionStatus status) {
-                NicVO vo = _nicDao.findByIp4AddressAndNetworkId(profile.getIPv4Address(), networkId);
+                NicVO vo = _nicDao.findNonPlaceHolderByIp4AddressAndNetworkId(profile.getIPv4Address(), networkId);
                 if (vo == null) {
                     applyProfileToNic(nic, profile, deviceId);
                     vo = _nicDao.persist(nic);
@@ -1707,6 +1707,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                     }
                 }
             }
+            if (network.getVpcId() != null) {
+                _vpcMgr.reconfigStaticNatForVpcVr(network.getVpcId());
+                try {
+                    _vpcMgr.applyStaticRouteForVpcVpnIfNeeded(network.getVpcId(), true);
+                } catch (ResourceUnavailableException e) {
+                    logger.error("Unable to apply static routes for vpc " + network.getVpcId() + " due to " + e.getMessage());
+                }
+            }
         } finally {
             for (final NetworkElement element : networkElements) {
                 if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) {
@@ -1952,6 +1960,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                                 ip.setOneToOneNat(false);
                                 ip.setAssociatedWithVmId(null);
                                 ip.setVmIp(null);
+                                ip.setForRouter(false);
                                 _ipAddressDao.update(ip.getId(), ip);
                             }
                         }
@@ -3294,6 +3303,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                 }
             }
         }
+        if (network.getVpcId() != null) {
+            _vpcMgr.reconfigStaticNatForVpcVr(network.getVpcId());
+            try {
+                _vpcMgr.applyStaticRouteForVpcVpnIfNeeded(network.getVpcId(), true);
+            } catch (ResourceUnavailableException e) {
+                logger.error("Unable to apply static routes for vpc " + network.getVpcId() + " due to " + e.getMessage());
+            }
+        }
         return success;
     }
 
@@ -4874,7 +4891,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
 
     @Override
     public ConfigKey<?>[] getConfigKeys() {
-        return new ConfigKey<?>[]{NetworkGcWait, NetworkGcInterval, NetworkLockTimeout,
+        return new ConfigKey<?>[]{NetworkGcWait, NetworkGcInterval, NetworkLockTimeout, DeniedRoutes,
                 GuestDomainSuffix, NetworkThrottlingRate, MinVRVersion,
                 PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RollingRestartEnabled,
                 TUNGSTEN_ENABLED, NSX_ENABLED, NETRIS_ENABLED };
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java
index aa143838c34..2b5277f1560 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java
@@ -197,6 +197,7 @@ public class IPAddressDaoImpl extends GenericDaoBase<IPAddressVO, Long> implemen
         address.setSourceNat(false);
         address.setOneToOneNat(false);
         address.setAssociatedWithVmId(null);
+        address.setForRouter(false);
         address.setState(State.Free);
         address.setAssociatedWithNetworkId(null);
         address.setVpcId(null);
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java
index 4c7569a55b9..6be7d24e367 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java
@@ -116,6 +116,9 @@ public class IPAddressVO implements IpAddress {
     @Column(name = "forsystemvms")
     private boolean forSystemVms = false;
 
+    @Column(name = "for_router")
+    private boolean forRouter = false;
+
     @Column(name= GenericDao.REMOVED_COLUMN)
     private Date removed;
 
@@ -385,4 +388,13 @@ public class IPAddressVO implements IpAddress {
     public boolean isForSystemVms() {
         return forSystemVms;
     }
+
+    @Override
+    public boolean isForRouter() {
+        return forRouter;
+    }
+
+    public void setForRouter(boolean forRouter) {
+        this.forRouter = forRouter;
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDao.java b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDao.java
index 8113c06c866..f4120a138ac 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDao.java
@@ -33,4 +33,6 @@ public interface RemoteAccessVpnDao extends GenericDao<RemoteAccessVpnVO, Long>
     List<RemoteAccessVpnVO> findByAccount(Long accountId);
 
     List<RemoteAccessVpnVO> listByNetworkId(Long networkId);
+
+    List<RemoteAccessVpnVO> listByVpcId(Long vpcId);
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java
index 484aa6f6631..ccbc60a5562 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java
@@ -85,4 +85,11 @@ public class RemoteAccessVpnDaoImpl extends GenericDaoBase<RemoteAccessVpnVO, Lo
         sc.setParameters("networkId", networkId);
         return listBy(sc);
     }
+
+    @Override
+    public List<RemoteAccessVpnVO> listByVpcId(Long vpcId) {
+        SearchCriteria<RemoteAccessVpnVO> sc = AllFieldsSearch.create();
+        sc.setParameters("vpcId", vpcId);
+        return listBy(sc);
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java
index d3fef252f50..3475003c269 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java
@@ -20,4 +20,6 @@ import com.cloud.utils.db.GenericDao;
 
 public interface Site2SiteVpnGatewayDao extends GenericDao<Site2SiteVpnGatewayVO, Long> {
     Site2SiteVpnGatewayVO findByVpcId(long vpcId);
+
+    Site2SiteVpnGatewayVO findByPublicIpAddress(long ipAddressId);
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java
index d1fde963217..0aeefe90c29 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java
@@ -35,6 +35,7 @@ public class Site2SiteVpnGatewayDaoImpl extends GenericDaoBase<Site2SiteVpnGatew
     protected Site2SiteVpnGatewayDaoImpl() {
         AllFieldsSearch = createSearchBuilder();
         AllFieldsSearch.and("vpcId", AllFieldsSearch.entity().getVpcId(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.and("ipAddressId", AllFieldsSearch.entity().getAddrId(), SearchCriteria.Op.EQ);
         AllFieldsSearch.done();
     }
 
@@ -44,4 +45,11 @@ public class Site2SiteVpnGatewayDaoImpl extends GenericDaoBase<Site2SiteVpnGatew
         sc.setParameters("vpcId", vpcId);
         return findOneBy(sc);
     }
+
+    @Override
+    public Site2SiteVpnGatewayVO findByPublicIpAddress(long ipAddressId) {
+        SearchCriteria<Site2SiteVpnGatewayVO> sc = AllFieldsSearch.create();
+        sc.setParameters("ipAddressId", ipAddressId);
+        return findOneBy(sc);
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java
index 2246bd6eed2..632d96819cd 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java
@@ -27,6 +27,7 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 
 import com.cloud.utils.db.GenericDao;
 
@@ -42,7 +43,10 @@ public class StaticRouteVO implements StaticRoute {
     String uuid;
 
     @Column(name = "vpc_gateway_id", updatable = false)
-    long vpcGatewayId;
+    Long vpcGatewayId;
+
+    @Column(name = "next_hop")
+    private String nextHop;
 
     @Column(name = "cidr")
     private String cidr;
@@ -67,6 +71,9 @@ public class StaticRouteVO implements StaticRoute {
         uuid = UUID.randomUUID().toString();
     }
 
+    @Transient
+    boolean forVpn = false;
+
     /**
      * @param vpcGatewayId
      * @param cidr
@@ -74,7 +81,7 @@ public class StaticRouteVO implements StaticRoute {
      * @param accountId TODO
      * @param domainId TODO
      */
-    public StaticRouteVO(long vpcGatewayId, String cidr, Long vpcId, long accountId, long domainId) {
+    public StaticRouteVO(Long vpcGatewayId, String cidr, Long vpcId, long accountId, long domainId, String nextHop) {
         super();
         this.vpcGatewayId = vpcGatewayId;
         this.cidr = cidr;
@@ -82,14 +89,32 @@ public class StaticRouteVO implements StaticRoute {
         this.vpcId = vpcId;
         this.accountId = accountId;
         this.domainId = domainId;
+        this.nextHop = nextHop;
+        uuid = UUID.randomUUID().toString();
+    }
+
+    public StaticRouteVO(String cidr, Long vpcId, long accountId, long domainId, String nextHop, State state, boolean forVpn) {
+        super();
+        this.cidr = cidr;
+        this.state = state;
+        this.vpcId = vpcId;
+        this.accountId = accountId;
+        this.domainId = domainId;
+        this.nextHop = nextHop;
         uuid = UUID.randomUUID().toString();
+        this.forVpn = forVpn;
     }
 
     @Override
-    public long getVpcGatewayId() {
+    public Long getVpcGatewayId() {
         return vpcGatewayId;
     }
 
+    @Override
+    public String getNextHop() {
+        return nextHop;
+    }
+
     @Override
     public String getCidr() {
         return cidr;
@@ -145,4 +170,8 @@ public class StaticRouteVO implements StaticRoute {
     public String getName() {
         return null;
     }
+
+    public boolean isForVpn() {
+        return forVpn;
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java
index 753c45fcc78..a5c4c83ff0f 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java
@@ -68,8 +68,15 @@ public class VpcServiceMapDaoImpl extends GenericDaoBase<VpcServiceMapVO, Long>
 
     @Override
     public boolean canProviderSupportServiceInVpc(long vpcId, Service service, Provider provider) {
-        // TODO Auto-generated method stub
-        return false;
+        SearchCriteria<VpcServiceMapVO> sc = AllFieldsSearch.create();
+        sc.setParameters("vpcId", vpcId);
+        sc.setParameters("service", service.getName());
+        sc.setParameters("provider", provider.getName());
+        if (findOneBy(sc) != null) {
+            return true;
+        } else {
+            return false;
+        }
     }
 
     @Override
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
index d34b03c4cb0..70a2558e2d4 100644
--- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
@@ -46,8 +46,12 @@ public interface NicDao extends GenericDao<NicVO, Long> {
 
     NicVO findByNetworkIdAndTypeIncludingRemoved(long networkId, VirtualMachine.Type vmType);
 
+    NicVO findNonPlaceHolderByNetworkIdAndType(long networkId, VirtualMachine.Type vmType);
+
     NicVO findByIp4AddressAndNetworkId(String ip4Address, long networkId);
 
+    NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId);
+
     NicVO findByNetworkIdAndMacAddress(long networkId, String mac);
 
     NicVO findDefaultNicForVM(long instanceId);
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
index 7d1af1982ae..3618785c1b8 100644
--- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
@@ -69,6 +69,7 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
         AllFieldsSearch.and("secondaryip", AllFieldsSearch.entity().getSecondaryIp(), Op.EQ);
         AllFieldsSearch.and("nicid", AllFieldsSearch.entity().getId(), Op.EQ);
         AllFieldsSearch.and("strategy", AllFieldsSearch.entity().getReservationStrategy(), Op.EQ);
+        AllFieldsSearch.and("strategyNEQ", AllFieldsSearch.entity().getReservationStrategy(), Op.NEQ);
         AllFieldsSearch.and("reserverName",AllFieldsSearch.entity().getReserver(),Op.EQ);
         AllFieldsSearch.and("macAddress", AllFieldsSearch.entity().getMacAddress(), Op.EQ);
         AllFieldsSearch.and("deviceid", AllFieldsSearch.entity().getDeviceId(), Op.EQ);
@@ -195,6 +196,15 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
         return findByNetworkIdAndTypeInternal(networkId, vmType, true);
     }
 
+    @Override
+    public NicVO findNonPlaceHolderByNetworkIdAndType(long networkId, VirtualMachine.Type vmType) {
+        SearchCriteria<NicVO> sc = AllFieldsSearch.create();
+        sc.setParameters("network", networkId);
+        sc.setParameters("vmType", vmType);
+        sc.setParameters("strategyNEQ", Nic.ReservationStrategy.PlaceHolder.toString());
+        return findOneBy(sc);
+    }
+
     @Override
     public NicVO findByNetworkIdTypeAndGateway(long networkId, VirtualMachine.Type vmType, String gateway) {
         SearchCriteria<NicVO> sc = AllFieldsSearch.create();
@@ -222,6 +232,16 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
         return findOneBy(sc);
     }
 
+    @Override
+    public NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId) {
+        SearchCriteria<NicVO> sc = AllFieldsSearch.create();
+        sc.setParameters("address", ip4Address);
+        sc.setParameters("network", networkId);
+        sc.setParameters("strategyNEQ", Nic.ReservationStrategy.PlaceHolder.toString());
+        return findOneBy(sc);
+    }
+
+
     @Override
     public NicVO findByNetworkIdAndMacAddress(long networkId, String mac) {
         SearchCriteria<NicVO> sc = AllFieldsSearch.create();
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
index 3f39a7da089..739c5644859 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
@@ -453,3 +453,9 @@ ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_nsx`;
 
 -- Drop the Tungsten and NSX columns from the VPC offerings (replaced by checking the provider on the vpc_offering_service_map table)
 ALTER TABLE `cloud`.`vpc_offerings` DROP COLUMN `for_nsx`;
+
+-- Add next_hop to the static_routes table
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.static_routes', 'next_hop', 'varchar(50) COMMENT "next hop of the static route" AFTER `vpc_gateway_id`');
+
+-- Add `for_router` to `user_ip_address` table
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_ip_address', 'for_router', 'tinyint(1) DEFAULT 0 COMMENT "True if the ip address is used by Domain Router to expose services"');
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index e2d13e47daa..2c409482a19 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -381,6 +381,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             routesPutBody.setPrefix(prefix);
             routesPutBody.setNextHop(nextHop);
             routesPutBody.setSiteId(new BigDecimal(siteId));
+            routesPutBody.setStateStatus(RoutesPutBody.StateStatusEnum.ACTIVE);
 
             routesPutBody.setDescription(staticRouteId);
             routesPutBody.setSwitches(Collections.emptyList());
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 43b3932b00c..5404cc46c85 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -49,6 +49,7 @@ import com.cloud.dc.dao.ASNumberDao;
 import com.cloud.dc.dao.ASNumberRangeDao;
 import com.cloud.dc.dao.VlanDetailsDao;
 import com.cloud.hypervisor.Hypervisor;
+import com.cloud.network.vpc.VpcGateway;
 import com.cloud.storage.BucketVO;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
@@ -1036,6 +1037,9 @@ public class ApiResponseHelper implements ResponseGenerator {
                 addSystemVmInfoToIpResponse(nic, response);
             }
         }
+        if (ipAddress.isForRouter()) {
+            response.setVirtualMachineType(Type.DomainRouter.toString());
+        }
         if (ipAddress.getAssociatedWithVmId() != null) {
             addUserVmDetailsInIpResponse(response, ipAddress);
         }
@@ -1062,12 +1066,25 @@ public class ApiResponseHelper implements ResponseGenerator {
     }
 
     private void addUserVmDetailsInIpResponse(IPAddressResponse response, IpAddress ipAddress) {
-        UserVm userVm = ApiDBUtils.findUserVmById(ipAddress.getAssociatedWithVmId());
-        if (userVm != null) {
-            response.setVirtualMachineId(userVm.getUuid());
-            response.setVirtualMachineName(userVm.getHostName());
-            response.setVirtualMachineType(userVm.getType().toString());
-            response.setVirtualMachineDisplayName(ObjectUtils.firstNonNull(userVm.getDisplayName(), userVm.getHostName()));
+        VirtualMachine vm = ApiDBUtils.findVMInstanceById(ipAddress.getAssociatedWithVmId());
+        if (vm == null) {
+            return;
+        }
+        if (vm.getType().equals(Type.User)) {
+            UserVm userVm = ApiDBUtils.findUserVmById(ipAddress.getAssociatedWithVmId());
+            if (userVm != null) {
+                response.setVirtualMachineId(userVm.getUuid());
+                response.setVirtualMachineName(userVm.getHostName());
+                response.setVirtualMachineType(userVm.getType().toString());
+                response.setVirtualMachineDisplayName(ObjectUtils.firstNonNull(userVm.getDisplayName(), userVm.getHostName()));
+            }
+        } else if (vm.getType().equals(Type.DomainRouter)) {
+            final boolean isAdmin = Account.Type.ADMIN.equals(CallContext.current().getCallingAccount().getType());
+            if (isAdmin) {
+                response.setVirtualMachineId(vm.getUuid());
+                response.setVirtualMachineName(vm.getHostName());
+            }
+            response.setVirtualMachineType(vm.getType().toString());
         }
     }
 
@@ -1234,6 +1251,13 @@ public class ApiResponseHelper implements ResponseGenerator {
                         ipResponse.setVirtualMachineDisplayName(vm.getHostName());
                     }
                 }
+            } else if (nic.getVmType() == Type.DomainRouter) {
+                VirtualMachine vm = ApiDBUtils.findVMInstanceById(nic.getInstanceId());
+                if (vm != null) {
+                    ipResponse.setVirtualMachineId(vm.getUuid());
+                    ipResponse.setVirtualMachineName(vm.getHostName());
+                    ipResponse.setVirtualMachineType(vm.getType().toString());
+                }
             } else if (nic.getVmType().isUsedBySystem()) {
                 ipResponse.setIsSystem(true);
                 addSystemVmInfoToIpResponse(nic, ipResponse);
@@ -3156,12 +3180,6 @@ public class ApiResponseHelper implements ResponseGenerator {
         List<? extends Network.Provider> serviceProviders = ApiDBUtils.getProvidersForService(service);
         List<ProviderResponse> serviceProvidersResponses = new ArrayList<ProviderResponse>();
         for (Network.Provider serviceProvider : serviceProviders) {
-            // return only Virtual Router/JuniperSRX/CiscoVnmc as a provider for the firewall
-            if (service == Service.Firewall
-                    && !(serviceProvider == Provider.VirtualRouter || serviceProvider == Provider.CiscoVnmc || serviceProvider == Provider.PaloAlto || serviceProvider == Provider.BigSwitchBcf || serviceProvider == Provider.Tungsten)) {
-                continue;
-            }
-
             ProviderResponse serviceProviderResponse = createServiceProviderResponse(serviceProvider);
             serviceProvidersResponses.add(serviceProviderResponse);
         }
@@ -3772,6 +3790,16 @@ public class ApiResponseHelper implements ResponseGenerator {
                 response.setVpcId(vpc.getUuid());
             }
         }
+        if (result.getVpcGatewayId() != null) {
+            VpcGateway vpcGateway = _entityMgr.findById(VpcGateway.class, result.getVpcGatewayId());
+            if (vpcGateway != null) {
+                response.setVpcGatewayId(vpcGateway.getUuid());
+                response.setVpcGatewayIp(vpcGateway.getIp4Address());
+            }
+        }
+        if (result.getNextHop() != null) {
+            response.setNextHop(result.getNextHop());
+        }
         response.setCidr(result.getCidr());
 
         StaticRoute.State state = result.getState();
diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
index e440d39462e..2c54a5d890c 100644
--- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
@@ -38,6 +38,8 @@ import com.cloud.dc.dao.VlanDetailsDao;
 import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NsxProviderDao;
 import com.cloud.network.dao.PublicIpQuarantineDao;
+import com.cloud.network.dao.RemoteAccessVpnDao;
+import com.cloud.network.dao.Site2SiteVpnGatewayDao;
 import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.element.NsxProviderVO;
 import com.cloud.network.vo.PublicIpQuarantineVO;
@@ -329,6 +331,10 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
 
     @Inject
     PublicIpQuarantineDao publicIpQuarantineDao;
+    @Inject
+    RemoteAccessVpnDao remoteAccessVpnDao;
+    @Inject
+    Site2SiteVpnGatewayDao site2SiteVpnGatewayDao;
 
     SearchBuilder<IPAddressVO> AssignIpAddressSearch;
     SearchBuilder<IPAddressVO> AssignIpAddressFromPodVlanSearch;
@@ -744,6 +750,21 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
                 throw new CloudRuntimeException("Unable to acquire lock on public IP.");
             }
 
+            if (ipToBeDisassociated.isForRouter()) {
+                if (remoteAccessVpnDao.findByPublicIpAddress(ipToBeDisassociated.getId()) != null) {
+                    InvalidParameterValueException ex = new InvalidParameterValueException("Can't release IP address as the IP address is used by a Remote Access VPN");
+                    ex.addProxyObject(ipToBeDisassociated.getUuid(), "ipId");
+                    throw ex;
+
+                }
+                if (site2SiteVpnGatewayDao.findByPublicIpAddress(ipToBeDisassociated.getId()) != null) {
+                    InvalidParameterValueException ex = new InvalidParameterValueException("Can't release IP address as the IP address is used by a VPC gateway");
+                    ex.addProxyObject(ipToBeDisassociated.getUuid(), "ipId");
+                    throw ex;
+
+                }
+            }
+
             PublicIpQuarantine publicIpQuarantine = null;
             // Cleanup all ip address resources - PF/LB/Static nat rules
             if (!cleanupIpResources(addrId, userId, caller)) {
diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java
index b8533b472ec..a9c5725d489 100644
--- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java
@@ -2300,6 +2300,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
             }
             if (capabilities != null && implementedProvider != null) {
                 for (Service service : capabilities.keySet()) {
+                    logger.info("Add provider {} and service {}", implementedProvider.getName(), service.getName());
                     if (s_serviceToImplementedProvidersMap.containsKey(service)) {
                         List<Provider> providers = s_serviceToImplementedProvidersMap.get(service);
                         providers.add(implementedProvider);
diff --git a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
index a87504cd07a..53cf838ca87 100644
--- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
@@ -543,7 +543,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
         // Set capabilities for vpn
         final Map<Capability, String> vpnCapabilities = new HashMap<Capability, String>();
         vpnCapabilities.put(Capability.SupportedVpnProtocols, "pptp,l2tp,ipsec");
-        vpnCapabilities.put(Capability.VpnTypes, "removeaccessvpn");
+        vpnCapabilities.put(Capability.VpnTypes, "remoteaccessvpn");
         capabilities.put(Service.Vpn, vpnCapabilities);
 
         final Map<Capability, String> dnsCapabilities = new HashMap<Capability, String>();
diff --git a/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java
index 4f76488337d..0c670ae83e0 100644
--- a/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java
+++ b/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java
@@ -288,8 +288,9 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru {
             profile.setIPv4Netmask(null);
         }
 
-        if (config.getVpcId() == null && vm.getType() == VirtualMachine.Type.DomainRouter) {
-            boolean isPublicNetwork = _networkModel.isProviderSupportServiceInNetwork(config.getId(), Service.SourceNat, Provider.VirtualRouter);
+        if (vm.getType() == VirtualMachine.Type.DomainRouter) {
+            boolean isPublicNetwork = _networkModel.isProviderSupportServiceInNetwork(config.getId(), Service.SourceNat, Provider.VirtualRouter)
+                    || _networkModel.isProviderSupportServiceInNetwork(config.getId(), Service.SourceNat, Provider.VPCVirtualRouter);
             if (!isPublicNetwork) {
                 Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(config, null);
                 if (placeholderNic == null) {
diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
index 83a3dbcfaa7..3481efc2b51 100644
--- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
+++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
@@ -1219,8 +1219,9 @@ public class CommandSetupHelper {
         final SetupGuestNetworkCommand setupCmd = new SetupGuestNetworkCommand(dhcpRange, networkDomain, router.getIsRedundantRouter(), defaultDns1, defaultDns2, add, _itMgr.toNicTO(nicProfile,
                 router.getHypervisorType()));
 
-        boolean isForNsx = _networkModel.isProviderForNetworkOffering(Provider.Nsx, networkOfferingVO.getId());
-        setupCmd.setVrGuestGateway(isForNsx);
+        boolean isVrGuestGateway = _networkModel.isAnyServiceSupportedInNetwork(network.getId(), Provider.VPCVirtualRouter, Service.SourceNat, Service.Gateway);
+        setupCmd.setVrGuestGateway(isVrGuestGateway);
+
         NicVO publicNic = _nicDao.findDefaultNicForVM(router.getId());
         if (publicNic != null) {
             updateSetupGuestNetworkCommandIpv6(setupCmd, network, publicNic, defaultIp6Dns1, defaultIp6Dns2);
diff --git a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
index 1f4642bbd85..dd82a2b8203 100644
--- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
+++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
@@ -28,6 +28,7 @@ import java.util.Map;
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 
+import com.cloud.network.vpc.VpcManager;
 import com.cloud.network.vpc.dao.VpcDao;
 import com.cloud.utils.validation.ChecksumUtil;
 import org.apache.cloudstack.api.ApiConstants;
@@ -176,6 +177,8 @@ public class NetworkHelperImpl implements NetworkHelper {
     CapacityManager capacityMgr;
     @Inject
     VpcDao vpcDao;
+    @Inject
+    VpcManager vpcManager;
 
     protected final Map<HypervisorType, ConfigKey<String>> hypervisorsMap = new HashMap<>();
 
@@ -276,6 +279,10 @@ public class NetworkHelperImpl implements NetworkHelper {
         _itMgr.expunge(router.getUuid());
         _routerHealthCheckResultDao.expungeHealthChecks(router.getId());
         _routerDao.remove(router.getId());
+
+        if (router.getVpcId() != null) {
+            vpcManager.reconfigStaticNatForVpcVr(router.getVpcId());
+        }
         return router;
     }
 
@@ -773,7 +780,7 @@ public class NetworkHelperImpl implements NetworkHelper {
             logger.debug("Adding nic for Virtual Router in Guest network " + guestNetwork);
             String defaultNetworkStartIp = null, defaultNetworkStartIpv6 = null;
             final Nic placeholder = _networkModel.getPlaceholderNicForRouter(guestNetwork, routerDeploymentDefinition.getPodId());
-            if (!routerDeploymentDefinition.isPublicNetwork()) {
+            if (!routerDeploymentDefinition.isPublicNetwork() || !vpcManager.isSrcNatIpRequiredForVpcVr(routerDeploymentDefinition.getVpc().getVpcOfferingId())) {
                 if (guestNetwork.getCidr() != null) {
                     if (placeholder != null && placeholder.getIPv4Address() != null) {
                         logger.debug("Requesting ipv4 address " + placeholder.getIPv4Address() + " stored in placeholder nic for the network "
diff --git a/server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java b/server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java
index 399019db3e2..649689d8d77 100644
--- a/server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java
+++ b/server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java
@@ -120,7 +120,9 @@ public class NicProfileHelperImpl implements NicProfileHelper {
     public NicProfile createGuestNicProfileForVpcRouter(final RouterDeploymentDefinition vpcRouterDeploymentDefinition, final Network guestNetwork) {
         final NicProfile guestNic = new NicProfile();
 
-        if (BroadcastDomainType.NSX == guestNetwork.getBroadcastDomainType()) {
+        if (BroadcastDomainType.NSX == guestNetwork.getBroadcastDomainType() ||
+                BroadcastDomainType.Netris == guestNetwork.getBroadcastDomainType() ||
+                !_vpcMgr.isSrcNatIpRequiredForVpcVr(vpcRouterDeploymentDefinition.getVpc().getVpcOfferingId())) {
             NicVO vrNic = _nicDao.findByNetworkIdAndTypeIncludingRemoved(guestNetwork.getId(), VirtualMachine.Type.DomainRouter);
             if (vrNic != null) {
                 guestNic.setIPv4Address(vrNic.getIPv4Address());
diff --git a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
index 8f07fcfca3b..7e263eaed84 100644
--- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
@@ -204,6 +204,7 @@ import com.cloud.network.rules.StaticNatImpl;
 import com.cloud.network.rules.StaticNatRule;
 import com.cloud.network.rules.dao.PortForwardingRulesDao;
 import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.VpcManager;
 import com.cloud.network.vpc.VpcService;
 import com.cloud.network.vpc.dao.VpcDao;
 import com.cloud.network.vpn.Site2SiteVpnManager;
@@ -336,6 +337,7 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
 
     @Inject private NetworkService networkService;
     @Inject private VpcService vpcService;
+    @Inject private VpcManager vpcManager;
 
     @Autowired
     @Qualifier("networkHelper")
@@ -2039,6 +2041,12 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
             if (_disableRpFilter) {
                 rpFilter = " disable_rp_filter=true";
             }
+            Vpc vpc = vpcManager.getActiveVpc(router.getVpcId());
+            if (vpcManager.isSrcNatIpRequiredForVpcVr(vpc.getVpcOfferingId())) {
+                buf.append(" has_public_network=true");
+            } else {
+                buf.append(" has_public_network=false");
+            }
         } else if (!publicNetwork) {
             type = "dhcpsrvr";
         } else {
diff --git a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
index bb517eed524..df3fa1d547b 100644
--- a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
@@ -82,10 +82,9 @@ import com.cloud.network.vpc.NetworkACLManager;
 import com.cloud.network.vpc.PrivateGateway;
 import com.cloud.network.vpc.PrivateIpAddress;
 import com.cloud.network.vpc.PrivateIpVO;
-import com.cloud.network.vpc.StaticRoute;
+import com.cloud.network.vpc.StaticRouteVO;
 import com.cloud.network.vpc.StaticRouteProfile;
 import com.cloud.network.vpc.Vpc;
-import com.cloud.network.vpc.VpcGateway;
 import com.cloud.network.vpc.VpcManager;
 import com.cloud.network.vpc.VpcVO;
 import com.cloud.network.vpc.dao.PrivateIpDao;
@@ -321,17 +320,19 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian
                 String defaultDns2 = null;
                 String defaultIp6Dns1 = null;
                 String defaultIp6Dns2 = null;
+                boolean isDnsConfigured = false;
                 // remove public and guest nics as we will plug them later
                 final Iterator<NicProfile> it = profile.getNics().iterator();
                 while (it.hasNext()) {
                     final NicProfile nic = it.next();
                     if (nic.getTrafficType() == TrafficType.Public || nic.getTrafficType() == TrafficType.Guest) {
                         // save dns information
-                        if (nic.getTrafficType() == TrafficType.Public) {
+                        if (nic.getTrafficType() == TrafficType.Public || !isDnsConfigured) {
                             defaultDns1 = nic.getIPv4Dns1();
                             defaultDns2 = nic.getIPv4Dns2();
                             defaultIp6Dns1 = nic.getIPv6Dns1();
                             defaultIp6Dns2 = nic.getIPv6Dns2();
+                            isDnsConfigured = true;
                         }
                         logger.debug("Removing nic " + nic + " of type " + nic.getTrafficType() + " from the nics passed on vm start. " + "The nic will be plugged later");
                         it.remove();
@@ -532,17 +533,8 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian
             }
 
             // 4) RE-APPLY ALL STATIC ROUTE RULES
-            final List<? extends StaticRoute> routes = _staticRouteDao.listByVpcId(domainRouterVO.getVpcId());
-            final List<StaticRouteProfile> staticRouteProfiles = new ArrayList<StaticRouteProfile>(routes.size());
-            final Map<Long, VpcGateway> gatewayMap = new HashMap<Long, VpcGateway>();
-            for (final StaticRoute route : routes) {
-                VpcGateway gateway = gatewayMap.get(route.getVpcGatewayId());
-                if (gateway == null) {
-                    gateway = _entityMgr.findById(VpcGateway.class, route.getVpcGatewayId());
-                    gatewayMap.put(gateway.getId(), gateway);
-                }
-                staticRouteProfiles.add(new StaticRouteProfile(route, gateway));
-            }
+            final List<StaticRouteVO> routes = _vpcMgr.getVpcStaticRoutes(domainRouterVO.getVpcId());
+            final List<StaticRouteProfile> staticRouteProfiles = _vpcMgr.getVpcStaticRoutes(routes);
 
             logger.debug("Found " + staticRouteProfiles.size() + " static routes to apply as a part of vpc route " + domainRouterVO + " start");
             if (!staticRouteProfiles.isEmpty()) {
diff --git a/server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java b/server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java
index 55f7609f9c6..1678015dc4e 100644
--- a/server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java
@@ -58,6 +58,8 @@ import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.LoadBalancerVMMapDao;
 import com.cloud.network.dao.LoadBalancerVMMapVO;
+import com.cloud.network.dao.RemoteAccessVpnDao;
+import com.cloud.network.dao.Site2SiteVpnGatewayDao;
 import com.cloud.network.rules.FirewallRule.FirewallRuleType;
 import com.cloud.network.rules.FirewallRule.Purpose;
 import com.cloud.network.rules.FirewallRule.State;
@@ -153,6 +155,10 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
     VpcService _vpcSvc;
     @Inject
     VMTemplateDao _templateDao;
+    @Inject
+    RemoteAccessVpnDao remoteAccessVpnDao;
+    @Inject
+    Site2SiteVpnGatewayDao site2SiteVpnGatewayDao;
 
     protected void checkIpAndUserVm(IpAddress ipAddress, UserVm userVm, Account caller, Boolean ignoreVmState) {
         if (ipAddress == null || ipAddress.getAllocatedTime() == null || ipAddress.getAllocatedToAccountId() == null) {
@@ -1262,6 +1268,25 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
             throw ex;
         }
 
+        if (ipAddress.isForRouter()) {
+            if (remoteAccessVpnDao.findByPublicIpAddress(ipAddress.getId()) != null) {
+                InvalidParameterValueException ex = new InvalidParameterValueException("Can't disable static nat as the IP address is used by a Remote Access VPN");
+                ex.addProxyObject(ipAddress.getUuid(), "ipId");
+                throw ex;
+
+            }
+            if (site2SiteVpnGatewayDao.findByPublicIpAddress(ipAddress.getId()) != null) {
+                InvalidParameterValueException ex = new InvalidParameterValueException("Can't disable static nat as the IP address is used by a VPC gateway");
+                ex.addProxyObject(ipAddress.getUuid(), "ipId");
+                throw ex;
+
+            }
+            if (disableStaticNat(ipId, caller, ctx.getCallingUserId(), false)) {
+                return true;
+            }
+            return false;
+        }
+
         Long vmId = ipAddress.getAssociatedWithVmId();
         if (vmId == null) {
             InvalidParameterValueException ex = new InvalidParameterValueException("Specified IP address id is not associated with any vm Id");
@@ -1294,7 +1319,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
 
         IPAddressVO ipAddress = _ipAddressDao.findById(ipId);
         checkIpAndUserVm(ipAddress, null, caller, false);
-        long networkId = ipAddress.getAssociatedWithNetworkId();
+        Long networkId = ipAddress.getAssociatedWithNetworkId();
 
         if (!ipAddress.isOneToOneNat()) {
             InvalidParameterValueException ex = new InvalidParameterValueException("One to one nat is not enabled for the specified ip id");
@@ -1329,11 +1354,14 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
             ipAddress.setAssociatedWithVmId(null);
             ipAddress.setRuleState(null);
             ipAddress.setVmIp(null);
+            ipAddress.setForRouter(false);
             if (isIpSystem && !releaseIpIfElastic) {
                 ipAddress.setSystem(false);
             }
             _ipAddressDao.update(ipAddress.getId(), ipAddress);
-            _vpcMgr.unassignIPFromVpcNetwork(ipAddress.getId(), networkId);
+            if (networkId != null) {
+                _vpcMgr.unassignIPFromVpcNetwork(ipAddress.getId(), networkId);
+            }
 
             if (isIpSystem && releaseIpIfElastic && !_ipAddrMgr.handleSystemIpRelease(ipAddress)) {
                 logger.warn("Failed to release system ip address " + ipAddress);
@@ -1371,7 +1399,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
         return new StaticNatRuleImpl(ruleVO, dstIp);
     }
 
-    protected boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) {
+    @Override
+    public boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) {
         IpAddress sourceIp = _ipAddressDao.findById(sourceIpId);
 
         List<StaticNat> staticNats = createStaticNatForIp(sourceIp, caller, forRevoke);
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index aef3f06f28d..db90aef30c8 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -48,11 +48,22 @@ import com.cloud.bgp.BGPService;
 import com.cloud.dc.ASNumberVO;
 import com.cloud.dc.dao.ASNumberDao;
 import com.cloud.dc.Vlan;
+import com.cloud.network.RemoteAccessVpn;
+import com.cloud.network.Site2SiteVpnConnection;
 import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NsxProviderDao;
+import com.cloud.network.dao.RemoteAccessVpnDao;
+import com.cloud.network.dao.RemoteAccessVpnVO;
+import com.cloud.network.dao.Site2SiteCustomerGatewayDao;
+import com.cloud.network.dao.Site2SiteCustomerGatewayVO;
+import com.cloud.network.dao.Site2SiteVpnConnectionDao;
+import com.cloud.network.dao.Site2SiteVpnConnectionVO;
 import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.element.NsxProviderVO;
+import com.cloud.network.rules.RulesManager;
+import com.cloud.network.vpn.RemoteAccessVpnService;
 import com.cloud.resourcelimit.CheckedReservation;
+import com.cloud.vm.dao.VMInstanceDao;
 import com.google.common.collect.Sets;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.alert.AlertService;
@@ -296,6 +307,20 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
     private NetrisProviderDao netrisProviderDao;
     @Inject
     RoutedIpv4Manager routedIpv4Manager;
+    @Inject
+    DomainRouterDao domainRouterDao;
+    @Inject
+    RulesManager rulesManager;
+    @Inject
+    VMInstanceDao vmInstanceDao;
+    @Inject
+    RemoteAccessVpnDao remoteAccessVpnDao;
+    @Inject
+    RemoteAccessVpnService remoteAccessVpnMgr;
+    @Inject
+    Site2SiteVpnConnectionDao site2SiteVpnConnectionDao;
+    @Inject
+    Site2SiteCustomerGatewayDao site2SiteCustomerGatewayDao;
 
     private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker"));
     private List<VpcProvider> vpcElements = null;
@@ -461,7 +486,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                     final Map<Service, Set<Provider>> svcProviderMap = new HashMap<>();
                     final Set<Provider> defaultProviders = Set.of(Provider.Netris);
                     for (final Service svc : getSupportedServices()) {
-                        if (List.of(Service.UserData, Service.Dhcp, Service.Dns).contains(svc)) {
+                        if (List.of(Service.UserData, Service.Dhcp, Service.Dns, Service.Vpn).contains(svc)) {
                             final Set<Provider> userDataProvider = Set.of(Provider.VPCVirtualRouter);
                             svcProviderMap.put(svc, userDataProvider);
                         } else {
@@ -2225,6 +2250,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         logger.debug("Cleaning up existed site to site VPN gateways");
         _s2sVpnMgr.cleanupVpnGatewayByVpc(vpcId);
 
+        List<RemoteAccessVpnVO> vpns = remoteAccessVpnDao.listByVpcId(vpcId);
+        for (RemoteAccessVpnVO vpn : vpns) {
+            logger.debug("Disabling remote access VPN on {}", vpn.getServerAddressId());
+            remoteAccessVpnMgr.destroyRemoteAccessVpnForIp(vpn.getServerAddressId(), caller, true);
+        }
+
         // 2) release all ip addresses
         final List<IPAddressVO> ipsToRelease = _ipAddressDao.listByAssociatedVpc(vpcId, null);
         logger.debug("Releasing ips for vpc id=" + vpcId + " as a part of vpc cleanup");
@@ -2360,6 +2391,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                     restartRequired = true;
                     return false;
                 }
+                reconfigStaticNatForVpcVr(vpcId);
                 return true;
             }
 
@@ -2867,28 +2899,38 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
     @Override
     public boolean applyStaticRoutesForVpc(final long vpcId) throws ResourceUnavailableException {
         final Account caller = CallContext.current().getCallingAccount();
-        final List<? extends StaticRoute> routes = _staticRouteDao.listByVpcId(vpcId);
+        final List<StaticRouteVO> routes = getVpcStaticRoutes(vpcId);
         return applyStaticRoutes(routes, caller, true);
     }
 
-    protected boolean applyStaticRoutes(final List<? extends StaticRoute> routes, final Account caller, final boolean updateRoutesInDB) throws ResourceUnavailableException {
-        final boolean success = true;
-        final List<StaticRouteProfile> staticRouteProfiles = new ArrayList<StaticRouteProfile>(routes.size());
-        final Map<Long, VpcGateway> gatewayMap = new HashMap<Long, VpcGateway>();
-        for (final StaticRoute route : routes) {
-            VpcGateway gateway = gatewayMap.get(route.getVpcGatewayId());
-            if (gateway == null) {
-                gateway = _vpcGatewayDao.findById(route.getVpcGatewayId());
-                gatewayMap.put(gateway.getId(), gateway);
+    @Override
+    public boolean applyStaticRouteForVpcVpnIfNeeded(final Long vpcId, boolean updateAllVpn) throws ResourceUnavailableException {
+        if (isProviderSupportServiceInVpc(vpcId, Service.Vpn, Network.Provider.VPCVirtualRouter)) {
+            boolean isVpcVRSourceNat = isProviderSupportServiceInVpc(vpcId, Service.SourceNat, Network.Provider.VPCVirtualRouter);
+            if (isVpcVRSourceNat) {
+                logger.debug("Skipping static route configuration as VPC VR is Source NAT");
+                return true;
             }
-            staticRouteProfiles.add(new StaticRouteProfile(route, gateway));
+            logger.debug("Configuring static route for VPC VR of VPC " + vpcId);
+            final Account caller = CallContext.current().getCallingAccount();
+            final List<StaticRouteVO> routes = getVpcStaticRoutes(vpcId, updateAllVpn);
+            return applyStaticRoutes(routes, caller, false);
         }
+        return true;
+    }
+
+    protected boolean applyStaticRoutes(final List<StaticRouteVO> routes, final Account caller, final boolean updateRoutesInDB) throws ResourceUnavailableException {
+        final boolean success = true;
+        final List<StaticRouteProfile> staticRouteProfiles = getVpcStaticRoutes(routes);
         if (!applyStaticRoutes(staticRouteProfiles)) {
             logger.warn("Routes are not completely applied");
             return false;
         } else {
             if (updateRoutesInDB) {
-                for (final StaticRoute route : routes) {
+                for (final StaticRouteVO route : routes) {
+                    if (route.isForVpn()) {
+                        continue;
+                    }
                     if (route.getState() == StaticRoute.State.Revoke) {
                         _staticRouteDao.remove(route.getId());
                         logger.debug("Removed route " + route + " from the DB");
@@ -2951,7 +2993,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
     @DB
     protected boolean revokeStaticRoutesForVpc(final long vpcId, final Account caller) throws ResourceUnavailableException {
         // get all static routes for the vpc
-        final List<StaticRouteVO> routes = _staticRouteDao.listByVpcId(vpcId);
+        final List<StaticRouteVO> routes = getVpcStaticRoutes(vpcId);
         logger.debug("Found " + routes.size() + " to revoke for the vpc " + vpcId);
         if (!routes.isEmpty()) {
             // mark all of them as revoke
@@ -2972,23 +3014,46 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
     @Override
     @DB
     @ActionEvent(eventType = EventTypes.EVENT_STATIC_ROUTE_CREATE, eventDescription = "creating static route", create = true)
-    public StaticRoute createStaticRoute(final long gatewayId, final String cidr) throws NetworkRuleConflictException {
+    public StaticRoute createStaticRoute(final Long gatewayId, Long vpcId, final String nextHop, final String cidr) throws NetworkRuleConflictException {
         final Account caller = CallContext.current().getCallingAccount();
 
         // parameters validation
-        final VpcGateway gateway = _vpcGatewayDao.findById(gatewayId);
-        if (gateway == null) {
-            throw new InvalidParameterValueException("Invalid gateway id is given");
+        if (gatewayId == null && nextHop == null) {
+            throw new InvalidParameterValueException("one of gatewayId and nextHop must be specified");
+        }
+
+        if (gatewayId != null && nextHop != null) {
+            throw new InvalidParameterValueException("Only one of gatewayId and nextHop can be specified");
         }
 
-        if (gateway.getState() != VpcGateway.State.Ready) {
-            throw new InvalidParameterValueException("Gateway is not in the " + VpcGateway.State.Ready + " state: " + gateway.getState());
+        if (gatewayId != null) {
+            final VpcGateway gateway = _vpcGatewayDao.findById(gatewayId);
+            if (gateway == null) {
+                throw new InvalidParameterValueException("Invalid gateway id is given");
+            }
+
+            if (gateway.getState() != VpcGateway.State.Ready) {
+                throw new InvalidParameterValueException("Gateway is not in the " + VpcGateway.State.Ready + " state: " + gateway.getState());
+            }
+
+            if (vpcId != null) {
+                if (!vpcId.equals(gateway.getVpcId())) {
+                    throw new InvalidParameterValueException("Invalid gateway id is given");
+                }
+            } else {
+                vpcId = gateway.getVpcId();
+            }
+        } else if (nextHop != null) {
+            if (vpcId == null) {
+                throw new InvalidParameterValueException("vpcId must be specified");
+            }
         }
 
-        final Vpc vpc = getActiveVpc(gateway.getVpcId());
+        final Vpc vpc = getActiveVpc(vpcId);
         if (vpc == null) {
             throw new InvalidParameterValueException("Can't add static route to VPC that is being deleted");
         }
+
         _accountMgr.checkAccess(caller, null, false, vpc);
 
         if (!NetUtils.isValidIp4Cidr(cidr)) {
@@ -3002,7 +3067,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         }
 
         // 2) CIDR should be outside of link-local cidr
-        if (NetUtils.isNetworksOverlap(vpc.getCidr(), NetUtils.getLinkLocalCIDR())) {
+        if (NetUtils.isNetworksOverlap(cidr, NetUtils.getLinkLocalCIDR())) {
             throw new InvalidParameterValueException("CIDR should be outside of link local cidr " + NetUtils.getLinkLocalCIDR());
         }
 
@@ -3011,10 +3076,15 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
             throw new InvalidParameterValueException("The static gateway cidr overlaps with one of the denied routes of the zone the VPC belongs to");
         }
 
+        // 4) validate next hop
+        if (nextHop != null && !isNextHopValid(nextHop, vpc)) {
+            throw new InvalidParameterValueException(String.format("Next hop %s is invalid. It must be within VPC CIDR or on the same public or private network", nextHop));
+        }
+
         return Transaction.execute(new TransactionCallbackWithException<StaticRouteVO, NetworkRuleConflictException>() {
             @Override
             public StaticRouteVO doInTransaction(final TransactionStatus status) throws NetworkRuleConflictException {
-                StaticRouteVO newRoute = new StaticRouteVO(gateway.getId(), cidr, vpc.getId(), vpc.getAccountId(), vpc.getDomainId());
+                StaticRouteVO newRoute = new StaticRouteVO(gatewayId, cidr, vpc.getId(), vpc.getAccountId(), vpc.getDomainId(), nextHop);
                 logger.debug("Adding static route " + newRoute);
                 newRoute = _staticRouteDao.persist(newRoute);
 
@@ -3030,8 +3100,46 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         });
     }
 
+    private boolean isNextHopValid(String nextHop, Vpc vpc) {
+        // Scenario 1: VM as next hop
+        if (NetUtils.isIpWithInCidrRange(nextHop, vpc.getCidr())) {
+            logger.debug("The next Hop {} is valid as it is within the VPC cidr {}", nextHop, vpc.getCidr());
+            return true;
+        }
+        // Scenario 2: Another public IP as next hop
+        List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpc.getId(), null);
+        List<Long> vlanIds = new ArrayList<>();
+        for (IPAddressVO ip : ips) {
+            if (vlanIds.contains(ip.getVlanId())) {
+                continue;
+            }
+            VlanVO vlan = _vlanDao.findById(ip.getVlanId());
+            if (vlan != null) {
+                String vlanCidr = NetUtils.getCidrFromGatewayAndNetmask(vlan.getVlanGateway(), vlan.getVlanNetmask());
+                if (NetUtils.isIpWithInCidrRange(nextHop, vlanCidr)) {
+                    logger.debug("The next Hop {} is valid as it is on the same network as Public IP address {} ", nextHop, ip.getAddress());
+                    return true;
+                }
+            }
+            vlanIds.add(ip.getVlanId());
+        }
+
+        // Scenario 3: An IP on private gateway as next hop
+        List<VpcGatewayVO> vpcGateways = _vpcGatewayDao.listByVpcId(vpc.getId());
+        for (VpcGatewayVO vpcGateway : vpcGateways) {
+            String vpcGatewayCidr = NetUtils.getCidrFromGatewayAndNetmask(vpcGateway.getGateway(), vpcGateway.getNetmask());
+            if (NetUtils.isIpWithInCidrRange(nextHop, vpcGatewayCidr)) {
+                logger.debug("The next Hop {} is valid as it is on the same network as private gateway {} ", nextHop, vpcGateway.getIp4Address());
+                return true;
+            }
+        }
+
+        logger.debug("The next Hop {} is invalid", nextHop);
+        return false;
+    }
+
     protected boolean isCidrDenylisted(final String cidr, final long zoneId) {
-        final String routesStr = NetworkOrchestrationService.GuestDomainSuffix.valueIn(zoneId);
+        final String routesStr = NetworkOrchestrationService.DeniedRoutes.valueIn(zoneId);
         if (routesStr != null && !routesStr.isEmpty()) {
             final String[] cidrDenyList = routesStr.split(",");
 
@@ -3435,6 +3543,15 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                 && vpcOffSvcProvidersMap.get(Service.Gateway).contains(Network.Provider.VPCVirtualRouter));
     }
 
+     @Override
+     public boolean isSrcNatIpRequiredForVpcVr(long vpcOfferingId) {
+         final Map<Network.Service, Set<Network.Provider>> vpcOffSvcProvidersMap = getVpcOffSvcProvidersMap(vpcOfferingId);
+         return (Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.SourceNat))
+                 && vpcOffSvcProvidersMap.get(Network.Service.SourceNat).contains(Network.Provider.VPCVirtualRouter))
+                 || (Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.Gateway))
+                 && vpcOffSvcProvidersMap.get(Service.Gateway).contains(Network.Provider.VPCVirtualRouter));
+     }
+
     /**
      * rollingRestartVpc performs restart of routers of a VPC by first
      * deploying a new VR and then destroying old VRs in rolling fashion. For
@@ -3524,4 +3641,241 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
     protected boolean isDefaultAcl(long aclId) {
         return aclId == NetworkACL.DEFAULT_ALLOW || aclId == NetworkACL.DEFAULT_DENY;
     }
+
+    @Override
+    public List<StaticRouteProfile> getVpcStaticRoutes(final List<? extends StaticRoute> routes) {
+        final List<StaticRouteProfile> staticRouteProfiles = new ArrayList<>(routes.size());
+        final Map<Long, VpcGateway> gatewayMap = new HashMap<Long, VpcGateway>();
+        for (final StaticRoute route : routes) {
+            if (route.getVpcGatewayId() != null) {
+                VpcGateway gateway = gatewayMap.get(route.getVpcGatewayId());
+                if (gateway == null) {
+                    gateway = _entityMgr.findById(VpcGateway.class, route.getVpcGatewayId());
+                    gatewayMap.put(gateway.getId(), gateway);
+                }
+                staticRouteProfiles.add(new StaticRouteProfile(route, gateway));
+            } else {
+                staticRouteProfiles.add(new StaticRouteProfile(route));
+            }
+        }
+        return staticRouteProfiles;
+    }
+
+    @Override
+    public List<StaticRouteVO> getVpcStaticRoutes(Long vpcId) {
+        return getVpcStaticRoutes(vpcId, false);
+    }
+
+    public List<StaticRouteVO> getVpcStaticRoutes(Long vpcId, boolean updateAllVpn) {
+        final List<StaticRouteVO> routes = _staticRouteDao.listByVpcId(vpcId);
+
+        if (isProviderSupportServiceInVpc(vpcId, Service.Vpn, Network.Provider.VPCVirtualRouter)
+                && !isProviderSupportServiceInVpc(vpcId, Service.SourceNat, Network.Provider.VPCVirtualRouter)) {
+
+            Vpc vpc = vpcDao.findById(vpcId);
+            IPAddressVO ipAddressForVpcVR = getIpAddressForVpcVr(vpc, null, false);
+            String nextHop = getFirstGuestIpAddressForVpcVr(vpc.getId());
+
+            if (ipAddressForVpcVR != null && (updateAllVpn || nextHop != null)) {
+                // Add Static Routes for Remote Access VPN
+                List<StaticRouteVO> staticRoutesForRemoteAccessVpn = new ArrayList<>();
+                RemoteAccessVpnVO remoteAccessVpn = remoteAccessVpnDao.findByPublicIpAddress(ipAddressForVpcVR.getId());
+                if (remoteAccessVpn != null) {
+                    String ipRange = remoteAccessVpn.getIpRange();
+                    String startIp = ipRange.split("-")[0];
+                    String endIp = ipRange.split("-")[1];
+                    int cidrSize = NetUtils.getBigCidrSizeOfIpRange(NetUtils.ip2Long(startIp), NetUtils.ip2Long(endIp));
+                    String cidr = NetUtils.transformCidr(startIp + "/" + cidrSize);
+                    if (nextHop == null || RemoteAccessVpn.State.Removed.equals(remoteAccessVpn.getState())) {
+                        StaticRouteVO newRoute = new StaticRouteVO(cidr, vpc.getId(), vpc.getAccountId(), vpc.getDomainId(), null,
+                                StaticRoute.State.Revoke, true);
+                        staticRoutesForRemoteAccessVpn.add(newRoute);
+                    } else {
+                        StaticRoute.State state = updateAllVpn ? StaticRoute.State.Update : StaticRoute.State.Add;
+                        StaticRouteVO newRoute = new StaticRouteVO(cidr, vpc.getId(), vpc.getAccountId(), vpc.getDomainId(), nextHop,
+                                state, true);
+                        staticRoutesForRemoteAccessVpn.add(newRoute);
+                    }
+                }
+                logger.debug("Adding {} static routes for Remote Access VPN", staticRoutesForRemoteAccessVpn.size());
+                routes.addAll(staticRoutesForRemoteAccessVpn);
+
+                // Add Static Routes for Site-to-Site VPN connections
+                List<StaticRouteVO> staticRoutesForSite2SiteVpn = new ArrayList<>();
+                List<Site2SiteVpnConnectionVO> vpnConnections = site2SiteVpnConnectionDao.listByVpcId(vpcId);
+                for (Site2SiteVpnConnectionVO vpnConnection : vpnConnections) {
+                    Site2SiteCustomerGatewayVO customerGateway = site2SiteCustomerGatewayDao.findById(vpnConnection.getCustomerGatewayId());
+                    if (nextHop == null || Site2SiteVpnConnection.State.Removed.equals(vpnConnection.getState())) {
+                        for (String cidr: customerGateway.getGuestCidrList().split(",")) {
+                            StaticRouteVO newRoute = new StaticRouteVO(cidr, vpc.getId(), vpc.getAccountId(), vpc.getDomainId(), null,
+                                    StaticRoute.State.Revoke, true);
+                            staticRoutesForSite2SiteVpn.add(newRoute);
+                        }
+                    } else {
+                        StaticRoute.State state = updateAllVpn ? StaticRoute.State.Update : StaticRoute.State.Add;
+                        for (String cidr : customerGateway.getGuestCidrList().split(",")) {
+                            StaticRouteVO newRoute = new StaticRouteVO(cidr, vpc.getId(), vpc.getAccountId(), vpc.getDomainId(), nextHop,
+                                    state, true);
+                            staticRoutesForSite2SiteVpn.add(newRoute);
+                        }
+                    }
+                }
+                logger.debug("Adding {} static routes for {} Site-to-Site VPN connections",
+                        staticRoutesForSite2SiteVpn.size(), vpnConnections.size());
+                routes.addAll(staticRoutesForSite2SiteVpn);
+            }
+        }
+
+        logger.debug("Found {} static routes for VPC {}", routes.size(), vpcId);
+        return routes;
+    }
+
+    @Override
+    public boolean isProviderSupportServiceInVpc(long vpcId, Service service, Provider provider) {
+        return _vpcSrvcDao.canProviderSupportServiceInVpc(vpcId, service, provider);
+    }
+
+    @Override
+    public IPAddressVO getIpAddressForVpcVr(Vpc vpc, IPAddressVO ipAddress, boolean allocateIpIfNeeded) {
+        // Validate if the IP address is associated to a VPC VR
+        final List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpc.getId(), null);
+        IPAddressVO ipAddressForVR = ips.stream().filter(ip -> ip.isForRouter()).findFirst().orElse(null);
+        if (ipAddressForVR != null) {
+            if (ipAddress != null && ipAddressForVR.getId() != ipAddress.getId()) {
+                throw new InvalidParameterValueException(String.format("Cannot assign Public IP %s to VPC VR as %s has been associated to the VPC VR.",
+                        ipAddress.getAddress().addr(), ipAddressForVR.getAddress().addr()));
+            }
+            return ipAddressForVR;
+        } else if (ipAddress != null) {
+            return ipAddress;
+        }
+
+        if (allocateIpIfNeeded) {
+            Account account = _accountMgr.getAccount(vpc.getAccountId());
+            DataCenter zone = _dcDao.findById(vpc.getZoneId());
+            try {
+                IpAddress ip = _ipAddrMgr.allocateIp(account, false, CallContext.current().getCallingAccount(),
+                        CallContext.current().getCallingUserId(), zone, null, null);
+                this.associateIPToVpc(ip.getId(), vpc.getId());
+                return _ipAddressDao.findById(ip.getId());
+            } catch (InsufficientAddressCapacityException | ResourceAllocationException |
+                     ResourceUnavailableException ex) {
+                throw new InvalidParameterValueException("Cannot assign Public IP to VPC VR: " + ex.getMessage());
+            }
+        } else {
+            return null;
+        }
+    }
+
+    /* This method configures the Static Nat for VPC VR if it is used for VPN but not Source NAT.
+     * (1) Update forRouter to true and one-to-one to true
+     * (2) Get current network and router ID/IP
+     * (3) Get new network and router ID/IP
+     * (4) If network or IP is changed (in case VPC tier is removed or shutdown), disable/apply Static NAT with new VM ID and VM IP
+     * (5) otherwise, If VPC VR ID does not exist or is changed, update the VM ID.
+     * (6) otherwise, do nothing
+     *
+     * This is used in the following processes
+     * (1) create remote access VPN
+     * (2) create S2S VPN
+     * (3) destroy Router
+     * (4) restart Vpc with cleanup
+     * (5) add VPC tier
+     * (6) delete VPC tier
+     * (7) remove VPC
+     */
+
+    @Override
+    public boolean configStaticNatForVpcVr(Vpc vpc, IPAddressVO ipAddress) {
+        logger.debug("Configuring static nat for VPC VR of VPC " + vpc.getId());
+        // (1) Update forRouter to true and one-to-one to true
+        if (!ipAddress.isForRouter()) {
+            ipAddress.setForRouter(true);
+            ipAddress.setOneToOneNat(true);
+            _ipAddressDao.update(ipAddress.getId(), ipAddress);
+        }
+
+        // (2) Get current network and router ID/IP
+        Long currentNetworkId = ipAddress.getAssociatedWithNetworkId();
+        Long currentRouterId = ipAddress.getAssociatedWithVmId();
+        String currentRouterIp = ipAddress.getVmIp();
+
+        // (3) Get new network and router ID/IP
+        Long newNetworkId = null;
+        Long newRouterId = null;
+        String newRouterIp = null;
+        List<NetworkVO> networks = _ntwkDao.listByVpc(vpc.getId());
+        for (NetworkVO network : networks) {
+            NicVO newNic = nicDao.findNonPlaceHolderByNetworkIdAndType(network.getId(), VirtualMachine.Type.DomainRouter);
+            if (newNic != null) {
+                logger.debug("Got VPC VR NIC for network {}: {}", network.getId(), newNic);
+                newNetworkId = network.getId();
+                newRouterId = newNic.getInstanceId();
+                newRouterIp = newNic.getIPv4Address();
+                break;
+            }
+        }
+
+        // Do nothing if the current and new network and router are Null
+        if (ObjectUtils.allNull(currentNetworkId, currentRouterId, newNetworkId, newRouterId)) {
+            logger.debug("The current and new network and router are Null, do nothing");
+            return true;
+        }
+
+        if (currentNetworkId == null || !currentNetworkId.equals(newNetworkId)) {
+            // (4) If network or IP is changed (in case VPC tier is removed or shutdown), disable/apply Static NAT with new VM ID and VM IP
+            if (currentNetworkId != null) {
+                // Disable Static NAT for current VPC VR
+                logger.debug("Disabling static nat for VPC VR (network: {}, router: {})", currentNetworkId, currentRouterId);
+                CallContext ctx = CallContext.current();
+                if (!rulesManager.applyStaticNatForIp(ipAddress.getId(), false, ctx.getCallingAccount(),true)) {
+                    throw new CloudRuntimeException("Failed to disable static nat for VPC VR");
+                }
+                ipAddress.setAssociatedWithNetworkId(null);
+                ipAddress.setAssociatedWithVmId(null);
+                ipAddress.setVmIp(null);
+                _ipAddressDao.update(ipAddress.getId(), ipAddress);
+            }
+            if (newNetworkId != null) {
+                // Enable static nat for the new VPC VR
+                logger.debug("Enabling static nat for VPC VR  (network: {}, router: {})", newNetworkId, newRouterId);
+                ipAddress.setAssociatedWithNetworkId(newNetworkId);
+                ipAddress.setAssociatedWithVmId(newRouterId);
+                ipAddress.setVmIp(newRouterIp);
+                _ipAddressDao.update(ipAddress.getId(), ipAddress);
+                CallContext ctx = CallContext.current();
+                if (!rulesManager.applyStaticNatForIp(ipAddress.getId(), false, ctx.getCallingAccount(),false)) {
+                    throw new CloudRuntimeException("Failed to enable static nat for VPC VR");
+                }
+            }
+        } else if (currentRouterId == null || !currentRouterId.equals(newRouterId)) {
+            // (5) otherwise, If VPC VR ID does not exist or is changed, update the VM ID.
+            ipAddress.setAssociatedWithVmId(newRouterId);
+            ipAddress.setVmIp(newRouterIp);
+            _ipAddressDao.update(ipAddress.getId(), ipAddress);
+        }
+        return true;
+    }
+
+    @Override
+    public void reconfigStaticNatForVpcVr(Long vpcId) {
+        Vpc vpc = vpcDao.findById(vpcId);
+        IPAddressVO ipAddressForVpcVR = getIpAddressForVpcVr(vpc, null, false);
+        if (ipAddressForVpcVR != null && !configStaticNatForVpcVr(vpc, ipAddressForVpcVR)) {
+            throw new CloudRuntimeException("Failed to reconfig static nat for VPC VR as part of the process");
+        }
+    }
+
+    private String getFirstGuestIpAddressForVpcVr(Long vpcId) {
+        String nextHop = null;
+        List<NetworkVO> networks = _ntwkDao.listByVpc(vpcId);
+        for (NetworkVO network : networks) {
+            NicVO nic = nicDao.findNonPlaceHolderByNetworkIdAndType(network.getId(), VirtualMachine.Type.DomainRouter);
+            if (nic != null) {
+                nextHop = nic.getIPv4Address();
+                break;
+            }
+        }
+        return nextHop;
+    }
 }
diff --git a/server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java
index 6cef834b0f7..9be925d36a0 100644
--- a/server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java
@@ -67,6 +67,7 @@ import com.cloud.network.rules.FirewallRule.Purpose;
 import com.cloud.network.rules.FirewallRuleVO;
 import com.cloud.network.rules.RulesManager;
 import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.VpcManager;
 import com.cloud.network.vpc.dao.VpcDao;
 import com.cloud.projects.Project.ListProjectResourcesCriteria;
 import com.cloud.server.ConfigurationServer;
@@ -124,6 +125,9 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc
     UsageEventDao _usageEventDao;
     @Inject
     ConfigurationDao _configDao;
+    @Inject
+    VpcManager vpcManager;
+
     List<RemoteAccessVPNServiceProvider> _vpnServiceProviders;
 
     @Inject
@@ -181,7 +185,11 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc
 
             Long networkId = ipAddress.getAssociatedWithNetworkId();
             if (networkId != null) {
-                _networkMgr.checkIpForService(ipAddress, Service.Vpn, null);
+                if (ipAddress.isForRouter()) {
+                    logger.debug("The IP address is reserved for Domain Router, skipping the check now");
+                } else {
+                    _networkMgr.checkIpForService(ipAddress, Service.Vpn, null);
+                }
             }
 
             final Long vpcId = ipAddress.getVpcId();
@@ -232,9 +240,11 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc
                     throw new InvalidParameterValueException("Vpn service is not supported in network id=" + ipAddr.getAssociatedWithNetworkId());
                 }
                 cidr = NetUtils.getCidr(network.getCidr());
+                validateIpAddressForVpnServiceOnNetwork(network, ipAddress);
             } else {
                 Vpc vpc = _vpcDao.findById(vpcId);
                 cidr = NetUtils.getCidr(vpc.getCidr());
+                validateIpAddressForVpnServiceOnVpc(vpc, ipAddress);
             }
 
             String[] guestIpRange = NetUtils.getIpRangeFromCidr(cidr.first(), cidr.second());
@@ -257,13 +267,62 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc
                 if (forDisplay != null) {
                     remoteAccessVpnVO.setDisplay(forDisplay);
                 }
-                return _remoteAccessVpnDao.persist(remoteAccessVpnVO);
+                remoteAccessVpnVO = _remoteAccessVpnDao.persist(remoteAccessVpnVO);
+                if (vpcId != null) {
+                    try {
+                        vpcManager.applyStaticRouteForVpcVpnIfNeeded(vpcId, false);
+                    } catch (ResourceUnavailableException | CloudRuntimeException e) {
+                        logger.error("Unable to apply static routes for vpc " + vpcId + " due to " + e.getMessage());
+                    }
+                }
+                return remoteAccessVpnVO;
             });
         } finally {
             _ipAddressDao.releaseFromLockTable(publicIpId);
         }
     }
 
+    private void validateIpAddressForVpnServiceOnNetwork(Network network, IPAddressVO ipAddress) {
+        Long networkId = network.getId();
+        if (_networkMgr.isProviderSupportServiceInNetwork(networkId, Service.Vpn, Network.Provider.VirtualRouter)) {
+            // if VR is the VPN provider,
+            // (1) if VR is Source NAT, the IP address must be used as Source NAT
+            // (2) if VR is not Source NAT, the IP address must not be used as Source NAT
+            boolean isVRSourceNat = _networkMgr.isProviderSupportServiceInNetwork(networkId, Service.SourceNat, Network.Provider.VirtualRouter);
+            if (isVRSourceNat && !ipAddress.isSourceNat()) {
+                throw new InvalidParameterValueException("Vpn service can only be configured on the Source NAT IP of network id=" + ipAddress.getAssociatedWithNetworkId());
+            }
+            if (!isVRSourceNat && ipAddress.isSourceNat()) {
+                throw new InvalidParameterValueException("Vpn service can not be configured on the Source NAT IP of network id=" + ipAddress.getAssociatedWithNetworkId());
+            }
+            if (!isVRSourceNat) {
+                throw new InvalidParameterValueException("Currently it is not supported to create Vpn service on a non-Source NAT IP of network id=" + ipAddress.getAssociatedWithNetworkId());
+            }
+        }
+    }
+
+    private void validateIpAddressForVpnServiceOnVpc(Vpc vpc, IPAddressVO ipAddress) {
+        Long vpcId = vpc.getId();
+        if (vpcManager.isProviderSupportServiceInVpc(vpcId, Service.Vpn, Network.Provider.VPCVirtualRouter)) {
+            // if VPC VR is the VPN provider,
+            // (1) if VPC VR is Source NAT, the IP address must be used as Source NAT
+            // (2) if VPC VR is not Source NAT, the IP address must not be used as Source NAT
+            boolean isVpcVRSourceNat = vpcManager.isProviderSupportServiceInVpc(vpcId, Service.SourceNat, Network.Provider.VPCVirtualRouter);
+            if (isVpcVRSourceNat && !ipAddress.isSourceNat()) {
+                throw new InvalidParameterValueException("Vpn service can only be configured on the Source NAT IP of VPC id=" + ipAddress.getVpcId());
+            }
+            if (!isVpcVRSourceNat && ipAddress.isSourceNat()) {
+                throw new InvalidParameterValueException("Vpn service can not be configured on the Source NAT IP of VPC id=" + ipAddress.getVpcId());
+            }
+            if (!isVpcVRSourceNat) {
+                IPAddressVO ipAddressForVpcVR = vpcManager.getIpAddressForVpcVr(vpc, ipAddress, true);
+                if (!vpcManager.configStaticNatForVpcVr(vpc, ipAddressForVpcVR)) {
+                    throw new CloudRuntimeException("Failed to enable static nat for VPC VR as part of remote access vpn creation");
+                }
+            }
+        }
+    }
+
     private void validateRemoteAccessVpnConfiguration() throws ConfigurationException {
         String ipRange = RemoteAccessVpnClientIpRange.value();
         if (ipRange == null) {
@@ -365,6 +424,14 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc
                         Transaction.execute(new TransactionCallbackNoReturn() {
                             @Override
                             public void doInTransactionWithoutResult(TransactionStatus status) {
+                                if (vpn.getVpcId() != null) {
+                                    try {
+                                        vpcManager.applyStaticRouteForVpcVpnIfNeeded(vpn.getVpcId(), false);
+                                    } catch (ResourceUnavailableException | CloudRuntimeException e) {
+                                        logger.error("Unable to apply static routes for vpc " + vpn.getVpcId() + " due to " + e.getMessage());
+                                    }
+                                }
+
                                 _remoteAccessVpnDao.remove(vpn.getId());
 
                                 List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId());
diff --git a/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java b/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java
index 094f81607fe..5c31bb3bbb2 100644
--- a/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java
@@ -48,6 +48,8 @@ import com.cloud.event.EventTypes;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.IpAddressManager;
+import com.cloud.network.Network;
 import com.cloud.network.Site2SiteCustomerGateway;
 import com.cloud.network.Site2SiteVpnConnection;
 import com.cloud.network.Site2SiteVpnConnection.State;
@@ -61,9 +63,12 @@ import com.cloud.network.dao.Site2SiteVpnConnectionVO;
 import com.cloud.network.dao.Site2SiteVpnGatewayDao;
 import com.cloud.network.dao.Site2SiteVpnGatewayVO;
 import com.cloud.network.element.Site2SiteVpnServiceProvider;
+import com.cloud.network.vpc.Vpc;
 import com.cloud.network.vpc.VpcManager;
 import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.VpcOfferingServiceMapVO;
 import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
 import com.cloud.projects.Project.ListProjectResourcesCriteria;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
@@ -80,6 +85,7 @@ import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.DomainRouterVO;
+import com.cloud.vm.dao.DomainRouterDao;
 
 @Component
 public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager {
@@ -100,11 +106,17 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
     @Inject
     ConfigurationDao _configDao;
     @Inject
-    VpcManager _vpcMgr;
-    @Inject
     AccountManager _accountMgr;
     @Inject
     private AnnotationDao annotationDao;
+    @Inject
+    VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
+    @Inject
+    private DomainRouterDao domainRouterDao;
+    @Inject
+    private IpAddressManager ipAddressManager;
+    @Inject
+    private VpcManager vpcManager;
 
     int _connLimit;
     int _subnetsLimit;
@@ -136,13 +148,9 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
         if (gws != null) {
             throw new InvalidParameterValueException("The VPN gateway of VPC " + vpcId + " already existed!");
         }
-        //Use source NAT ip for VPC
-        List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpcId, true);
-        if (ips.size() != 1) {
-            throw new CloudRuntimeException("Cannot found source nat ip of vpc " + vpcId);
-        }
 
-        Site2SiteVpnGatewayVO gw = new Site2SiteVpnGatewayVO(owner.getAccountId(), owner.getDomainId(), ips.get(0).getId(), vpcId);
+        IPAddressVO ipAddress = getIpAddressIdForVpn(vpcId, vpc.getVpcOfferingId());
+        Site2SiteVpnGatewayVO gw = new Site2SiteVpnGatewayVO(owner.getAccountId(), owner.getDomainId(), ipAddress.getId(), vpcId);
 
         if (cmd.getDisplay() != null) {
             gw.setDisplay(cmd.getDisplay());
@@ -152,6 +160,29 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
         return gw;
     }
 
+    private IPAddressVO getIpAddressIdForVpn(Long vpcId, Long vpcOferingId) {
+        VpcOfferingServiceMapVO mapForSourceNat = vpcOfferingServiceMapDao.findByServiceProviderAndOfferingId(Network.Service.SourceNat.getName(), Network.Provider.VPCVirtualRouter.getName(), vpcOferingId);
+        VpcOfferingServiceMapVO mapForVpn = vpcOfferingServiceMapDao.findByServiceProviderAndOfferingId(Network.Service.Vpn.getName(), Network.Provider.VPCVirtualRouter.getName(), vpcOferingId);
+        if (mapForSourceNat == null && mapForVpn != null) {
+            // Use Static NAT IP of VPC VR
+            logger.debug(String.format("The VPC VR provides %s Service, however it does not provide %s service, trying to configure using IP of VPC VR", Network.Service.Vpn.getName(), Network.Service.SourceNat.getName()));
+
+            Vpc vpc = _vpcDao.findById(vpcId);
+            IPAddressVO ipAddressForVpcVR = vpcManager.getIpAddressForVpcVr(vpc, null, true);
+            if (!vpcManager.configStaticNatForVpcVr(vpc, ipAddressForVpcVR)) {
+                throw new CloudRuntimeException("Failed to enable static nat for VPC VR as part of vpn gateway");
+            }
+            return ipAddressForVpcVR;
+        } else {
+            //Use source NAT ip for VPC
+            List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpcId, true);
+            if (ips.size() != 1) {
+                throw new CloudRuntimeException("Cannot found source nat ip of vpc " + vpcId);
+            }
+            return ips.get(0);
+        }
+    }
+
     protected void checkCustomerGatewayCidrList(String guestCidrList) {
         String[] cidrList = guestCidrList.split(",");
         if (cidrList.length > _subnetsLimit) {
@@ -272,7 +303,8 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
         String[] cidrList = customerGateway.getGuestCidrList().split(",");
 
         // Remote sub nets cannot overlap VPC's sub net
-        String vpcCidr = _vpcDao.findById(vpnGateway.getVpcId()).getCidr();
+        Vpc vpc = _vpcDao.findById(vpnGateway.getVpcId());
+        String vpcCidr = vpc.getCidr();
         for (String cidr : cidrList) {
             if (NetUtils.isNetworksOverlap(vpcCidr, cidr)) {
                 throw new InvalidParameterValueException("The subnets of customer gateway " + customerGatewayId + "'s subnet " + cidr + " is overlapped with VPC cidr " +
@@ -308,6 +340,13 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
         }
 
         _vpnConnectionDao.persist(conn);
+
+        try {
+            vpcManager.applyStaticRouteForVpcVpnIfNeeded(vpc.getId(), false);
+        } catch (ResourceUnavailableException | CloudRuntimeException e) {
+            logger.error("Unable to apply static routes for vpc " + vpc.getId() + " due to " + e.getMessage());
+        }
+
         return conn;
     }
 
@@ -584,7 +623,19 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
         if (conn.getState() != State.Pending) {
             stopVpnConnection(id);
         }
+
+        conn.setState(State.Removed);
+        _vpnConnectionDao.update(id, conn);
+
+        final Site2SiteVpnGateway vpnGateway = _vpnGatewayDao.findById(conn.getVpnGatewayId());
+        try {
+            vpcManager.applyStaticRouteForVpcVpnIfNeeded(vpnGateway.getVpcId(), false);
+        } catch (ResourceUnavailableException | CloudRuntimeException e) {
+            logger.error("Unable to apply static routes for vpc " + vpnGateway.getVpcId() + " due to " + e.getMessage());
+        }
+
         _vpnConnectionDao.remove(id);
+
         return true;
     }
 
diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
index 76efd339fa9..782734c8c8b 100644
--- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
@@ -1255,6 +1255,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
         serviceProviderMap.put(Service.Dhcp, routerProvider);
         serviceProviderMap.put(Service.Dns, routerProvider);
         serviceProviderMap.put(Service.UserData, routerProvider);
+        serviceProviderMap.put(Service.Vpn, routerProvider);
         if (forVpc) {
             serviceProviderMap.put(Service.NetworkACL, provider);
         } else {
diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py
index 21e125e3bb6..c4201b34e3b 100755
--- a/systemvm/debian/opt/cloud/bin/configure.py
+++ b/systemvm/debian/opt/cloud/bin/configure.py
@@ -1021,13 +1021,20 @@ class CsSite2SiteVpn(CsDataBag):
             local_ip = self.dbag[vpn]['local_public_ip']
             dev = CsHelper.get_device(local_ip)
 
+            if not self.config.has_public_network():
+                for interface in self.config.address().get_interfaces():
+                    if interface.is_guest() and interface.is_added():
+                        dev = interface.get_device()
+                        local_ip = interface.get_ip()
+                        break
+
             if dev == "":
                 logging.error("Request for ipsec to %s not possible because ip is not configured", local_ip)
                 continue
 
             CsHelper.start_if_stopped("ipsec")
-            self.configure_iptables(dev, self.dbag[vpn])
-            self.configure_ipsec(self.dbag[vpn])
+            self.configure_iptables(dev, local_ip, self.dbag[vpn])
+            self.configure_ipsec(local_ip, self.dbag[vpn])
 
         # Delete vpns that are no longer in the configuration
         for ip in self.confips:
@@ -1043,10 +1050,10 @@ class CsSite2SiteVpn(CsDataBag):
         os.remove(vpnsecretsfile)
         CsHelper.execute("ipsec reload")
 
-    def configure_iptables(self, dev, obj):
-        self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])])
-        self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 4500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])])
-        self.fw.append(["", "front", "-A INPUT -i %s -p esp -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])])
+    def configure_iptables(self, dev, local_ip, obj):
+        self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], local_ip)])
+        self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 4500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], local_ip)])
+        self.fw.append(["", "front", "-A INPUT -i %s -p esp -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], local_ip)])
         self.fw.append(["nat", "front", "-A POSTROUTING -t nat -o %s -m mark --mark 0x525 -j ACCEPT" % dev])
         for net in obj['peer_guest_cidr_list'].lstrip().rstrip().split(','):
             self.fw.append(["mangle", "front",
@@ -1058,7 +1065,7 @@ class CsSite2SiteVpn(CsDataBag):
             self.fw.append(["mangle", "",
                             "-A INPUT -s %s -d %s -j MARK --set-xmark 0x524/0xffffffff" % (net, obj['local_guest_cidr'])])
 
-    def configure_ipsec(self, obj):
+    def configure_ipsec(self, local_ip, obj):
         leftpeer = obj['local_public_ip']
         rightpeer = obj['peer_gateway_ip']
         peerlist = obj['peer_guest_cidr_list'].replace(' ', '')
@@ -1080,7 +1087,8 @@ class CsSite2SiteVpn(CsDataBag):
         file.repopulate()  # This avoids issues when switching off split_connections or removing subnets with split_connections == true
         file.add("#conn for vpn-%s" % rightpeer, 0)
         file.search("conn ", "conn vpn-%s" % rightpeer)
-        file.addeq(" left=%s" % leftpeer)
+        file.addeq(" left=%s" % local_ip)
+        file.addeq(" leftid=%s" % leftpeer)
         file.addeq(" leftsubnet=%s" % obj['local_guest_cidr'])
         file.addeq(" right=%s" % rightpeer)
         file.addeq(" rightsubnet=%s" % peerlist)
@@ -1100,7 +1108,7 @@ class CsSite2SiteVpn(CsDataBag):
             file.addeq(" dpddelay=30")
             file.addeq(" dpdtimeout=120")
             file.addeq(" dpdaction=restart")
-        if splitconnections and peerlistarr.count > 1:
+        if splitconnections and len(peerlistarr) > 1:
             logging.debug('Splitting connections for rightsubnets %s' % peerlistarr)
             for peeridx in range(1, len(peerlistarr)):
                 logging.debug('Adding split connection -%d for subnet %s' % (peeridx + 1, peerlistarr[peeridx]))
@@ -1221,9 +1229,17 @@ class CsRemoteAccessVpn(CsDataBag):
                 logging.debug("Enabling remote access vpn on " + public_ip)
 
                 CsHelper.start_if_stopped("ipsec")
-                self.configure_l2tpIpsec(public_ip, self.dbag[public_ip])
+
                 logging.debug("Remote accessvpn  data bag %s",  self.dbag)
-                self.remoteaccessvpn_iptables(public_ip, self.dbag[public_ip])
+                if not self.config.has_public_network():
+                    for interface in self.config.address().get_interfaces():
+                        if interface.is_guest() and interface.is_added():
+                            self.configure_l2tpIpsec(interface.get_ip(), self.dbag[public_ip])
+                            self.remoteaccessvpn_iptables(interface.get_device(), interface.get_ip(), self.dbag[public_ip])
+                            break
+                else:
+                    self.configure_l2tpIpsec(public_ip, self.dbag[public_ip])
+                    self.remoteaccessvpn_iptables(self.dbag['public_interface'], public_ip, self.dbag[public_ip])
 
                 CsHelper.execute("ipsec update")
                 CsHelper.execute("systemctl start xl2tpd")
@@ -1248,6 +1264,7 @@ class CsRemoteAccessVpn(CsDataBag):
         # Left
         l2tpfile = CsFile(l2tpconffile)
         l2tpfile.addeq(" left=%s" % left)
+        l2tpfile.addeq(" leftid=%s" % obj['vpn_server_ip'])
         l2tpfile.commit()
 
         secret = CsFile(vpnsecretfilte)
@@ -1264,8 +1281,7 @@ class CsRemoteAccessVpn(CsDataBag):
         xl2tpoptions.search("ms-dns ", "ms-dns %s" % localip)
         xl2tpoptions.commit()
 
-    def remoteaccessvpn_iptables(self, publicip, obj):
-        publicdev = obj['public_interface']
+    def remoteaccessvpn_iptables(self, publicdev, publicip, obj):
         localcidr = obj['local_cidr']
         local_ip = obj['local_ip']
 
@@ -1640,7 +1656,7 @@ def main(argv):
                                ("dhcp",                {"process_iptables": False, "executor": [CsDhcp("dhcpentry", config)]}),
                                ("load_balancer",       {"process_iptables": True,  "executor": []}),
                                ("monitor_service",     {"process_iptables": False, "executor": [CsMonitor("monitorservice", config)]}),
-                               ("static_routes",       {"process_iptables": False, "executor": [CsStaticRoutes("staticroutes", config)]})
+                               ("static_routes",       {"process_iptables": True, "executor": [CsStaticRoutes("staticroutes", config)]})
                                ])
 
     if not config.is_vpc():
diff --git a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py
index 0d1c31ac14b..864c6fb504c 100755
--- a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py
+++ b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py
@@ -343,7 +343,7 @@ class CsIP:
 
             interfaces = [CsInterface(address, self.config)]
             CsHelper.reconfigure_interfaces(self.cl, interfaces)
-            if self.get_type() in ['public'] and not self.config.is_routed():
+            if self.get_type() in ['public'] and not self.config.is_routed() and self.config.has_public_network():
                 self.set_mark()
 
             if 'gateway' in self.address:
@@ -360,7 +360,14 @@ class CsIP:
             # The code looks redundant here, but we actually have to cater for routers and
             # VPC routers in a different manner. Please do not remove this block otherwise
             # The VPC default route will be broken.
-            if self.get_type() in ["public"] and address["device"] == CsHelper.PUBLIC_INTERFACES[self.cl.get_type()]:
+            if not self.config.has_public_network():
+                for interface in self.config.address().get_interfaces():
+                    if interface.is_guest() and interface.is_added() and interface.get_device() == self.dev:
+                        gateway = interface.get_gateway()
+                        route.add_defaultroute(gateway)
+                        CsHelper.execute("sudo echo 0 > /proc/sys/net/ipv4/conf/%s/rp_filter" % interface.get_device())
+                        break
+            elif self.get_type() in ["public"] and address["device"] == CsHelper.PUBLIC_INTERFACES[self.cl.get_type()]:
                 gateway = str(address["gateway"])
                 route.add_defaultroute(gateway)
         else:
@@ -426,7 +433,7 @@ class CsIP:
         self.fw.append(["filter", "", "-P FORWARD DROP"])
 
     def fw_router(self):
-        if self.config.is_vpc() or self.config.is_routed():
+        if self.config.is_vpc() or self.config.is_routed() or self.config.is_dhcp():
             return
 
         self.fw.append(["mangle", "front", "-A PREROUTING " +
@@ -523,20 +530,24 @@ class CsIP:
             self.fw.append(["mangle", "front", "-A PREROUTING " +
                             " -i %s -m state --state RELATED,ESTABLISHED " % self.dev +
                             "-j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff"])
+
             guestNetworkCidr = self.address['network']
-            self.fw.append(["filter", "", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" %
-                            (guestNetworkCidr, self.dev, self.dev)])
-            self.fw.append(
-                ["filter", "front", "-A ACL_INBOUND_%s -d 224.0.0.18/32 -j ACCEPT" % self.dev])
-            self.fw.append(
-                ["filter", "front", "-A ACL_INBOUND_%s -d 225.0.0.50/32 -j ACCEPT" % self.dev])
-            self.fw.append(
-                ["filter", "", "-A ACL_INBOUND_%s -j DROP" % self.dev])
+            if self.config.has_public_network():
+                self.fw.append(["filter", "", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" %
+                                (guestNetworkCidr, self.dev, self.dev)])
+                self.fw.append(
+                    ["filter", "front", "-A ACL_INBOUND_%s -d 224.0.0.18/32 -j ACCEPT" % self.dev])
+                self.fw.append(
+                    ["filter", "front", "-A ACL_INBOUND_%s -d 225.0.0.50/32 -j ACCEPT" % self.dev])
+                self.fw.append(
+                    ["filter", "", "-A ACL_INBOUND_%s -j DROP" % self.dev])
+                self.fw.append(
+                    ["mangle", "front", "-A ACL_OUTBOUND_%s -d 225.0.0.50/32 -j ACCEPT" % self.dev])
+                self.fw.append(
+                    ["mangle", "front", "-A ACL_OUTBOUND_%s -d 224.0.0.18/32 -j ACCEPT" % self.dev])
+            else:
+                self.fw.append(["filter", "", "-A FORWARD -d %s -o %s -j ACCEPT" % (guestNetworkCidr, self.dev)])
 
-            self.fw.append(
-                ["mangle", "front", "-A ACL_OUTBOUND_%s -d 225.0.0.50/32 -j ACCEPT" % self.dev])
-            self.fw.append(
-                ["mangle", "front", "-A ACL_OUTBOUND_%s -d 224.0.0.18/32 -j ACCEPT" % self.dev])
             self.fw.append(
                 ["filter", "", "-A INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % self.dev])
             self.fw.append(
@@ -551,9 +562,11 @@ class CsIP:
                 ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 443 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
             self.fw.append(
                 ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 8080 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
-            self.fw.append(["mangle", "",
-                            "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j ACL_OUTBOUND_%s" %
-                            (self.dev, guestNetworkCidr, self.address['gateway'], self.dev)])
+
+            if self.config.has_public_network():
+                self.fw.append(["mangle", "",
+                                "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j ACL_OUTBOUND_%s" %
+                                (self.dev, guestNetworkCidr, self.address['gateway'], self.dev)])
 
         if self.is_private_gateway():
             self.fw.append(["filter", "", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" %
@@ -686,6 +699,47 @@ class CsIP:
             self.nft_ipv4_acl.append({'type': "", 'chain': 'FORWARD',
                                       'rule': "iifname %s oifname %s ct state related,established counter accept" % (self.dev, self.dev)})
 
+    def fw_dhcpserver(self):
+        if not self.config.is_dhcp():
+            return
+
+        self.fw.append(["mangle", "front",
+                        "-A POSTROUTING " +
+                        "-p udp -m udp --dport 68 -j CHECKSUM --checksum-fill"])
+
+        self.fw.append(["filter", "", "-A INPUT -d 224.0.0.18/32 -j ACCEPT"])
+        self.fw.append(["filter", "", "-A INPUT -d 225.0.0.50/32 -j ACCEPT"])
+        self.fw.append(["filter", "", "-A INPUT -i %s -m state --state RELATED,ESTABLISHED -j ACCEPT" %
+                        self.dev])
+        self.fw.append(["filter", "", "-A INPUT -p icmp -j ACCEPT"])
+        self.fw.append(["filter", "", "-A INPUT -i lo -j ACCEPT"])
+
+        if self.config.is_vpc():
+            self.fw.append(
+                ["filter", "", "-A INPUT -i eth0 -p tcp -m tcp --dport 3922 -m state --state NEW,ESTABLISHED -j ACCEPT"])
+        else:
+            self.fw.append(
+                ["filter", "", "-A INPUT -i eth1 -p tcp -m tcp --dport 3922 -m state --state NEW,ESTABLISHED -j ACCEPT"])
+
+        if self.get_type() in ["guest"]:
+            guestNetworkCidr = self.address['network']
+            self.fw.append(
+                ["filter", "", "-A INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % self.dev])
+            self.fw.append(
+                ["filter", "", "-A INPUT -i %s -p udp -m udp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)])
+            self.fw.append(
+                ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)])
+            self.fw.append(
+                ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 80 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
+            self.fw.append(
+                ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 443 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
+            self.fw.append(
+                ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 8080 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)])
+            self.fw.append(
+                ["filter", "", "-A FORWARD -i %s -o %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % (self.dev, self.dev)])
+            self.fw.append(
+                ["filter", "", "-A FORWARD -i %s -o %s -m state --state NEW -j ACCEPT" % (self.dev, self.dev)])
+
 
     def post_config_change(self, method):
         route = CsRoute()
@@ -735,6 +789,7 @@ class CsIP:
         self.fw_vpcrouter()
         self.fw_router_routing()
         self.fw_vpcrouter_routing()
+        self.fw_dhcpserver()
 
         cmdline = self.config.cmdline()
 
diff --git a/systemvm/debian/opt/cloud/bin/cs/CsConfig.py b/systemvm/debian/opt/cloud/bin/cs/CsConfig.py
index a17f6ac4aa5..33a78fc03e1 100755
--- a/systemvm/debian/opt/cloud/bin/cs/CsConfig.py
+++ b/systemvm/debian/opt/cloud/bin/cs/CsConfig.py
@@ -148,3 +148,6 @@ class CsConfig(object):
             return 'mangle'
         else:
             return ""
+
+    def has_public_network(self):
+        return self.cmdline().idata().get('has_public_network', 'true') == 'true'
diff --git a/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py b/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py
index 46259dcb356..29a4318a2a9 100755
--- a/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py
+++ b/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py
@@ -142,9 +142,9 @@ class CsDhcp(CsDataBag):
             else:
                 listen_address.append(ip)
             # Add localized "data-server" records in /etc/hosts for VPC routers
-            if self.config.is_vpc() or self.config.is_router():
+            if (self.config.is_vpc() and gn.is_vr_guest_gateway()) or self.config.is_router():
                 self.add_host(gateway, "%s data-server" % CsHelper.get_hostname())
-            elif self.config.is_dhcp():
+            elif self.config.is_dhcp() or (self.config.is_vpc() and not gn.is_vr_guest_gateway()):
                 self.add_host(ip, "%s data-server" % CsHelper.get_hostname())
             idx += 1
 
diff --git a/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py b/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py
index 615c61d98e3..db9ad7701d7 100755
--- a/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py
+++ b/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py
@@ -35,13 +35,19 @@ class CsGuestNetwork:
     def is_guestnetwork(self):
         return self.guest
 
+    def is_vr_guest_gateway(self):
+        return self.guest and ('is_vr_guest_gateway' not in self.data or self.data['is_vr_guest_gateway'])
+
     def get_dns(self):
         if not self.guest:
             return self.config.get_dns()
 
         dns = []
-        if 'router_guest_gateway' in self.data and not self.config.use_extdns() and ('is_vr_guest_gateway' not in self.data or not self.data['is_vr_guest_gateway']):
-            dns.append(self.data['router_guest_gateway'])
+        if not self.config.use_extdns():
+            if 'router_guest_gateway' in self.data and self.is_vr_guest_gateway():
+                dns.append(self.data['router_guest_gateway'])
+            elif 'router_guest_ip' in self.data and not self.is_vr_guest_gateway():
+                dns.append(self.data['router_guest_ip'])
 
         if 'dns' in self.data:
             dns.extend(self.data['dns'].split(','))
diff --git a/systemvm/debian/opt/cloud/bin/passwd_server_ip.py b/systemvm/debian/opt/cloud/bin/passwd_server_ip.py
index 8051951a18f..bf1eab2db02 100755
--- a/systemvm/debian/opt/cloud/bin/passwd_server_ip.py
+++ b/systemvm/debian/opt/cloud/bin/passwd_server_ip.py
@@ -28,6 +28,7 @@
 import binascii
 import cgi
 import os
+import socketserver
 import sys
 import syslog
 import threading
@@ -97,9 +98,17 @@ def removePassword(ip):
             del passMap[ip]
 
 
-class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
-    pass
+class CloudStackPasswordServer(socketserver.TCPServer):
+    allow_reuse_address = 1
+    def server_bind(self):
+        """Override server_bind to store the server name."""
+        socketserver.TCPServer.server_bind(self)
+        host, port = self.server_address[:2]
+        self.server_name = host
+        self.server_port = port
 
+class ThreadedHTTPServer(ThreadingMixIn, CloudStackPasswordServer):
+    pass
 
 class PasswordRequestHandler(BaseHTTPRequestHandler):
     server_version = 'CloudStack Password Server'
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 16c364f4280..9331cac80b2 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1547,6 +1547,7 @@
 "label.newinstance": "New Instance",
 "label.newname": "New name",
 "label.next": "Next",
+"label.nexthop": "Next hop",
 "label.nfs": "NFS",
 "label.nfsmountopts": "NFS mount options",
 "label.nfsserver": "NFS server",
@@ -2083,7 +2084,7 @@
 "label.simplified.chinese.keyboard": "Simplified Chinese keyboard",
 "label.site": "Netris Site",
 "label.site.to.site.vpn": "Site-to-site VPN",
-"label.site.to.site.vpn.connections": "Site-to-site VPN Connections",
+"label.site.to.site.vpn.connections": "VPN Connections",
 "label.size": "Size",
 "label.sizegb": "Size",
 "label.smb.domain": "SMB domain",
@@ -2546,6 +2547,7 @@
 "label.volumetype": "Volume Type",
 "label.vpc": "VPC",
 "label.vpcs": "VPCs",
+"label.vpc.gateway.ip": "VPC Gateway IP",
 "label.vpc.id": "VPC ID",
 "label.vpc.offerings": "VPC offerings",
 "label.vpc.virtual.router": "VPC virtual router",
@@ -3027,7 +3029,7 @@
 "message.enable.vpn": "Please confirm that you want remote access VPN enabled for this IP address.",
 "message.enable.vpn.failed": "Failed to enable VPN.",
 "message.enable.vpn.processing": "Enabling VPN...",
-"message.enabled.vpn": "Your remote access VPN is currently enabled and can be accessed via the IP.",
+"message.enabled.vpn": "Your remote access VPN is currently enabled and can be accessed via the IP",
 "message.enabled.vpn.ip.sec": "Your IPSec pre-shared key is",
 "message.enabling.security.group.provider": "Enabling security group provider",
 "message.enter.valid.nic.ip": "Please enter a valid IP address for NIC",
diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue
index 06775d8efaf..58f0aa4a918 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -443,7 +443,7 @@
           <div class="resource-detail-item__label">{{ $t('label.vmname') }}</div>
           <div class="resource-detail-item__details">
             <desktop-outlined />
-            <router-link :to="{ path: createPathBasedOnVmType(resource.vmtype, resource.virtualmachineid) }">{{ resource.vmname || resource.vm || resource.virtualmachinename || resource.virtualmachineid }} </router-link>
+            <router-link :to="{ path: createPathBasedOnVmType(resource.vmtype || resource.virtualmachinetype, resource.virtualmachineid) }">{{ resource.vmname || resource.vm || resource.virtualmachinename || resource.virtualmachineid }} </router-link>
             <status class="status status--end" :text="resource.vmstate" v-if="resource.vmstate"/>
           </div>
         </div>
diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js
index 79eae34f537..8464b789509 100644
--- a/ui/src/config/section/network.js
+++ b/ui/src/config/section/network.js
@@ -794,7 +794,7 @@ export default {
       }, {
         name: 'vpn',
         component: shallowRef(defineAsyncComponent(() => import('@/views/network/VpnDetails.vue'))),
-        show: (record) => { return record.issourcenat }
+        show: (record) => { return record.issourcenat || record.virtualmachinetype === 'DomainRouter' || !record.hasrules }
       },
       {
         name: 'events',
@@ -1001,7 +1001,6 @@ export default {
       title: 'label.site.to.site.vpn.connections',
       docHelp: 'adminguide/networking_and_traffic.html#setting-up-a-site-to-site-vpn-connection',
       icon: 'sync-outlined',
-      hidden: true,
       permission: ['listVpnConnections'],
       columns: ['publicip', 'state', 'gateway', 'ipsecpsk', 'ikepolicy', 'esppolicy'],
       details: ['publicip', 'gateway', 'passive', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'esppolicy', 'ikelifetime', 'ikeversion', 'esplifetime', 'dpd', 'splitconnections', 'forceencap', 'created'],
diff --git a/ui/src/views/network/PublicIpResource.vue b/ui/src/views/network/PublicIpResource.vue
index 03b56fab2ee..6855f6e1738 100644
--- a/ui/src/views/network/PublicIpResource.vue
+++ b/ui/src/views/network/PublicIpResource.vue
@@ -134,21 +134,21 @@ export default {
         this.tabs = this.defaultTabs
         return
       }
-      // VPC IPs with source nat have only VPN
-      if (this.resource && this.resource.vpcid && this.resource.issourcenat) {
-        this.tabs = this.defaultTabs.concat(this.$route.meta.tabs.filter(tab => tab.name === 'vpn'))
-        return
-      }
-      // VPC IPs with vpnenabled have only VPN
-      if (this.resource && this.resource.vpcid && this.resource.vpnenabled) {
-        this.tabs = this.defaultTabs.concat(this.$route.meta.tabs.filter(tab => tab.name === 'vpn'))
-        return
-      }
-      // VPC IPs with static nat have nothing
-      if (this.resource && this.resource.vpcid && this.resource.isstaticnat) {
-        return
-      }
       if (this.resource && this.resource.vpcid) {
+        // VPC IPs with source nat have only VPN
+        if (this.resource.issourcenat) {
+          this.tabs = this.defaultTabs.concat(this.$route.meta.tabs.filter(tab => tab.name === 'vpn'))
+          return
+        }
+
+        // VPC IPs with static nat have nothing
+        if (this.resource.isstaticnat) {
+          if (this.resource.virtualmachinetype === 'DomainRouter') {
+            this.tabs = this.defaultTabs.concat(this.$route.meta.tabs.filter(tab => tab.name === 'vpn'))
+          }
+          return
+        }
+
         // VPC IPs don't have firewall
         let tabs = this.$route.meta.tabs.filter(tab => tab.name !== 'firewall')
 
@@ -194,6 +194,9 @@ export default {
       this.actions = this.$route.meta.actions || []
     },
     fetchNetwork () {
+      if (!this.resource.associatednetworkid) {
+        return null
+      }
       return new Promise((resolve, reject) => {
         api('listNetworks', {
           listAll: true,
diff --git a/ui/src/views/network/StaticRoutesTab.vue b/ui/src/views/network/StaticRoutesTab.vue
index 0c960244902..bae5fcce168 100644
--- a/ui/src/views/network/StaticRoutesTab.vue
+++ b/ui/src/views/network/StaticRoutesTab.vue
@@ -17,29 +17,44 @@
 
 <template>
   <a-spin :spinning="componentLoading">
-    <div class="new-route" v-ctrl-enter="handleAdd">
-      <a-input v-model:value="newRoute" :placeholder="$t('label.cidr.destination.network')" v-focus="true"></a-input>
+    <div class="form" v-ctrl-enter="handleAdd">
+      <div class="form__label">
+        <a-input v-model:value="newRoute" :placeholder="$t('label.cidr.destination.network')" v-focus="true"></a-input>
+      </div>
+      <div class="form__label" v-if="this.$route.fullPath.startsWith('/vpc')">
+        <div :span="24" class="form__label">via</div>
+      </div>
+      <div class="form__label" v-if="this.$route.fullPath.startsWith('/vpc')">
+        <a-input v-model:value="nexthop" :placeholder="$t('label.nexthop')"></a-input>
+      </div>
       <a-button type="primary" :disabled="!('createStaticRoute' in $store.getters.apis)" @click="handleAdd">{{ $t('label.add.route') }}</a-button>
     </div>
 
-    <div class="list">
-      <div v-for="(route, index) in routes" :key="index" class="list__item">
-        <div class="list__col">
-          <div class="list__label">{{ $t('label.cidr.destination.network') }}</div>
-          <div>{{ route.cidr }}</div>
-        </div>
-        <div class="actions">
-          <tooltip-button :tooltip="$t('label.edit.tags')" icon="tag-outlined" @onClick="() => openTagsModal(route)" />
+    <a-divider/>
+    <a-table
+      size="small"
+      style="overflow-y: auto"
+      :loading="loading"
+      :columns="columns"
+      :dataSource="routes"
+      :pagination="false"
+      :rowKey="record => record.id">
+      <template #bodyCell="{ column, text, record }">
+        <template v-if="column.key === 'vpcgatewayip'">
+          <router-link :to="{ path: '/privategw/' + record.vpcgatewayid }" >{{ text }}</router-link>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button :tooltip="$t('label.edit.tags')" icon="tag-outlined" @onClick="() => openTagsModal(record)" />
           <tooltip-button
             :tooltip="$t('label.delete')"
             :disabled="!('deleteStaticRoute' in $store.getters.apis)"
             icon="delete-outlined"
             type="primary"
             :danger="true"
-            @onClick="() => handleDelete(route)" />
-        </div>
-      </div>
-    </div>
+            @onClick="() => handleDelete(record)" />
+        </template>
+      </template>
+    </a-table>
 
     <a-modal
       :title="$t('label.edit.tags')"
@@ -90,10 +105,12 @@
 import { ref, reactive, toRaw } from 'vue'
 import { api } from '@/api'
 import TooltipButton from '@/components/widgets/TooltipButton'
+import TooltipLabel from '@/components/widgets/TooltipLabel.vue'
 
 export default {
   name: 'StaticRoutesTab',
   components: {
+    TooltipLabel,
     TooltipButton
   },
   props: {
@@ -114,7 +131,27 @@ export default {
       tagsModalVisible: false,
       tags: [],
       tagsLoading: false,
-      newRoute: null
+      newRoute: null,
+      nexthop: null,
+      columns: [
+        {
+          title: this.$t('label.cidr.destination.network'),
+          dataIndex: 'cidr'
+        },
+        {
+          title: this.$t('label.vpc.gateway.ip'),
+          key: 'vpcgatewayip',
+          dataIndex: 'vpcgatewayip'
+        },
+        {
+          title: this.$t('label.nexthop'),
+          dataIndex: 'nexthop'
+        },
+        {
+          title: this.$t('label.actions'),
+          key: 'actions'
+        }
+      ]
     }
   },
   created () {
@@ -141,10 +178,15 @@ export default {
     },
     fetchData () {
       this.componentLoading = true
-      api('listStaticRoutes', {
-        gatewayid: this.resource.id,
-        listall: true
-      }).then(json => {
+      var params = {
+        listAll: true
+      }
+      if (this.$route.fullPath.startsWith('/vpc')) {
+        params.vpcid = this.resource.id
+      } else {
+        params.gatewayid = this.resource.id
+      }
+      api('listStaticRoutes', params).then(json => {
         this.routes = json.liststaticroutesresponse.staticroute
       }).catch(error => {
         this.$notifyError(error)
@@ -157,10 +199,18 @@ export default {
       if (!this.newRoute) return
 
       this.componentLoading = true
-      api('createStaticRoute', {
-        cidr: this.newRoute,
-        gatewayid: this.resource.id
-      }).then(response => {
+      var params = {
+        cidr: this.newRoute
+      }
+      if (this.$route.fullPath.startsWith('/vpc')) {
+        params.vpcid = this.resource.id
+        if (this.nexthop) {
+          params.nexthop = this.nexthop
+        }
+      } else {
+        params.gatewayid = this.resource.id
+      }
+      api('createStaticRoute', params).then(response => {
         this.$pollJob({
           jobId: response.createstaticrouteresponse.jobid,
           title: this.$t('message.success.add.static.route'),
@@ -304,7 +354,6 @@ export default {
     },
     openTagsModal (route) {
       this.selectedRule = route
-      this.rulesRef.value.resetFields()
       this.fetchTags(this.selectedRule)
       this.tagsModalVisible = true
     }
@@ -368,20 +417,38 @@ export default {
     margin-left: auto;
   }
 
-  .new-route {
+  .form {
     display: flex;
-    padding-top: 10px;
+    margin-right: -20px;
+    margin-bottom: 20px;
+    flex-direction: column;
+    align-items: flex-start;
 
-    input {
-      margin-right: 10px;
+    @media (min-width: 760px) {
+      flex-direction: row;
     }
 
-    button {
-      &:not(:last-child) {
-        margin-right: 10px;
+    &__item {
+      display: flex;
+      flex-direction: column;
+      flex: 1;
+      padding-right: 20px;
+      margin-bottom: 20px;
+
+      @media (min-width: 760px) {
+        margin-bottom: 0;
       }
+
+      input,
+      .ant-select {
+        margin-top: auto;
+      }
+
     }
 
+    &__label {
+      font-size: 18px;
+      font-weight: bold;
+    }
   }
-
 </style>
diff --git a/ui/src/views/network/VpcTab.vue b/ui/src/views/network/VpcTab.vue
index 9090654b183..b645f7118ba 100644
--- a/ui/src/views/network/VpcTab.vue
+++ b/ui/src/views/network/VpcTab.vue
@@ -360,6 +360,9 @@
           </a-spin>
         </a-modal>
       </a-tab-pane>
+      <a-tab-pane :tab="$t('label.static.routes')" key="staticroutes">
+        <StaticRoutesTab :resource="resource" :loading="loading" />
+      </a-tab-pane>
       <a-tab-pane :tab="$t('label.virtual.routers')" key="vr" v-if="$store.getters.userInfo.roletype === 'Admin'">
         <RoutersTab :resource="resource" :loading="loading" />
       </a-tab-pane>
@@ -393,6 +396,7 @@ import EventsTab from '@/components/view/EventsTab'
 import AnnotationsTab from '@/components/view/AnnotationsTab'
 import ResourceIcon from '@/components/view/ResourceIcon'
 import BgpPeersTab from '@/views/infra/zone/BgpPeersTab.vue'
+import StaticRoutesTab from './StaticRoutesTab'
 
 export default {
   name: 'VpcTab',
@@ -404,6 +408,7 @@ export default {
     RoutersTab,
     VpcTiersTab,
     VnfAppliancesTab,
+    StaticRoutesTab,
     EventsTab,
     AnnotationsTab,
     ResourceIcon
diff --git a/ui/src/views/network/VpcTiersTab.vue b/ui/src/views/network/VpcTiersTab.vue
index 2f194e60784..0d07f898d4f 100644
--- a/ui/src/views/network/VpcTiersTab.vue
+++ b/ui/src/views/network/VpcTiersTab.vue
@@ -637,7 +637,7 @@ export default {
           }
         }
         this.networkOfferings = filteredOfferings
-        if (this.isNsxEnabled || ['netris', 'nsx'].includes(this.zoneExtNetProvider.toLowerCase())) {
+        if (this.isNsxEnabled || (this.zoneExtNetProvider && ['netris', 'nsx'].includes(this.zoneExtNetProvider.toLowerCase()))) {
           this.networkOfferings = this.networkOfferings.filter(offering => offering.networkmode === (this.isOfferingNatMode ? 'NATTED' : 'ROUTED'))
         }
         if (this.resource.asnumberid) {
diff --git a/ui/src/views/offering/AddNetworkOffering.vue b/ui/src/views/offering/AddNetworkOffering.vue
index 713c1b805c1..686232e8257 100644
--- a/ui/src/views/offering/AddNetworkOffering.vue
+++ b/ui/src/views/offering/AddNetworkOffering.vue
@@ -464,7 +464,7 @@
         <a-form-item
           name="conservemode"
           ref="conservemode"
-          v-if="(guestType === 'shared' || guestType === 'isolated') && !isVpcVirtualRouterForAtLeastOneService &&
+          v-if="(guestType === 'shared' || guestType === 'isolated') &&
           (form.provider !== 'NSX' && form.provider !== 'Netris') && networkmode !== 'ROUTED'">
           <template #label>
             <tooltip-label :title="$t('label.conservemode')" :tooltip="apiParams.conservemode.description"/>
diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
index 7abe64b0433..f1ac1a3164e 100644
--- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java
+++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java
@@ -1825,6 +1825,22 @@ public class NetUtils {
         return MAX_CIDR;
     }
 
+    /**
+     Return the size of smallest CIDR which contains the IP range (startIp-endIp).
+     */
+    public static int getBigCidrSizeOfIpRange(long startIp, long endIp) {
+        assert startIp <= MAX_IPv4_ADDR : "Keep startIp smaller than or equals to " + MAX_IPv4_ADDR;
+        assert endIp <= MAX_IPv4_ADDR : "Keep endIp smaller than or equals to " + MAX_IPv4_ADDR;
+        for (int cidrSize = MAX_CIDR; cidrSize >= 1; cidrSize--) {
+            long minStartIp = startIp & (((long) 0xffffffff) >> (MAX_CIDR - cidrSize) << (MAX_CIDR - cidrSize));
+            long maxEndIp = (minStartIp | (((long) 0x1) << (MAX_CIDR - cidrSize)) - 1);
+            if (minStartIp <= startIp && maxEndIp >= endIp) {
+                return cidrSize;
+            }
+        }
+        return MAX_CIDR;
+    }
+
     /**
      Return the list of pairs (Network Address, Network cidrsize)
      */
diff --git a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
index 2f0666a39c6..13f6f245d25 100644
--- a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
@@ -932,4 +932,15 @@ public class NetUtilsTest {
         Assert.assertEquals("192.168.0.0/24", NetUtils.transformCidr("192.168.0.100/24"));
         Assert.assertEquals("10.10.10.10/32", NetUtils.transformCidr("10.10.10.10/32"));
     }
+
+    @Test
+    public void testVpnIpRange() {
+        String ipRange = "10.1.2.1-10.1.2.8";
+        String startIp = ipRange.split("-")[0];
+        String endIp = ipRange.split("-")[1];
+        int cidrSize = NetUtils.getBigCidrSizeOfIpRange(NetUtils.ip2Long(startIp), NetUtils.ip2Long(endIp));
+        Assert.assertEquals(28, cidrSize);
+        String cidr = NetUtils.transformCidr(startIp + "/" + cidrSize);
+        Assert.assertEquals("10.1.2.0/28", cidr);
+    }
 }


(cloudstack) 18/44: Phase4 - Add support for Source NAT, Static NAT and Port Forwarding (#19)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit ce9cbb2ff886c1fcb327d48d367104bf5eec5a4f
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Tue Dec 3 19:35:38 2024 -0500

    Phase4 - Add support for Source NAT, Static NAT and Port Forwarding (#19)
    
    * Run moodifyvxlan script if broadcast domain type is Netris
    
    * Add Netris NAT offerings
    
    * Add support to add Source nat rules for Natted offering
    
    * fix api params while creating Netris source NAT rule
    
    * Add support to add and delete source nat rule on netris
    
    * Add support to create /32 NAT subnet
    
    * Add support to add and delete Static NAT rules in Netris (#23)
    
    * Add support to add and delete Static NAT rules in Netris
    
    * fix static nat creation on netris & removal of subnet on deletion of static nat rule
    
    * remove nat subnet after deltion of the static nat rule
    
    * add check to see if subnet already exists and add license header
    
    * Add port forwarding rules as DNAT rules in Netris (#24)
    
    * Add port forwarding rules as DNAT rules in Netris
    
    * Fixes
    
    * Allow removing DNAT rules
    
    * Fixes
    
    * Fix subnet search
    
    * Fix update SNAT only for SNAT rules
    
    * Address comments
    
    * Fix
    
    * Fix netris pom xml
    
    * Fix SNAT rule creation
    
    * Fix IP and port placements (#27)
    
    * Fix IP and port placements
    
    * fix dnat to IP for PF rules
    
    * change dnatport
    
    ---------
    
    Co-authored-by: Nicolas Vazquez <ni...@gmail.com>
---
 .../com/cloud/network/SDNProviderNetworkRule.java  | 204 +++++++++++
 .../element/PortForwardingServiceProvider.java     |  28 ++
 .../com/cloud/network/netris/NetrisService.java    |   8 +
 .../java/com/cloud/network/vpc/VpcOffering.java    |   1 +
 .../java/com/cloud/offering/NetworkOffering.java   |   1 +
 .../engine/orchestration/NetworkOrchestrator.java  |  20 +-
 .../contrail/management/ContrailManagerImpl.java   |   4 +-
 plugins/network-elements/netris/pom.xml            |   2 +-
 .../agent/api/CreateOrUpdateNetrisNatCommand.java  | 149 ++++++++
 .../agent/api/DeleteNetrisNatRuleCommand.java      |  72 ++++
 .../apache/cloudstack/agent/api/NetrisCommand.java |  12 +-
 .../cloudstack/resource/NetrisNetworkRule.java     |  18 +-
 .../apache/cloudstack/resource/NetrisResource.java |  36 ++
 .../resource/NetrisResourceObjectUtils.java        |  16 +-
 .../apache/cloudstack/service/NetrisApiClient.java |  16 +
 .../cloudstack/service/NetrisApiClientImpl.java    | 395 ++++++++++++++++++++-
 .../apache/cloudstack/service/NetrisElement.java   | 196 +++++++++-
 .../cloudstack/service/NetrisGuestNetworkGuru.java |  18 +-
 .../service/NetrisPublicNetworkGuru.java           |   9 +
 .../cloudstack/service/NetrisServiceImpl.java      | 139 ++++++++
 .../resource/NetrisResourceObjectUtilsTest.java    |  12 +
 .../apache/cloudstack/resource/NsxNetworkRule.java | 354 +-----------------
 .../org/apache/cloudstack/service/NsxElement.java  | 158 ++++-----
 .../apache/cloudstack/service/NsxElementTest.java  |  42 +--
 .../com/cloud/network/SDNProviderOpObject.java     |   8 +-
 .../java/com/cloud/network/vpc/VpcManagerImpl.java |  18 +
 .../com/cloud/server/ConfigurationServerImpl.java  |   4 +
 27 files changed, 1415 insertions(+), 525 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java
new file mode 100644
index 00000000000..15238f85bb6
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java
@@ -0,0 +1,204 @@
+// 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 com.cloud.network;
+
+import java.util.List;
+
+public class SDNProviderNetworkRule {
+
+    private long domainId;
+    private long accountId;
+    private long zoneId;
+    private Long networkResourceId;
+    private String networkResourceName;
+    private boolean isVpcResource;
+    private long vmId;
+    private long ruleId;
+    private String publicIp;
+    private String vmIp;
+    private String publicPort;
+    private String privatePort;
+    private String protocol;
+    private String algorithm;
+    private List<String> sourceCidrList;
+    private List<String> destinationCidrList;
+    private Integer icmpCode;
+
+    private Integer icmpType;
+    private String trafficType;
+    private Network.Service service;
+
+    public long getDomainId() {
+        return domainId;
+    }
+
+    public void setDomainId(long domainId) {
+        this.domainId = domainId;
+    }
+
+    public long getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(long accountId) {
+        this.accountId = accountId;
+    }
+
+    public long getZoneId() {
+        return zoneId;
+    }
+
+    public void setZoneId(long zoneId) {
+        this.zoneId = zoneId;
+    }
+
+    public Long getNetworkResourceId() {
+        return networkResourceId;
+    }
+
+    public void setNetworkResourceId(Long networkResourceId) {
+        this.networkResourceId = networkResourceId;
+    }
+
+    public String getNetworkResourceName() {
+        return networkResourceName;
+    }
+
+    public void setNetworkResourceName(String networkResourceName) {
+        this.networkResourceName = networkResourceName;
+    }
+
+    public boolean isVpcResource() {
+        return isVpcResource;
+    }
+
+    public void setVpcResource(boolean vpcResource) {
+        isVpcResource = vpcResource;
+    }
+
+    public long getVmId() {
+        return vmId;
+    }
+
+    public void setVmId(long vmId) {
+        this.vmId = vmId;
+    }
+
+    public long getRuleId() {
+        return ruleId;
+    }
+
+    public void setRuleId(long ruleId) {
+        this.ruleId = ruleId;
+    }
+
+    public String getPublicIp() {
+        return publicIp;
+    }
+
+    public void setPublicIp(String publicIp) {
+        this.publicIp = publicIp;
+    }
+
+    public String getVmIp() {
+        return vmIp;
+    }
+
+    public void setVmIp(String vmIp) {
+        this.vmIp = vmIp;
+    }
+
+    public String getPublicPort() {
+        return publicPort;
+    }
+
+    public void setPublicPort(String publicPort) {
+        this.publicPort = publicPort;
+    }
+
+    public String getPrivatePort() {
+        return privatePort;
+    }
+
+    public void setPrivatePort(String privatePort) {
+        this.privatePort = privatePort;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public void setAlgorithm(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    public Network.Service getService() {
+        return service;
+    }
+
+    public void setService(Network.Service service) {
+        this.service = service;
+    }
+
+    public Integer getIcmpCode() {
+        return icmpCode;
+    }
+
+    public void setIcmpCode(Integer icmpCode) {
+        this.icmpCode = icmpCode;
+    }
+
+    public Integer getIcmpType() {
+        return icmpType;
+    }
+
+    public void setIcmpType(Integer icmpType) {
+        this.icmpType = icmpType;
+    }
+
+    public List<String> getSourceCidrList() {
+        return sourceCidrList;
+    }
+
+    public void setSourceCidrList(List<String> sourceCidrList) {
+        this.sourceCidrList = sourceCidrList;
+    }
+
+    public List<String> getDestinationCidrList() {
+        return destinationCidrList;
+    }
+
+    public void setDestinationCidrList(List<String> destinationCidrList) {
+        this.destinationCidrList = destinationCidrList;
+    }
+
+    public String getTrafficType() {
+        return trafficType;
+    }
+
+    public void setTrafficType(String trafficType) {
+        this.trafficType = trafficType;
+    }
+}
diff --git a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java
index e99bc2fd416..8dcc8b6d0a4 100644
--- a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java
+++ b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java
@@ -17,12 +17,40 @@
 package com.cloud.network.element;
 
 import java.util.List;
+import java.util.Objects;
 
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
 import com.cloud.network.rules.PortForwardingRule;
+import com.cloud.network.vpc.NetworkACLItem;
 
 public interface PortForwardingServiceProvider extends NetworkElement, IpDeployingRequester {
+
+    static String getPublicPortRange(PortForwardingRule rule) {
+        return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
+                String.valueOf(rule.getSourcePortStart()) :
+                String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
+    }
+
+    static String getPrivatePFPortRange(PortForwardingRule rule) {
+        return rule.getDestinationPortStart() == rule.getDestinationPortEnd() ?
+                String.valueOf(rule.getDestinationPortStart()) :
+                String.valueOf(rule.getDestinationPortStart()).concat("-").concat(String.valueOf(rule.getDestinationPortEnd()));
+    }
+
+    static String getPrivatePortRange(FirewallRule rule) {
+        return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
+                String.valueOf(rule.getSourcePortStart()) :
+                String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
+    }
+
+    static String getPrivatePortRangeForACLRule(NetworkACLItem rule) {
+        return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
+                String.valueOf(rule.getSourcePortStart()) :
+                String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
+    }
+
     /**
      * Apply rules
      * @param network
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index f6203874c2d..ea2f73383e0 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -16,6 +16,8 @@
 // under the License.
 package com.cloud.network.netris;
 
+import com.cloud.network.IpAddress;
+import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.vpc.Vpc;
 
 public interface NetrisService {
@@ -23,4 +25,10 @@ public interface NetrisService {
     boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
     boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
     boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
+    boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String sourceNatIp);
+    boolean createPortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule);
+    boolean deletePortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule);
+    boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address);
+    boolean createStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String vpcCidr, String staticNatIp, String vmIp);
+    boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp);
 }
diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
index 77d52991130..17f49bb3652 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
@@ -33,6 +33,7 @@ public interface VpcOffering extends InternalIdentity, Identity {
     public static final String DEFAULT_VPC_NAT_NSX_OFFERING_NAME = "VPC offering with NSX - NAT Mode";
     public static final String DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME = "VPC offering with NSX - Route Mode";
     public static final String DEFAULT_VPC_ROUTE_NETRIS_OFFERING_NAME = "VPC offering with Netris - Route Mode";
+    public static final String DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME = "VPC offering with Netris - NAT Mode";
 
     /**
      *
diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java
index 4df01bd8986..5000a4f8c62 100644
--- a/api/src/main/java/com/cloud/offering/NetworkOffering.java
+++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java
@@ -65,6 +65,7 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity,
     public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB = "DefaultNATNSXNetworkOfferingForVpcWithInternalLB";
     public static final String DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC = "DefaultRoutedNSXNetworkOfferingForVpc";
     public static final String DEFAULT_ROUTED_NETRIS_OFFERING_FOR_VPC = "DefaultRoutedNetrisNetworkOfferingForVpc";
+    public static final String DEFAULT_NAT_NETRIS_OFFERING_FOR_VPC = "DefaultNATNetrisNetworkOfferingForVpc";
     public static final String DEFAULT_NAT_NSX_OFFERING = "DefaultNATNSXNetworkOffering";
     public static final String DEFAULT_ROUTED_NSX_OFFERING = "DefaultRoutedNSXNetworkOffering";
     public final static String QuickCloudNoServices = "QuickCloudNoServices";
diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 1d6a9ee6946..15681b27c6f 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -561,27 +561,27 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.QuickCloudNoServices) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.QuickCloudNoServices, "Offering for QuickCloud with no services", TrafficType.Guest, null, true,
                             Availability.Optional, null, new HashMap<Network.Service, Set<Network.Provider>>(), true, Network.GuestType.Shared, false, null, true, null, true,
-                            false, null, false, null, true, false, false, false, null, null, null, true, null, null, false);
+                            false, null, false, null, true, false, false, false, false,null, null, null, true, null, null, false);
                 }
 
                 //#2 - SG enabled network offering
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOfferingWithSGService) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, "Offering for Shared Security group enabled networks",
                             TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true,
-                            null, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false);
+                            null, true, false, null, false, null, true, false, false, false, false,null, null, null, true, null, null, false);
                 }
 
                 //#3 - shared network offering with no SG service
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOffering) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOffering, "Offering for Shared networks", TrafficType.Guest, null, true,
                             Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false, null, false,
-                            null, true, false, false, false, null,null, null, true, null, null, false);
+                            null, true, false, false, false, false,null,null, null, true, null, null, false);
                 }
 
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE, "Offering for Tungsten Shared Security group enabled networks",
                             TrafficType.Guest, null, true, Availability.Optional, null, defaultTungstenSharedSGEnabledNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true,
-                            null, true, false, null, false, null, true, false, true, false, null, null,null, true, null, null, false);
+                            null, true, false, null, false, null, true, false, true, false, false,null, null,null, true, null, null, false);
                     offering.setState(NetworkOffering.State.Enabled);
                     _networkOfferingDao.update(offering.getId(), offering);
                 }
@@ -591,14 +591,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService,
                             "Offering for Isolated networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Required, null,
                             defaultIsolatedSourceNatEnabledNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null,
-                            true, false, false, false, null, null,null, true, null, null, false);
+                            true, false, false, false, false,null, null,null, true, null, null, false);
                 }
 
                 //#5 - default vpc offering with LB service
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks,
                             "Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null,
-                            defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false);
+                            defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, false,null, null, null,true, null, null, false);
                 }
 
                 //#6 - default vpc offering with no LB service
@@ -607,14 +607,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                     defaultVPCOffProviders.remove(Service.Lb);
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB,
                             "Offering for Isolated VPC networks with Source Nat service enabled and LB service disabled", TrafficType.Guest, null, false, Availability.Optional,
-                            null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false);
+                            null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, false,null, null, null,true, null, null, false);
                 }
 
                 //#7 - isolated offering with source nat disabled
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOffering) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOffering, "Offering for Isolated networks with no Source Nat service",
                             TrafficType.Guest, null, true, Availability.Optional, null, defaultIsolatedNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null,
-                            true, null, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false);
+                            true, null, true, false, null, false, null, true, false, false, false, false,null, null, null, true, null, null, false);
                 }
 
                 //#8 - network offering with internal lb service
@@ -636,7 +636,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB,
                             "Offering for Isolated VPC networks with Internal Lb support", TrafficType.Guest, null, false, Availability.Optional, null, internalLbOffProviders,
-                            true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, true, null, null, false);
+                            true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, false,null, null, null, true, null, null, false);
                     offering.setInternalLb(true);
                     offering.setPublicLb(false);
                     _networkOfferingDao.update(offering.getId(), offering);
@@ -667,7 +667,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedEIPandELBNetworkOffering) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedEIPandELBNetworkOffering,
                             "Offering for Shared networks with Elastic IP and Elastic LB capabilities", TrafficType.Guest, null, true, Availability.Optional, null,
-                            netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false);
+                            netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false, false, false,false, null, null, null, true, null, null, false);
                     offering.setDedicatedLB(false);
                     _networkOfferingDao.update(offering.getId(), offering);
                 }
diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java
index 6da2e216ff8..622886e6d83 100644
--- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java
+++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java
@@ -217,7 +217,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager
         ConfigurationManager configMgr = (ConfigurationManager) _configService;
         NetworkOfferingVO voffer = configMgr.createNetworkOffering(offeringName, offeringDisplayText,
                 TrafficType.Public, null, true, Availability.Optional, null, serviceProviderMap, true,
-                Network.GuestType.Shared, false, null, false, null, true, false, null, true, null, false, false, false, false, null, null, null, true, null, null, false);
+                Network.GuestType.Shared, false, null, false, null, true, false, null, true, null, false, false, false, false, false,null, null, null, true, null, null, false);
         long id = voffer.getId();
         _networkOfferingDao.update(id, voffer);
         return _networkOfferingDao.findById(id);
@@ -252,7 +252,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager
         ConfigurationManager configMgr = (ConfigurationManager)_configService;
         NetworkOfferingVO voffer =
                 configMgr.createNetworkOffering(offeringName, offeringDisplayText, TrafficType.Guest, null, false, Availability.Optional, null, serviceProviderMap, true,
-                        Network.GuestType.Isolated, false, null, false, null, false, true, null, true, null, false, offeringName.equals(vpcRouterOfferingName), false, false, null,  null, null, true, null, null, false);
+                        Network.GuestType.Isolated, false, null, false, null, false, true, null, true, null, false, offeringName.equals(vpcRouterOfferingName), false, false, false,null,  null, null, true, null, null, false);
         if (offeringName.equals(vpcRouterOfferingName)) {
             voffer.setInternalLb(true);
         }
diff --git a/plugins/network-elements/netris/pom.xml b/plugins/network-elements/netris/pom.xml
index 21aeadec935..a50013ee351 100644
--- a/plugins/network-elements/netris/pom.xml
+++ b/plugins/network-elements/netris/pom.xml
@@ -26,7 +26,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.20.0.0-SNAPSHOT</version>
+        <version>4.20.0.0</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisNatCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisNatCommand.java
new file mode 100644
index 00000000000..5e0546a8256
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisNatCommand.java
@@ -0,0 +1,149 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class CreateOrUpdateNetrisNatCommand extends NetrisCommand {
+    private String vpcName;
+    private Long vpcId;
+    private String vpcCidr;
+    private String natRuleName;
+    private String natIp;
+    private String natRuleType;
+
+    // Parameters for DNAT
+    private String protocol;
+    private String sourceAddress;
+    private String sourcePort;
+    private String destinationAddress;
+    private String destinationPort;
+    private String state;
+
+    // Parameters for SNAT (Static NAT)
+    private String vmIp;
+
+
+    public CreateOrUpdateNetrisNatCommand(long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, boolean isVpc, String vpcCidr) {
+        super(zoneId, accountId, domainId, vNetName, networkId, isVpc);
+        this.vpcName = vpcName;
+        this.vpcId = vpcId;
+        this.vpcCidr = vpcCidr;
+    }
+
+    public String getVpcName() {
+        return vpcName;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public String getNatIp() {
+        return natIp;
+    }
+
+    public void setNatRuleName(String natRuleName) {
+        this.natRuleName = natRuleName;
+    }
+
+    public String getNatRuleName() {
+        return natRuleName;
+    }
+
+    public String getVpcCidr() {
+        return vpcCidr;
+    }
+
+    public void setNatIp(String natIp) {
+        this.natIp = natIp;
+    }
+
+    public String getVmIp() {
+        return vmIp;
+    }
+
+    public void setVmIp(String vmIp) {
+        this.vmIp = vmIp;
+    }
+
+    public String getNatRuleType() {
+        return natRuleType;
+    }
+
+    public void setNatRuleType(String natRuleType) {
+        this.natRuleType = natRuleType;
+    }
+
+    public void setVpcName(String vpcName) {
+        this.vpcName = vpcName;
+    }
+
+    public void setVpcId(Long vpcId) {
+        this.vpcId = vpcId;
+    }
+
+    public void setVpcCidr(String vpcCidr) {
+        this.vpcCidr = vpcCidr;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public String getSourceAddress() {
+        return sourceAddress;
+    }
+
+    public void setSourceAddress(String sourceAddress) {
+        this.sourceAddress = sourceAddress;
+    }
+
+    public String getSourcePort() {
+        return sourcePort;
+    }
+
+    public void setSourcePort(String sourcePort) {
+        this.sourcePort = sourcePort;
+    }
+
+    public String getDestinationAddress() {
+        return destinationAddress;
+    }
+
+    public void setDestinationAddress(String destinationAddress) {
+        this.destinationAddress = destinationAddress;
+    }
+
+    public String getDestinationPort() {
+        return destinationPort;
+    }
+
+    public void setDestinationPort(String destinationPort) {
+        this.destinationPort = destinationPort;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public void setState(String state) {
+        this.state = state;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisNatRuleCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisNatRuleCommand.java
new file mode 100644
index 00000000000..b01b2f45192
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisNatRuleCommand.java
@@ -0,0 +1,72 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class DeleteNetrisNatRuleCommand extends NetrisCommand {
+
+    private String vpcName;
+    private Long vpcId;
+    private String natRuleType;
+    private String natRuleName;
+    private String natIp;
+
+    public DeleteNetrisNatRuleCommand(long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, boolean isVpc) {
+        super(zoneId, accountId, domainId, vNetName, networkId, isVpc);
+        this.vpcName = vpcName;
+        this.vpcId = vpcId;
+    }
+
+    public String getVpcName() {
+        return vpcName;
+    }
+
+    public void setVpcName(String vpcName) {
+        this.vpcName = vpcName;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public void setVpcId(Long vpcId) {
+        this.vpcId = vpcId;
+    }
+
+    public String getNatRuleType() {
+        return natRuleType;
+    }
+
+    public void setNatRuleType(String natRuleType) {
+        this.natRuleType = natRuleType;
+    }
+
+    public String getNatRuleName() {
+        return natRuleName;
+    }
+
+    public void setNatRuleName(String natRuleName) {
+        this.natRuleName = natRuleName;
+    }
+
+    public String getNatIp() {
+        return natIp;
+    }
+
+    public void setNatIp(String natIp) {
+        this.natIp = natIp;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
index 463001452c1..ceb33ea24ed 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
@@ -22,11 +22,11 @@ public class NetrisCommand extends Command {
     private final long zoneId;
     private final Long accountId;
     private final Long domainId;
-    private final String name;
-    private final long id;
+    private String name;
+    private final Long id;
     private final boolean isVpc;
 
-    public NetrisCommand(long zoneId, Long accountId, Long domainId, String name, long id, boolean isVpc) {
+    public NetrisCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc) {
         this.zoneId = zoneId;
         this.accountId = accountId;
         this.domainId = domainId;
@@ -40,7 +40,11 @@ public class NetrisCommand extends Command {
         return name;
     }
 
-    public long getId() {
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Long getId() {
         return id;
     }
 
diff --git a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisNetworkRule.java
similarity index 59%
copy from api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisNetworkRule.java
index e99bc2fd416..2f4eadd443d 100644
--- a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisNetworkRule.java
@@ -14,21 +14,9 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.network.element;
+package org.apache.cloudstack.resource;
 
-import java.util.List;
+import com.cloud.network.SDNProviderNetworkRule;
 
-import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.network.Network;
-import com.cloud.network.rules.PortForwardingRule;
-
-public interface PortForwardingServiceProvider extends NetworkElement, IpDeployingRequester {
-    /**
-     * Apply rules
-     * @param network
-     * @param rules
-     * @return
-     * @throws ResourceUnavailableException
-     */
-    boolean applyPFRules(Network network, List<PortForwardingRule> rules) throws ResourceUnavailableException;
+public class NetrisNetworkRule extends SDNProviderNetworkRule {
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index dc922aef963..6f5e01b6392 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -30,6 +30,8 @@ import com.cloud.resource.ServerResource;
 import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.NetrisAnswer;
@@ -97,6 +99,10 @@ public class NetrisResource implements ServerResource {
           return executeRequest((DeleteNetrisVnetCommand) cmd);
         } else if (cmd instanceof SetupNetrisPublicRangeCommand) {
             return executeRequest((SetupNetrisPublicRangeCommand) cmd);
+        } else if (cmd instanceof DeleteNetrisNatRuleCommand) {
+            return executeRequest((DeleteNetrisNatRuleCommand) cmd);
+        } else if (cmd instanceof CreateOrUpdateNetrisNatCommand) {
+          return executeRequest((CreateOrUpdateNetrisNatCommand) cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
@@ -261,6 +267,36 @@ public class NetrisResource implements ServerResource {
         return new NetrisAnswer(cmd, true, "OK");
     }
 
+    private Answer executeRequest(CreateOrUpdateNetrisNatCommand cmd) {
+        String natRuleType = cmd.getNatRuleType();
+        if ("SNAT".equals(natRuleType)) {
+            boolean result = netrisApiClient.createOrUpdateSNATRule(cmd);
+            if (!result) {
+                return new NetrisAnswer(cmd, false, String.format("Failed to create SNAT rule on Netris for network %s", cmd.getName()));
+            }
+        } else if ("DNAT".equals(natRuleType)) {
+            boolean result = netrisApiClient.createOrUpdateDNATRule(cmd);
+            if (!result) {
+                return new NetrisAnswer(cmd, false, String.format("Failed to create DNAT rule on Netris for network %s", cmd.getName()));
+            }
+        } else if ("STATICNAT".equals(natRuleType)) {
+            boolean result = netrisApiClient.createStaticNatRule(cmd);
+            if (!result) {
+                return new NetrisAnswer(cmd, false, String.format("Failed to create SNAT rule on Netris for network %s", cmd.getName()));
+            }
+        }
+
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
+    private Answer executeRequest(DeleteNetrisNatRuleCommand cmd) {
+        boolean result = netrisApiClient.deleteNatRule(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Netris NAT rule: %s deletion failed", cmd.getNatRuleName()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
     @Override
     public boolean start() {
         return true;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
index 5b19aa43199..fd6851696c8 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -22,14 +22,14 @@ import org.apache.commons.lang3.ArrayUtils;
 public class NetrisResourceObjectUtils {
 
     public enum NetrisObjectType {
-        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET
+        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT
     }
 
     public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, NetrisObjectType netrisObjectType, String... suffixes) {
         long zoneId = cmd.getZoneId();
         Long accountId = cmd.getAccountId();
         Long domainId = cmd.getDomainId();
-        long objectId = cmd.getId();
+        Long objectId = cmd.getId();
         String objectName = cmd.getName();
         boolean isVpc = cmd.isVpc();
         boolean isZoneLevel = accountId == null && domainId == null;
@@ -60,6 +60,18 @@ public class NetrisResourceObjectUtils {
                     stringBuilder.append(String.format("-N%s", objectId));
                 }
                 break;
+            case SNAT:
+                stringBuilder.append(String.format("%s%s-%s", prefix, suffixes[0], "SNAT"));
+                suffixes = new String[0];
+                break;
+            case STATICNAT:
+                stringBuilder.append(String.format("%s%s-%s", prefix, suffixes[0], "STATICNAT"));
+                suffixes = new String[0];
+                break;
+            case DNAT:
+                stringBuilder.append(String.format("%s%s-%s", prefix, suffixes[0], "DNAT"));
+                suffixes = ArrayUtils.subarray(suffixes, 1, suffixes.length);
+                break;
             case VNET:
                break;
             default:
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index 0a00583df52..cf214bb4664 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -22,6 +22,8 @@ import io.netris.model.VPCListing;
 import io.netris.model.response.TenantResponse;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
@@ -48,8 +50,18 @@ public interface NetrisApiClient {
      */
     boolean deleteVpc(DeleteNetrisVpcCommand cmd);
 
+    /**
+     * Creation of a VPC network tier creates the following Netris resources:
+     * - Creates a Netris IPAM Subnet for the specified network tier's CIDR
+     * - Creates a Netris vNet
+     */
     boolean createVnet(CreateNetrisVnetCommand cmd);
 
+    /**
+     * Deletion of a VPC network tier deletes the following Netris resources:
+     * - Deletes the Netris IPAM Subnet for the specified network tier's CIDR
+     * - Deletes the Netris vNet
+     */
     boolean deleteVnet(DeleteNetrisVnetCommand cmd);
 
     /**
@@ -58,4 +70,8 @@ public interface NetrisApiClient {
      * - Check the IPAM subnet for NAT purpose for the range start-end. In case it doesn't exist, create it
      */
     boolean setupZoneLevelPublicRange(SetupNetrisPublicRangeCommand cmd);
+    boolean createOrUpdateSNATRule(CreateOrUpdateNetrisNatCommand cmd);
+    boolean createOrUpdateDNATRule(CreateOrUpdateNetrisNatCommand cmd);
+    boolean createStaticNatRule(CreateOrUpdateNetrisNatCommand cmd);
+    boolean deleteNatRule(DeleteNetrisNatRuleCommand cmd);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 2bf9b172608..51de34552a5 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -25,6 +25,7 @@ import io.netris.api.v1.AuthenticationApi;
 import io.netris.api.v1.SitesApi;
 import io.netris.api.v1.TenantsApi;
 import io.netris.api.v2.IpamApi;
+import io.netris.api.v2.NatApi;
 import io.netris.api.v2.VNetApi;
 import io.netris.api.v2.VpcApi;
 import io.netris.model.AllocationBody;
@@ -32,6 +33,8 @@ 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.InlineResponse2004;
 import io.netris.model.InlineResponse2004Data;
 import io.netris.model.IpTree;
@@ -39,6 +42,12 @@ import io.netris.model.IpTreeAllocation;
 import io.netris.model.IpTreeAllocationTenant;
 import io.netris.model.IpTreeSubnet;
 import io.netris.model.IpTreeSubnetSites;
+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.SitesResponseOK;
 import io.netris.model.SubnetBody;
 import io.netris.model.SubnetResBody;
@@ -62,13 +71,16 @@ import io.netris.model.VnetsBody;
 import io.netris.model.response.AuthResponse;
 import io.netris.model.response.TenantResponse;
 import io.netris.model.response.TenantsResponse;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -245,8 +257,50 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return createdIpamAllocation != null;
     }
 
+    @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")) {
+                    deleteNatSubnet(vpcResource.getId(), cmd.getNatIp());
+                }
+            }
+        } catch (Exception e) {
+            throw new CloudRuntimeException("Error deleting Netris NAT Rule", e);
+        }
+        return true;
+    }
+
+    private void deleteNatSubnet(Integer netrisVpcId, String natIp) {
+        FilterByVpc vpcFilter = new FilterByVpc();
+        vpcFilter.add(netrisVpcId);
+        String netrisSubnetName = natIp + "/32";
+        deleteSubnetInternal(vpcFilter, null, netrisSubnetName);
+    }
+
+    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 vpcCidr) {
-        logger.debug(String.format("Deleting Netris VPC IPAM Allocation %s for VPC %s", vpcCidr, vpcResource.getName()));
+        logger.debug("Deleting Netris VPC IPAM Allocation {} for VPC {}", vpcCidr, vpcResource.getName());
         try {
             VpcApi vpcApi = apiClient.getApiStubForMethod(VpcApi.class);
             VPCResponseResourceOK vpcResourcesResponse = vpcApi.apiV2VpcVpcIdResourcesGet(vpcResource.getId());
@@ -300,12 +354,20 @@ public class NetrisApiClientImpl implements NetrisApiClient {
 
     @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(String.format("Could not find the Netris VPC resource with name %s and tenant ID %s", vpcName, tenantId));
+            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 vpcCidr = cmd.getCidr();
         deleteVpcIpamAllocationInternal(vpcResource, vpcCidr);
         VPCResponseObjectOK response = deleteVpcInternal(vpcResource);
@@ -424,7 +486,9 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         filterByVpc.add(vpc.getId());
         SubnetResBody subnetResBody = ipamApi.apiV2IpamSubnetsGet(filterByVpc);
         List<IpTreeSubnet> exactSubnetList = subnetResBody.getData().stream()
-                .filter(x -> x.getAllocationID().equals(ipamAllocationId) && x.getPrefix().equals(exactCidr) && x.getPurpose() == purpose)
+                .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);
     }
@@ -460,6 +524,288 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         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 vpcSuffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
+        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 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(String.format("Creating subnet with NAT purpose for %s", targetIpSubnet));
+            createNatSubnet(targetIpSubnet, vpcResource.getId());
+        }
+
+        NatGetBody existingNatRule = netrisNatRuleExists(ruleName);
+        boolean ruleExists = Objects.nonNull(existingNatRule);
+        if (!ruleExists) {
+            String destinationAddress = action == NatPostBody.ActionEnum.SNAT ? "0.0.0.0/0" : cmd.getDestinationAddress() + "/32";
+            String destinationPort = cmd.getDestinationPort();
+            String sourceAddress = action == NatPostBody.ActionEnum.SNAT ? vpcCidr : "0.0.0.0/0";
+            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 vpcSuffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
+            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;
+            }
+            // Create a /32 subnet for the DNAT IP
+            createNatSubnet(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("0.0.0.0/0");
+            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(String natIp, Integer netrisVpcId) {
+        try {
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(netrisVpcId);
+            String netrisSubnetName = natIp;
+            List<IpTreeSubnet> matchedSubnets = getSubnet(vpcFilter, netrisSubnetName);
+            if (matchedSubnets.isEmpty()) {
+                VPCListing systemVpc = getSystemVpc();
+                createIpamSubnetInternal(natIp, natIp, SubnetBody.PurposeEnum.NAT, systemVpc);
+                return;
+            }
+            logger.debug("NAT subnet: {} already exists", natIp);
+        } catch (ApiException e) {
+            throw new CloudRuntimeException(String.format("Failed to create subnet for %s with NAT purpose", natIp));
+        }
+    }
+
+    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.setSourceAddress(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 void updateNatRequest(NatPostBody natBody) {
+
+    }
+
+    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("0.0.0.0/0");
+            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 void deleteVnetInternal(VPCListing associatedVpc, FilterBySites siteFilter, FilterByVpc vpcFilter, String netrisVnetName, String vNetName) {
         try {
             VNetApi vNetApi = apiClient.getApiStubForMethod(VNetApi.class);
@@ -483,21 +829,35 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
-    private void deleteSubnetInternal(FilterByVpc vpcFilter, String netrisVnetName, String netrisSubnetName) {
+    private List<IpTreeSubnet> getSubnet(FilterByVpc vpcFilter, String netrisSubnetName) {
         try {
-            logger.debug("Deleting Netris VPC IPAM Subnet {} for vNet: {}", netrisSubnetName, netrisVnetName);
             IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
             SubnetResBody subnetsResponse = ipamApi.apiV2IpamSubnetsGet(vpcFilter);
             List<IpTreeSubnet> subnets = subnetsResponse.getData();
-            List<IpTreeSubnet> matchedSubnets = subnets.stream().filter(subnet -> subnet.getName().equals(netrisSubnetName)).collect(Collectors.toList());
+            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: {} for the given vNet: {} appears to already be deleted on Netris", netrisSubnetName, netrisVnetName);
+                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 vNet: %s", netrisVnetName), e);
+            logAndThrowException(String.format("Failed to delete subnet: %s", netrisSubnetName), e);
         }
     }
 
@@ -607,4 +967,23 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
         return suffix;
     }
+
+    private NatGetBody netrisNatRuleExists(String netrisNatRule) {
+        try {
+            NatApi natApi = apiClient.getApiStubForMethod(NatApi.class);
+            //NatResponseGetOk response = natApi.apiV2NatGet(null, Arrays.asList(new BigDecimal(vpcId)));
+            NatResponseGetOk response = natApi.apiV2NatGet(null, null);
+            if (Objects.isNull(response) || !response.isIsSuccess()) {
+                throw new CloudRuntimeException("Failed to list Netris NAT rules");
+            }
+            List<NatGetBody> data = response.getData().stream().filter(natData -> natData.getName().equals(netrisNatRule)).collect(Collectors.toList());
+            if (data.isEmpty()) {
+                return null;
+            }
+            return data.get(0);
+
+        } catch (ApiException e) {
+            throw new CloudRuntimeException("Failed to list Netris NAT rules");
+        }
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 9a29a410862..eb74e6f7080 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -23,6 +23,7 @@ import com.cloud.agent.api.AgentControlCommand;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
 import com.cloud.agent.api.StartupCommand;
+import com.cloud.api.ApiDBUtils;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.deploy.DeployDestination;
@@ -39,15 +40,25 @@ import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.NetworkModel;
 import com.cloud.network.PhysicalNetworkServiceProvider;
+import com.cloud.network.PublicIpAddress;
+import com.cloud.network.SDNProviderOpObject;
+import com.cloud.network.dao.IPAddressDao;
+import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.element.DhcpServiceProvider;
 import com.cloud.network.element.DnsServiceProvider;
+import com.cloud.network.element.IpDeployer;
 import com.cloud.network.element.NetworkACLServiceProvider;
+import com.cloud.network.element.PortForwardingServiceProvider;
+import com.cloud.network.element.StaticNatServiceProvider;
 import com.cloud.network.element.VirtualRouterElement;
 import com.cloud.network.element.VpcProvider;
 import com.cloud.network.netris.NetrisService;
+import com.cloud.network.rules.FirewallRule;
 import com.cloud.network.rules.LoadBalancerContainer;
+import com.cloud.network.rules.PortForwardingRule;
+import com.cloud.network.rules.StaticNat;
 import com.cloud.network.vpc.NetworkACLItem;
 import com.cloud.network.vpc.PrivateGateway;
 import com.cloud.network.vpc.StaticRouteProfile;
@@ -61,27 +72,39 @@ import com.cloud.resource.ServerResource;
 import com.cloud.resource.UnableDeleteHostException;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
+import com.cloud.uservm.UserVm;
+import com.cloud.utils.Pair;
 import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.NicProfile;
 import com.cloud.vm.ReservationContext;
+import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.vm.dao.VMInstanceDao;
 import org.apache.cloudstack.StartupNetrisCommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.resource.NetrisNetworkRule;
+import org.apache.cloudstack.resourcedetail.FirewallRuleDetailVO;
+import org.apache.cloudstack.resourcedetail.dao.FirewallRuleDetailsDao;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.springframework.stereotype.Component;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
 @Component
 public class NetrisElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider,
-        NetworkACLServiceProvider, ResourceStateAdapter, Listener {
+        StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider, NetworkACLServiceProvider, ResourceStateAdapter, Listener {
 
     @Inject
     NetworkModel networkModel;
@@ -101,6 +124,12 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
     private DomainDao domainDao;
     @Inject
     private VpcDao vpcDao;
+    @Inject
+    private FirewallRuleDetailsDao firewallRuleDetailsDao;
+    @Inject
+    private IPAddressDao ipAddressDao;
+    @Inject
+    private VMInstanceDao vmInstanceDao;
 
     protected Logger logger = LogManager.getLogger(getClass());
 
@@ -257,6 +286,11 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
         return capabilities;
     }
 
+    @Override
+    public boolean applyIps(Network network, List<? extends PublicIpAddress> ipAddress, Set<Network.Service> services) throws ResourceUnavailableException {
+        return true;
+    }
+
     @Override
     public Network.Provider getProvider() {
         return Network.Provider.Netris;
@@ -398,4 +432,164 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
     public boolean reorderAclRules(Vpc vpc, List<? extends Network> networks, List<? extends NetworkACLItem> networkACLItems) {
         return false;
     }
+
+    @Override
+    public IpDeployer getIpDeployer(Network network) {
+        return this;
+    }
+
+    @Override
+    public boolean applyPFRules(Network network, List<PortForwardingRule> rules) throws ResourceUnavailableException {
+        if (!canHandle(network, Network.Service.PortForwarding)) {
+            return false;
+        }
+        return applyPFRulesInternal(network, rules);
+    }
+
+    private boolean addOrRemovePFRuleOnNetris(UserVm vm, PortForwardingRule rule, NetrisNetworkRule networkRule, SDNProviderOpObject netrisObject, boolean create) {
+        logger.debug("{} port forwarding rule on Netris for VM {} to ports {} - {}",
+                create ? "Creating" : "Deleting", vm.getUuid(), rule.getDestinationPortStart(), rule.getDestinationPortEnd());
+        Long vpcId = netrisObject.getVpcVO() != null ? netrisObject.getVpcVO().getId() : null;
+        String vpcName = netrisObject.getVpcVO() != null ? netrisObject.getVpcVO().getName() : null;
+        Long networkId = netrisObject.getNetworkVO() != null ? netrisObject.getNetworkVO().getId() : null;
+        String networkName = netrisObject.getNetworkVO() != null ? netrisObject.getNetworkVO().getName() : null;
+        String vpcCidr = netrisObject.getVpcVO() != null ? netrisObject.getVpcVO().getCidr() : null;
+
+        return create ?
+                netrisService.createPortForwardingRule(networkRule.getZoneId(), networkRule.getAccountId(), networkRule.getDomainId(),
+                        vpcName, vpcId, networkName, networkId, netrisObject.isVpcResource(), vpcCidr, networkRule) :
+                netrisService.deletePortForwardingRule(networkRule.getZoneId(), networkRule.getAccountId(), networkRule.getDomainId(),
+                        vpcName, vpcId, networkName, networkId, netrisObject.isVpcResource(), vpcCidr, networkRule);
+    }
+
+    private boolean applyPFRulesInternal(Network network, List<PortForwardingRule> rules) {
+        return Transaction.execute((TransactionCallback<Boolean>) status -> {
+            boolean result = true;
+            for (PortForwardingRule rule : rules) {
+                IPAddressVO publicIp = ApiDBUtils.findIpAddressById(rule.getSourceIpAddressId());
+                UserVm vm = ApiDBUtils.findUserVmById(rule.getVirtualMachineId());
+                if (vm == null && rule.getState() != FirewallRule.State.Revoke) {
+                    continue;
+                }
+                SDNProviderOpObject netrisObject = getNetrisOpObject(network);
+                String publicPort = PortForwardingServiceProvider.getPublicPortRange(rule);
+                String privatePort = PortForwardingServiceProvider.getPrivatePFPortRange(rule);
+                FirewallRuleDetailVO ruleDetail = firewallRuleDetailsDao.findDetail(rule.getId(), ApiConstants.NETRIS_DETAIL_KEY);
+
+                NetrisNetworkRule networkRule = new NetrisNetworkRule();
+                networkRule.setDomainId(netrisObject.getDomainId());
+                networkRule.setAccountId(netrisObject.getAccountId());
+                networkRule.setZoneId(netrisObject.getZoneId());
+                networkRule.setNetworkResourceId(netrisObject.getNetworkResourceId());
+                networkRule.setNetworkResourceName(netrisObject.getNetworkResourceName());
+                networkRule.setVpcResource(netrisObject.isVpcResource());
+                networkRule.setVmId(Objects.nonNull(vm) ? vm.getId() : 0);
+                networkRule.setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null);
+                networkRule.setPublicIp(publicIp.getAddress().addr());
+                networkRule.setPrivatePort(privatePort);
+                networkRule.setPublicPort(publicPort);
+                networkRule.setRuleId(rule.getId());
+                networkRule.setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT));
+
+                if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(rule.getState())) {
+                    boolean pfRuleResult = addOrRemovePFRuleOnNetris(vm, rule, networkRule, netrisObject, true);
+                    if (pfRuleResult) {
+                        logger.debug("Port forwarding rule {} created on Netris, adding detail on firewall rules details", rule.getId());
+                        if (ruleDetail == null && FirewallRule.State.Add == rule.getState()) {
+                            logger.debug("Adding new firewall detail for rule {}", rule.getId());
+                            firewallRuleDetailsDao.addDetail(rule.getId(), ApiConstants.NETRIS_DETAIL_KEY, "true", false);
+                        } else if (ruleDetail != null) {
+                            logger.debug("Updating firewall detail for rule {}", rule.getId());
+                            ruleDetail.setValue("true");
+                            firewallRuleDetailsDao.update(ruleDetail.getId(), ruleDetail);
+                        }
+                    }
+                    result &= pfRuleResult;
+                } else if (rule.getState() == FirewallRule.State.Revoke) {
+                    boolean pfRuleResult = addOrRemovePFRuleOnNetris(vm, rule, networkRule, netrisObject, false);
+                    if (pfRuleResult && ruleDetail != null) {
+                        logger.debug("Updating firewall rule detail {} for rule {}, set to false", ruleDetail.getId(), rule.getId());
+                        ruleDetail.setValue("false");
+                        firewallRuleDetailsDao.update(ruleDetail.getId(), ruleDetail);
+                    }
+                    result &= pfRuleResult;
+                }
+            }
+            return result;
+        });
+    }
+
+    private SDNProviderOpObject getNetrisOpObject(Network network) {
+        Pair<VpcVO, NetworkVO> vpcOrNetwork = getVpcOrNetwork(network.getVpcId(), network.getId());
+        VpcVO vpc = vpcOrNetwork.first();
+        NetworkVO networkVO = vpcOrNetwork.second();
+        long domainId = getResourceId("domain", vpc, networkVO);
+        long accountId = getResourceId("account", vpc, networkVO);
+        long zoneId = getResourceId("zone", vpc, networkVO);
+
+        return new SDNProviderOpObject.Builder()
+                .vpcVO(vpc)
+                .networkVO(networkVO)
+                .domainId(domainId)
+                .accountId(accountId)
+                .zoneId(zoneId)
+                .build();
+    }
+
+    public boolean applyStaticNats(Network config, List<? extends StaticNat> rules) throws ResourceUnavailableException {
+        for(StaticNat staticNat : rules) {
+            long sourceIpAddressId = staticNat.getSourceIpAddressId();
+            IPAddressVO ipAddressVO = ipAddressDao.findByIdIncludingRemoved(sourceIpAddressId);
+            VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(ipAddressVO.getAssociatedWithVmId());
+            // floating ip is released when nic was deleted
+            if (vm == null || networkModel.getNicInNetworkIncludingRemoved(vm.getId(), config.getId()) == null) {
+                continue;
+            }
+            Pair<VpcVO, NetworkVO> vpcOrNetwork = getVpcOrNetwork(config.getVpcId(), config.getId());
+            VpcVO vpc = vpcOrNetwork.first();
+            NetworkVO network = vpcOrNetwork.second();
+            Long networkResourceId = Objects.nonNull(vpc) ? vpc.getId() : network.getId();
+            String networkResourceName = Objects.nonNull(vpc) ? vpc.getName() : network.getName();
+            boolean isVpcResource = Objects.nonNull(vpc);
+            if (!staticNat.isForRevoke()) {
+                return netrisService.createStaticNatRule(config.getDataCenterId(), config.getAccountId(), config.getDomainId(),
+                       networkResourceName, networkResourceId, isVpcResource, vpc.getCidr(),
+                        ipAddressVO.getAddress().addr(), staticNat.getDestIpAddress());
+            } else {
+                return netrisService.deleteStaticNatRule(config.getDataCenterId(), config.getAccountId(), config.getDomainId(),
+                        networkResourceName, networkResourceId, isVpcResource, ipAddressVO.getAddress().addr());
+            }
+        }
+        return false;
+    }
+
+    public Pair<VpcVO, NetworkVO> getVpcOrNetwork(Long vpcId, long networkId) {
+        VpcVO vpc = null;
+        NetworkVO network = null;
+        if (Objects.nonNull(vpcId)) {
+            vpc = vpcDao.findById(vpcId);
+            if (Objects.isNull(vpc)) {
+                throw new CloudRuntimeException(String.format("Failed to find VPC with id: %s", vpcId));
+            }
+        } else {
+            network = networkDao.findById(networkId);
+            if (Objects.isNull(network)) {
+                throw new CloudRuntimeException(String.format("Failed to find network with id: %s", networkId));
+            }
+        }
+        return new Pair<>(vpc, network);
+    }
+
+    private long getResourceId(String resource, VpcVO vpc, NetworkVO network) {
+        switch (resource) {
+            case "domain":
+                return Objects.nonNull(vpc) ? vpc.getDomainId() : network.getDomainId();
+            case "account":
+                return Objects.nonNull(vpc) ? vpc.getAccountId() : network.getAccountId();
+            case "zone":
+                return Objects.nonNull(vpc) ? vpc.getZoneId() : network.getDataCenterId();
+            default:
+                return 0;
+        }
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
index 2a4891eb28c..8fa07f17d40 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
@@ -28,8 +28,10 @@ import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.network.Network;
 import com.cloud.network.NetworkMigrationResponder;
+import com.cloud.network.NetworkModel;
 import com.cloud.network.Networks;
 import com.cloud.network.PhysicalNetwork;
+import com.cloud.network.PublicIpAddress;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.dao.PhysicalNetworkVO;
 import com.cloud.network.guru.GuestNetworkGuru;
@@ -57,6 +59,8 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
 
     @Inject
     private NetrisService netrisService;
+    @Inject
+    NetworkModel networkModel;
 
     public NetrisGuestNetworkGuru() {
         super();
@@ -236,6 +240,18 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
 
         if (isNull(network.getVpcId()) && networkOfferingVO.getNetworkMode().equals(NetworkOffering.NetworkMode.NATTED)) {
             // Netris Natted mode
+            long domainId = domain.getId();
+            long accountId = account.getId();
+            long dataCenterId = zone.getId();
+            long resourceId = network.getId();
+            PublicIpAddress ipAddress = networkModel.getSourceNatIpAddressForGuestNetwork(account, network);
+            String snatIP = ipAddress.getAddress().addr();
+            boolean result = netrisService.createSnatRule(dataCenterId, accountId, domainId, vpc.getName(), vpc.getId(), network.getName(), resourceId, nonNull(network.getVpcId()), vpc.getCidr(), snatIP);
+            if (!result) {
+                String msg = String.format("Could not create Netris Nat Rule for IP %s", snatIP);
+                logger.error(msg);
+                throw new CloudRuntimeException(msg);
+            }
         }
 
         return nicProfile;
@@ -277,7 +293,7 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
             vpcName = vpc.getName();
             vpcId = vpc.getId();
         } else {
-            logger.debug(String.format("Creating a Tier 1 Gateway for the network %s before creating the NSX segment", networkVO.getName()));
+            logger.debug(String.format("Creating IPAM Allocation before creating IPAM Subnet", networkVO.getName()));
             long networkOfferingId = networkVO.getNetworkOfferingId();
             NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(networkOfferingId);
             boolean isSourceNatSupported = !NetworkOffering.NetworkMode.ROUTED.equals(networkOfferingVO.getNetworkMode()) &&
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisPublicNetworkGuru.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisPublicNetworkGuru.java
index 11db60a5aca..f1cc49b7125 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisPublicNetworkGuru.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisPublicNetworkGuru.java
@@ -128,6 +128,15 @@ public class NetrisPublicNetworkGuru extends PublicNetworkGuru {
                 if (!hasNatSupport) {
                     return nic;
                 }
+
+                String snatIP = ipAddress.getAddress().addr();
+                result = netrisService.createSnatRule(dataCenterId, accountId, domainId, vpc.getName(), vpc.getId(), network.getName(), network.getId(), isForVpc, vpc.getCidr(), snatIP);
+                if (!result) {
+                    String msg = String.format("Could not create Netris Nat Rule for IP %s", snatIP);
+                    logger.error(msg);
+                    throw new CloudRuntimeException(msg);
+                }
+
             }
         }
         return nic;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index d679eae258d..b1e00f771cd 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -19,25 +19,32 @@ package org.apache.cloudstack.service;
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.IpAddress;
 import com.cloud.network.Networks;
+import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.netris.NetrisService;
 import com.cloud.network.vpc.Vpc;
+import io.netris.model.NatPostBody;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.NetrisAnswer;
 import org.apache.cloudstack.agent.api.NetrisCommand;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
+import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import javax.inject.Inject;
+import java.util.Locale;
 import java.util.Objects;
 
 public class NetrisServiceImpl implements NetrisService, Configurable {
@@ -110,4 +117,136 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
         return answer.getResult();
     }
+
+    @Override
+    public boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String snatIP) {
+        CreateOrUpdateNetrisNatCommand cmd = new CreateOrUpdateNetrisNatCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, isForVpc, vpcCidr);
+        cmd.setNatIp(snatIP);
+        cmd.setNatRuleType("SNAT");
+        String suffix;
+        if (isForVpc) {
+            suffix = String.valueOf(vpcId); // D1-A1-Z1-V25-SNAT
+        } else {
+            suffix = String.valueOf(networkId); // D1-A1-Z1-N25-SNAT
+        }
+        cmd.setProtocol(NatPostBody.ProtocolEnum.ALL.getValue());
+        cmd.setState(NatPostBody.StateEnum.ENABLED.getValue());
+        String snatRuleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.SNAT, suffix);
+        cmd.setNatRuleName(snatRuleName);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean createPortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName,
+                                            Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule) {
+        CreateOrUpdateNetrisNatCommand cmd = new CreateOrUpdateNetrisNatCommand(zoneId, accountId, domainId, vpcName, vpcId,
+                networkName, networkId, isForVpc, vpcCidr);
+        cmd.setProtocol(networkRule.getProtocol().toLowerCase(Locale.ROOT));
+        cmd.setDestinationAddress(networkRule.getPublicIp());
+        cmd.setDestinationPort(networkRule.getPublicPort());
+        cmd.setSourceAddress(networkRule.getVmIp());
+        cmd.setSourcePort(networkRule.getPrivatePort());
+        cmd.setState(NatPostBody.StateEnum.ENABLED.getValue());
+        String ruleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.DNAT,
+                String.valueOf(vpcId), String.format("R%s", networkRule.getRuleId()));
+        cmd.setNatRuleName(ruleName);
+        cmd.setNatRuleType("DNAT");
+
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean deletePortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule) {
+        DeleteNetrisNatRuleCommand cmd = new DeleteNetrisNatRuleCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, isForVpc);
+        String ruleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.DNAT,
+                String.valueOf(vpcId), String.format("R%s", networkRule.getRuleId()));
+        cmd.setNatRuleType("DNAT");
+        cmd.setNatRuleName(ruleName);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
+        if (vpc == null || address == null) {
+            return false;
+        }
+        long accountId = vpc.getAccountId();
+        long domainId = vpc.getDomainId();
+        long zoneId = vpc.getZoneId();
+        long vpcId = vpc.getId();
+        String vpcName = vpc.getName();
+
+        logger.debug("Updating the source NAT IP for Netris VPC {} to IP: {}", vpc.getName(), address.getAddress().addr());
+
+        CreateOrUpdateNetrisNatCommand cmd = new CreateOrUpdateNetrisNatCommand(zoneId, accountId, domainId, vpcName, vpcId, null, null, true, address.getAddress().addr());
+        cmd.setNatRuleType("SNAT");
+        String snatRuleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.SNAT, String.valueOf(vpcId));
+        cmd.setNatRuleName(snatRuleName);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        if (!answer.getResult()) {
+            logger.error("Could not update the source NAT IP address for VPC {}: {}", vpc.getName(), answer.getDetails());
+            return false;
+        }
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean createStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String vpcCidr, String staticNatIp, String vmIp) {
+        String vpcName = null;
+        String networkName = null;
+        Long vpcId = null;
+        Long networkId = null;
+        if (isForVpc) {
+            vpcName = networkResourceName;
+            vpcId = networkResourceId;
+        } else {
+            networkName = networkResourceName;
+            networkId = networkResourceId;
+        }
+        CreateOrUpdateNetrisNatCommand cmd = new CreateOrUpdateNetrisNatCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, isForVpc, vpcCidr);
+        cmd.setNatRuleType("STATICNAT");
+        cmd.setNatIp(staticNatIp);
+        cmd.setVmIp(vmIp);
+        String suffix = getResourceSuffix(vpcId, networkId, isForVpc);
+        String dnatRuleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.STATICNAT, suffix);
+        cmd.setNatRuleName(dnatRuleName);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp) {
+        String vpcName = null;
+        String networkName = null;
+        Long vpcId = null;
+        Long networkId = null;
+        if (isForVpc) {
+            vpcName = networkResourceName;
+            vpcId = networkResourceId;
+        } else {
+            networkName = networkResourceName;
+            networkId = networkResourceId;
+        }
+        DeleteNetrisNatRuleCommand cmd = new DeleteNetrisNatRuleCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, isForVpc);
+        String suffix = getResourceSuffix(vpcId, networkId, isForVpc);
+        String dnatRuleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.STATICNAT, suffix);
+        cmd.setNatRuleName(dnatRuleName);
+        cmd.setNatRuleType("STATICNAT");
+        cmd.setNatIp(staticNatIp);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    private String getResourceSuffix(Long vpcId, Long networkId, boolean isForVpc) {
+        String suffix;
+        if (isForVpc) {
+            suffix = String.valueOf(vpcId); // D1-A1-Z1-V25-STATICNAT or D1-A1-Z1-V25-SNAT
+        } else {
+            suffix = String.valueOf(networkId); // D1-A1-Z1-N25-STATICNAT or D1-A1-Z1-N25-SNAT
+        }
+        return suffix;
+    }
 }
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java
index 11b4af48fed..e7d223672e4 100644
--- a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.resource;
 
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.junit.Assert;
 import org.junit.Test;
@@ -54,4 +55,15 @@ public class NetrisResourceObjectUtilsTest {
         String expectedNetrisVpcName = String.format("D%s-A%s-Z%s-V%s-%s", domainId, accountId, zoneId, vpcId, vpcName);
         Assert.assertEquals(expectedNetrisVpcName, netrisVpcName);
     }
+
+    @Test
+    public void testSuffixesForDNAT() {
+        CreateOrUpdateNetrisNatCommand cmd = new CreateOrUpdateNetrisNatCommand(zoneId, accountId, domainId, vpcName, vpcId, vpcName, null, true, vpcCidr);
+        cmd.setNatRuleType("DNAT");
+        long ruleId = 23L;
+        String ruleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.DNAT,
+                String.valueOf(vpcId), String.format("R%s", ruleId));
+        String expectedNetrisRuleName = String.format("D%s-A%s-Z%s-V%s-DNAT-R%s", domainId, accountId, zoneId, vpcId, ruleId);
+        Assert.assertEquals(expectedNetrisRuleName, ruleName);
+    }
 }
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
index c11141db9d4..153143b6d18 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
@@ -16,151 +16,18 @@
 // under the License.
 package org.apache.cloudstack.resource;
 
-import com.cloud.network.Network;
+import com.cloud.network.SDNProviderNetworkRule;
 
 import java.util.List;
 
-public class NsxNetworkRule {
+public class NsxNetworkRule extends SDNProviderNetworkRule {
 
     public enum NsxRuleAction {
         ALLOW, DROP
     }
 
-    private long domainId;
-    private long accountId;
-    private long zoneId;
-    private Long networkResourceId;
-    private String networkResourceName;
-    private boolean isVpcResource;
-    private long vmId;
-    private long ruleId;
-    private String publicIp;
-    private String vmIp;
-    private String publicPort;
-    private String privatePort;
-    private String protocol;
-    private String algorithm;
     private List<NsxLoadBalancerMember> memberList;
     private NsxRuleAction aclAction;
-    private List<String> sourceCidrList;
-    private List<String> destinationCidrList;
-    private Integer icmpCode;
-
-    private Integer icmpType;
-    private String trafficType;
-    private Network.Service service;
-
-    public long getDomainId() {
-        return domainId;
-    }
-
-    public void setDomainId(long domainId) {
-        this.domainId = domainId;
-    }
-
-    public long getAccountId() {
-        return accountId;
-    }
-
-    public void setAccountId(long accountId) {
-        this.accountId = accountId;
-    }
-
-    public long getZoneId() {
-        return zoneId;
-    }
-
-    public void setZoneId(long zoneId) {
-        this.zoneId = zoneId;
-    }
-
-    public Long getNetworkResourceId() {
-        return networkResourceId;
-    }
-
-    public void setNetworkResourceId(Long networkResourceId) {
-        this.networkResourceId = networkResourceId;
-    }
-
-    public String getNetworkResourceName() {
-        return networkResourceName;
-    }
-
-    public void setNetworkResourceName(String networkResourceName) {
-        this.networkResourceName = networkResourceName;
-    }
-
-    public boolean isVpcResource() {
-        return isVpcResource;
-    }
-
-    public void setVpcResource(boolean vpcResource) {
-        isVpcResource = vpcResource;
-    }
-
-    public long getVmId() {
-        return vmId;
-    }
-
-    public void setVmId(long vmId) {
-        this.vmId = vmId;
-    }
-
-    public long getRuleId() {
-        return ruleId;
-    }
-
-    public void setRuleId(long ruleId) {
-        this.ruleId = ruleId;
-    }
-
-    public String getPublicIp() {
-        return publicIp;
-    }
-
-    public void setPublicIp(String publicIp) {
-        this.publicIp = publicIp;
-    }
-
-    public String getVmIp() {
-        return vmIp;
-    }
-
-    public void setVmIp(String vmIp) {
-        this.vmIp = vmIp;
-    }
-
-    public String getPublicPort() {
-        return publicPort;
-    }
-
-    public void setPublicPort(String publicPort) {
-        this.publicPort = publicPort;
-    }
-
-    public String getPrivatePort() {
-        return privatePort;
-    }
-
-    public void setPrivatePort(String privatePort) {
-        this.privatePort = privatePort;
-    }
-
-    public String getProtocol() {
-        return protocol;
-    }
-
-    public void setProtocol(String protocol) {
-        this.protocol = protocol;
-    }
-
-    public void setAlgorithm(String algorithm) {
-        this.algorithm = algorithm;
-    }
-
-    public String getAlgorithm() {
-        return algorithm;
-    }
 
     public List<NsxLoadBalancerMember> getMemberList() {
         return memberList;
@@ -177,221 +44,4 @@ public class NsxNetworkRule {
     public void setAclAction(NsxRuleAction aclAction) {
         this.aclAction = aclAction;
     }
-
-    public Network.Service getService() {
-        return service;
-    }
-
-    public void setService(Network.Service service) {
-        this.service = service;
-    }
-
-    public Integer getIcmpCode() {
-        return icmpCode;
-    }
-
-    public void setIcmpCode(Integer icmpCode) {
-        this.icmpCode = icmpCode;
-    }
-
-    public Integer getIcmpType() {
-        return icmpType;
-    }
-
-    public void setIcmpType(Integer icmpType) {
-        this.icmpType = icmpType;
-    }
-
-    public List<String> getSourceCidrList() {
-        return sourceCidrList;
-    }
-
-    public void setSourceCidrList(List<String> sourceCidrList) {
-        this.sourceCidrList = sourceCidrList;
-    }
-
-    public List<String> getDestinationCidrList() {
-        return destinationCidrList;
-    }
-
-    public void setDestinationCidrList(List<String> destinationCidrList) {
-        this.destinationCidrList = destinationCidrList;
-    }
-
-    public String getTrafficType() {
-        return trafficType;
-    }
-
-    public void setTrafficType(String trafficType) {
-        this.trafficType = trafficType;
-    }
-
-    public static final class Builder {
-        private long domainId;
-        private long accountId;
-        private long zoneId;
-        private Long networkResourceId;
-        private String networkResourceName;
-        private boolean isVpcResource;
-        private long vmId;
-
-        private long ruleId;
-        private String publicIp;
-        private String vmIp;
-        private String publicPort;
-        private String privatePort;
-        private String protocol;
-        private String algorithm;
-        private List<NsxLoadBalancerMember> memberList;
-        private NsxRuleAction aclAction;
-        private List<String> sourceCidrList;
-        private List<String> destinationidrList;
-        private String trafficType;
-        private Integer icmpType;
-        private Integer icmpCode;
-        private Network.Service service;
-
-        public Builder() {
-            // Default constructor
-        }
-
-        public Builder setDomainId(long domainId) {
-            this.domainId = domainId;
-            return this;
-        }
-
-        public Builder setAccountId(long accountId) {
-            this.accountId = accountId;
-            return this;
-        }
-
-        public Builder setZoneId(long zoneId) {
-            this.zoneId = zoneId;
-            return this;
-        }
-
-        public Builder setNetworkResourceId(Long networkResourceId) {
-            this.networkResourceId = networkResourceId;
-            return this;
-        }
-
-        public Builder setNetworkResourceName(String networkResourceName) {
-            this.networkResourceName = networkResourceName;
-            return this;
-        }
-
-        public Builder setVpcResource(boolean isVpcResource) {
-            this.isVpcResource = isVpcResource;
-            return this;
-        }
-
-
-        public Builder setVmId(long vmId) {
-            this.vmId = vmId;
-            return this;
-        }
-
-        public Builder setRuleId(long ruleId) {
-            this.ruleId = ruleId;
-            return this;
-        }
-
-        public Builder setPublicIp(String publicIp) {
-            this.publicIp = publicIp;
-            return this;
-        }
-
-        public Builder setVmIp(String vmIp) {
-            this.vmIp = vmIp;
-            return this;
-        }
-
-        public Builder setPublicPort(String publicPort) {
-            this.publicPort = publicPort;
-            return this;
-        }
-
-        public Builder setPrivatePort(String privatePort) {
-            this.privatePort = privatePort;
-            return this;
-        }
-
-        public Builder setProtocol(String protocol) {
-            this.protocol = protocol;
-            return this;
-        }
-
-        public Builder setAlgorithm(String algorithm) {
-            this.algorithm = algorithm;
-            return this;
-        }
-
-        public Builder setMemberList(List<NsxLoadBalancerMember> memberList) {
-            this.memberList = memberList;
-            return this;
-        }
-
-
-        public Builder setAclAction(NsxRuleAction aclAction) {
-            this.aclAction = aclAction;
-            return this;
-        }
-
-        public Builder setTrafficType(String trafficType) {
-            this.trafficType = trafficType;
-            return this;
-        }
-
-        public Builder setIcmpType(Integer icmpType) {
-            this.icmpType = icmpType;
-            return this;
-        }
-
-        public Builder setIcmpCode(Integer icmpCode) {
-            this.icmpCode = icmpCode;
-            return this;
-        }
-
-        public Builder setSourceCidrList(List<String> sourceCidrList) {
-            this.sourceCidrList = sourceCidrList;
-            return this;
-        }
-
-        public Builder setDestinationCidrList(List<String> destinationCidrList) {
-            this.destinationidrList = destinationCidrList;
-            return this;
-        }
-
-        public Builder setService(Network.Service service) {
-            this.service = service;
-            return this;
-        }
-
-        public NsxNetworkRule build() {
-            NsxNetworkRule rule = new NsxNetworkRule();
-            rule.setDomainId(this.domainId);
-            rule.setAccountId(this.accountId);
-            rule.setZoneId(this.zoneId);
-            rule.setNetworkResourceId(this.networkResourceId);
-            rule.setNetworkResourceName(this.networkResourceName);
-            rule.setVpcResource(this.isVpcResource);
-            rule.setVmId(this.vmId);
-            rule.setVmIp(this.vmIp);
-            rule.setPublicIp(this.publicIp);
-            rule.setPublicPort(this.publicPort);
-            rule.setPrivatePort(this.privatePort);
-            rule.setProtocol(this.protocol);
-            rule.setRuleId(this.ruleId);
-            rule.setAlgorithm(this.algorithm);
-            rule.setMemberList(this.memberList);
-            rule.setAclAction(this.aclAction);
-            rule.setIcmpType(this.icmpType);
-            rule.setIcmpCode(this.icmpCode);
-            rule.setSourceCidrList(this.sourceCidrList);
-            rule.setDestinationCidrList(this.destinationidrList);
-            rule.setTrafficType(this.trafficType);
-            rule.setService(service);
-            return rule;
-        }
-    }
 }
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
index 7673e5a6038..753d0cf822e 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
@@ -109,7 +109,7 @@ import org.apache.cloudstack.api.command.admin.internallb.ListInternalLoadBalanc
 import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
 import org.apache.cloudstack.resource.NsxLoadBalancerMember;
 import org.apache.cloudstack.resource.NsxNetworkRule;
-import org.apache.cloudstack.resource.NsxOpObject;
+import com.cloud.network.SDNProviderOpObject;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.cloudstack.resourcedetail.FirewallRuleDetailVO;
@@ -553,26 +553,26 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
                 if (vm == null && rule.getState() != FirewallRule.State.Revoke) {
                     continue;
                 }
-                NsxOpObject nsxObject = getNsxOpObject(network);
-                String publicPort = getPublicPortRange(rule);
-
-                String privatePort = getPrivatePFPortRange(rule);
-
-                NsxNetworkRule networkRule = new NsxNetworkRule.Builder()
-                        .setDomainId(nsxObject.getDomainId())
-                        .setAccountId(nsxObject.getAccountId())
-                        .setZoneId(nsxObject.getZoneId())
-                        .setNetworkResourceId(nsxObject.getNetworkResourceId())
-                        .setNetworkResourceName(nsxObject.getNetworkResourceName())
-                        .setVpcResource(nsxObject.isVpcResource())
-                        .setVmId(Objects.nonNull(vm) ? vm.getId() : 0)
-                        .setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null)
-                        .setPublicIp(publicIp.getAddress().addr())
-                        .setPrivatePort(privatePort)
-                        .setPublicPort(publicPort)
-                        .setRuleId(rule.getId())
-                        .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT))
-                        .build();
+                SDNProviderOpObject nsxObject = getNsxOpObject(network);
+                String publicPort = PortForwardingServiceProvider.getPublicPortRange(rule);
+
+                String privatePort = PortForwardingServiceProvider.getPrivatePFPortRange(rule);
+
+                NsxNetworkRule networkRule = new NsxNetworkRule();
+                networkRule.setDomainId(nsxObject.getDomainId());
+                networkRule.setAccountId(nsxObject.getAccountId());
+                networkRule.setZoneId(nsxObject.getZoneId());
+                networkRule.setNetworkResourceId(nsxObject.getNetworkResourceId());
+                networkRule.setNetworkResourceName(nsxObject.getNetworkResourceName());
+                networkRule.setVpcResource(nsxObject.isVpcResource());
+                networkRule.setVmId(Objects.nonNull(vm) ? vm.getId() : 0);
+                networkRule.setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null);
+                networkRule.setPublicIp(publicIp.getAddress().addr());
+                networkRule.setPrivatePort(privatePort);
+                networkRule.setPublicPort(publicPort);
+                networkRule.setRuleId(rule.getId());
+                networkRule.setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT));
+
                 FirewallRuleDetailVO ruleDetail = firewallRuleDetailsDao.findDetail(rule.getId(), ApiConstants.FOR_NSX);
                 if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(rule.getState())) {
                     if ((ruleDetail == null && FirewallRule.State.Add == rule.getState()) || (ruleDetail != null && !ruleDetail.getValue().equalsIgnoreCase("true"))) {
@@ -634,30 +634,6 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
         return new Pair<>(vpc, network);
     }
 
-    private static String getPublicPortRange(PortForwardingRule rule) {
-        return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
-                String.valueOf(rule.getSourcePortStart()) :
-                String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
-    }
-
-    private static String getPrivatePFPortRange(PortForwardingRule rule) {
-        return rule.getDestinationPortStart() == rule.getDestinationPortEnd() ?
-                String.valueOf(rule.getDestinationPortStart()) :
-                String.valueOf(rule.getDestinationPortStart()).concat("-").concat(String.valueOf(rule.getDestinationPortEnd()));
-    }
-
-    private static String getPrivatePortRange(FirewallRule rule) {
-        return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
-                String.valueOf(rule.getSourcePortStart()) :
-                String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
-    }
-
-    private static String getPrivatePortRangeForACLRule(NetworkACLItem rule) {
-        return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
-                String.valueOf(rule.getSourcePortStart()) :
-                String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
-    }
-
     private long getResourceId(String resource, VpcVO vpc, NetworkVO network) {
         switch (resource) {
             case "domain":
@@ -671,7 +647,7 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
         }
     }
 
-    private NsxOpObject getNsxOpObject(Network network) {
+    private SDNProviderOpObject getNsxOpObject(Network network) {
         Pair<VpcVO, NetworkVO> vpcOrNetwork = getVpcOrNetwork(network.getVpcId(), network.getId());
         VpcVO vpc = vpcOrNetwork.first();
         NetworkVO networkVO = vpcOrNetwork.second();
@@ -679,7 +655,7 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
         long accountId = getResourceId("account", vpc, networkVO);
         long zoneId = getResourceId("zone", vpc, networkVO);
 
-        return new NsxOpObject.Builder()
+        return new SDNProviderOpObject.Builder()
                 .vpcVO(vpc)
                 .networkVO(networkVO)
                 .domainId(domainId)
@@ -694,25 +670,24 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
         for (LoadBalancingRule loadBalancingRule : rules) {
             IPAddressVO publicIp = ipAddressDao.findByIpAndDcId(network.getDataCenterId(),
                     loadBalancingRule.getSourceIp().addr());
-            NsxOpObject nsxObject = getNsxOpObject(network);
+            SDNProviderOpObject nsxObject = getNsxOpObject(network);
 
             List<NsxLoadBalancerMember> lbMembers = getLoadBalancerMembers(loadBalancingRule);
-            NsxNetworkRule networkRule = new NsxNetworkRule.Builder()
-                    .setDomainId(nsxObject.getDomainId())
-                    .setAccountId(nsxObject.getAccountId())
-                    .setZoneId(nsxObject.getZoneId())
-                    .setNetworkResourceId(nsxObject.getNetworkResourceId())
-                    .setNetworkResourceName(nsxObject.getNetworkResourceName())
-                    .setVpcResource(nsxObject.isVpcResource())
-                    .setMemberList(lbMembers)
-                    .setPublicIp(LoadBalancerContainer.Scheme.Public == loadBalancingRule.getScheme() ?
-                            publicIp.getAddress().addr() : loadBalancingRule.getSourceIp().addr())
-                    .setPublicPort(String.valueOf(loadBalancingRule.getSourcePortStart()))
-                    .setPrivatePort(String.valueOf(loadBalancingRule.getDefaultPortStart()))
-                    .setRuleId(loadBalancingRule.getId())
-                    .setProtocol(loadBalancingRule.getLbProtocol().toUpperCase(Locale.ROOT))
-                    .setAlgorithm(loadBalancingRule.getAlgorithm())
-                    .build();
+            NsxNetworkRule networkRule = new NsxNetworkRule();
+            networkRule.setDomainId(nsxObject.getDomainId());
+            networkRule.setAccountId(nsxObject.getAccountId());
+            networkRule.setZoneId(nsxObject.getZoneId());
+            networkRule.setNetworkResourceId(nsxObject.getNetworkResourceId());
+            networkRule.setNetworkResourceName(nsxObject.getNetworkResourceName());
+            networkRule.setVpcResource(nsxObject.isVpcResource());
+            networkRule.setPublicIp(LoadBalancerContainer.Scheme.Public == loadBalancingRule.getScheme() ?
+                            publicIp.getAddress().addr() : loadBalancingRule.getSourceIp().addr());
+            networkRule.setPublicPort(String.valueOf(loadBalancingRule.getSourcePortStart()));
+            networkRule.setPrivatePort(String.valueOf(loadBalancingRule.getDefaultPortStart()));
+            networkRule.setRuleId(loadBalancingRule.getId());
+            networkRule.setProtocol(loadBalancingRule.getLbProtocol().toUpperCase(Locale.ROOT));
+            networkRule.setAlgorithm(loadBalancingRule.getAlgorithm());
+            networkRule.setMemberList(lbMembers);
             if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(loadBalancingRule.getState())) {
                 result &= nsxService.createLbRule(networkRule);
             } else if (loadBalancingRule.getState() == FirewallRule.State.Revoke) {
@@ -757,7 +732,7 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
         List<NsxNetworkRule> nsxDelNetworkRules = new ArrayList<>();
         boolean success = true;
         for (NetworkACLItem rule : rules) {
-            String privatePort = getPrivatePortRangeForACLRule(rule);
+            String privatePort = PortForwardingServiceProvider.getPrivatePortRangeForACLRule(rule);
             NsxNetworkRule networkRule = getNsxNetworkRuleForAcl(rule, privatePort);
             if (Arrays.asList(NetworkACLItem.State.Active, NetworkACLItem.State.Add).contains(rule.getState())) {
                 success = success && nsxService.addFirewallRules(network, List.of(networkRule));
@@ -779,7 +754,7 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
     public boolean reorderAclRules(Vpc vpc, List<? extends Network> networks, List<? extends NetworkACLItem> networkACLItems) {
         List<NsxNetworkRule> aclRulesList = new ArrayList<>();
         for (NetworkACLItem rule : networkACLItems) {
-            String privatePort = getPrivatePortRangeForACLRule(rule);
+            String privatePort = PortForwardingServiceProvider.getPrivatePortRangeForACLRule(rule);
             aclRulesList.add(getNsxNetworkRuleForAcl(rule, privatePort));
         }
         for (Network network: networks) {
@@ -795,18 +770,18 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
     }
 
     private NsxNetworkRule getNsxNetworkRuleForAcl(NetworkACLItem rule, String privatePort) {
-        return new NsxNetworkRule.Builder()
-                .setRuleId(rule.getId())
-                .setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ? transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"))
-                .setAclAction(transformActionValue(rule.getAction()))
-                .setTrafficType(rule.getTrafficType().toString())
-                .setProtocol(rule.getProtocol().toUpperCase())
-                .setPublicPort(String.valueOf(rule.getSourcePortStart()))
-                .setPrivatePort(privatePort)
-                .setIcmpCode(rule.getIcmpCode())
-                .setIcmpType(rule.getIcmpType())
-                .setService(Network.Service.NetworkACL)
-                .build();
+        NsxNetworkRule nsxNetworkRule = new NsxNetworkRule();
+        nsxNetworkRule.setRuleId(rule.getId());
+        nsxNetworkRule.setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ? transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"));
+        nsxNetworkRule.setTrafficType(rule.getTrafficType().toString());
+        nsxNetworkRule.setProtocol(rule.getProtocol().toUpperCase());
+        nsxNetworkRule.setPublicPort(String.valueOf(rule.getSourcePortStart()));
+        nsxNetworkRule.setPrivatePort(privatePort);
+        nsxNetworkRule.setIcmpCode(rule.getIcmpCode());
+        nsxNetworkRule.setIcmpType(rule.getIcmpType());
+        nsxNetworkRule.setService(Network.Service.NetworkACL);
+        nsxNetworkRule.setAclAction(transformActionValue(rule.getAction()));
+        return nsxNetworkRule;
     }
         @Override
     public boolean applyFWRules(Network network, List<? extends FirewallRule> rules) throws ResourceUnavailableException {
@@ -817,20 +792,19 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
         List<NsxNetworkRule> nsxAddNetworkRules = new ArrayList<>();
         List<NsxNetworkRule> nsxDelNetworkRules = new ArrayList<>();
         for (FirewallRule rule : rules) {
-            NsxNetworkRule networkRule = new NsxNetworkRule.Builder()
-                    .setRuleId(rule.getId())
-                    .setAclAction(NsxNetworkRule.NsxRuleAction.ALLOW)
-                    .setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ?
-                            transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"))
-                    .setDestinationCidrList(Objects.nonNull(rule.getDestinationCidrList()) ?
-                            transformCidrListValues(rule.getDestinationCidrList()) : List.of("ANY"))
-                    .setIcmpCode(rule.getIcmpCode())
-                    .setIcmpType(rule.getIcmpType())
-                    .setPrivatePort(getPrivatePortRange(rule))
-                    .setTrafficType(rule.getTrafficType().toString())
-                    .setService(Network.Service.Firewall)
-                    .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT))
-                    .build();
+            NsxNetworkRule networkRule = new NsxNetworkRule();
+            networkRule.setRuleId(rule.getId());
+            networkRule.setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ?
+                        transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"));
+            networkRule.setDestinationCidrList(Objects.nonNull(rule.getDestinationCidrList()) ?
+                        transformCidrListValues(rule.getDestinationCidrList()) : List.of("ANY"));
+            networkRule.setIcmpCode(rule.getIcmpCode());
+            networkRule.setIcmpType(rule.getIcmpType());
+            networkRule.setPrivatePort(PortForwardingServiceProvider.getPrivatePortRange(rule));
+            networkRule.setTrafficType(rule.getTrafficType().toString());
+            networkRule.setService(Network.Service.Firewall);
+            networkRule.setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT));
+            networkRule.setAclAction(NsxNetworkRule.NsxRuleAction.ALLOW);
             if (rule.getState() == FirewallRule.State.Add) {
                 nsxAddNetworkRules.add(networkRule);
             } else if (rule.getState() == FirewallRule.State.Revoke) {
diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java
index 7c44a7324fb..a807155a2dc 100644
--- a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java
+++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java
@@ -36,6 +36,7 @@ import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.dao.PhysicalNetworkDao;
 import com.cloud.network.dao.PhysicalNetworkVO;
+import com.cloud.network.element.PortForwardingServiceProvider;
 import com.cloud.network.lb.LoadBalancingRule;
 import com.cloud.network.rules.FirewallRule;
 import com.cloud.network.rules.FirewallRuleVO;
@@ -72,7 +73,6 @@ import org.mockito.junit.MockitoJUnitRunner;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.List;
 
 import static org.junit.Assert.assertTrue;
@@ -307,84 +307,60 @@ public class NsxElementTest {
         assertNotNull(vpcNetworkPair.second());
     }
 
-    private Method getPublicPortRangeMethod() throws NoSuchMethodException {
-        Method method = NsxElement.class.getDeclaredMethod("getPublicPortRange", PortForwardingRule.class);
-        method.setAccessible(true);
-        return method;
-    }
-
-    private Method getPrivatePFPortRangeMethod() throws NoSuchMethodException {
-        Method method = NsxElement.class.getDeclaredMethod("getPrivatePFPortRange", PortForwardingRule.class);
-        method.setAccessible(true);
-        return method;
-    }
-
-    private Method getPrivatePortRangeMethod() throws NoSuchMethodException {
-        Method method = NsxElement.class.getDeclaredMethod("getPrivatePortRange", FirewallRule.class);
-        method.setAccessible(true);
-        return method;
-    }
-
-    private Method getPrivatePortRangeForACLRuleMethod() throws NoSuchMethodException {
-        Method method = NsxElement.class.getDeclaredMethod("getPrivatePortRangeForACLRule", NetworkACLItem.class);
-        method.setAccessible(true);
-        return method;
-    }
-
     @Test
     public void testGetPublicPortRangeWhenStartAndEndPortNumbersAreDifferent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         PortForwardingRule rule = new PortForwardingRuleVO("1", 11L, 80, 90, new Ip("172.30.10.11"), 8080, 8090, "tcp", 12L,
                 5L, 2L, 15L);
-        assertEquals("80-90", getPublicPortRangeMethod().invoke(null, rule));
+        assertEquals("80-90", PortForwardingServiceProvider.getPublicPortRange(rule));
     }
 
     @Test
     public void testGetPublicPortRangeWhenStartAndEndPortNumbersAreSame() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         PortForwardingRule rule = new PortForwardingRuleVO("1", 11L, 80, 80, new Ip("172.30.10.11"), 8080, 8080, "tcp", 12L,
                 5L, 2L, 15L);
-        assertEquals("80", getPublicPortRangeMethod().invoke(null, rule));
+        assertEquals("80", PortForwardingServiceProvider.getPublicPortRange(rule));
     }
 
     @Test
     public void testGetPrivatePFPortRangeWhenStartAndEndPortNumbersAreDifferent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         PortForwardingRule rule = new PortForwardingRuleVO("1", 11L, 80, 90, new Ip("172.30.10.11"), 8080, 8090, "tcp", 12L,
                 5L, 2L, 15L);
-        assertEquals("8080-8090", getPrivatePFPortRangeMethod().invoke(null, rule));
+        assertEquals("8080-8090", PortForwardingServiceProvider.getPrivatePFPortRange(rule));
     }
 
     @Test
     public void testGetPrivatePFPortRangeWhenStartAndEndPortNumbersAreSame() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         PortForwardingRule rule = new PortForwardingRuleVO("1", 11L, 80, 80, new Ip("172.30.10.11"), 8080, 8080, "tcp", 12L,
                 5L, 2L, 15L);
-        assertEquals("8080", getPrivatePFPortRangeMethod().invoke(null, rule));
+        assertEquals("8080", PortForwardingServiceProvider.getPrivatePFPortRange(rule));
     }
 
     @Test
     public void testGetPrivatePortRangeWhenStartAndEndPortNumbersAreSame() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         FirewallRuleVO rule = new FirewallRuleVO("1", 11L, 80, 80, "tcp", 23L, 5L, 2L,
         FirewallRule.Purpose.Firewall, List.of("172.30.10.0/24"), null, null, null, null, FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User);
-        assertEquals("80", getPrivatePortRangeMethod().invoke(null, rule));
+        assertEquals("80", PortForwardingServiceProvider.getPrivatePortRange(rule));
     }
 
     @Test
     public void testGetPrivatePortRangeWhenStartAndEndPortNumbersAreDifferent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         FirewallRuleVO rule = new FirewallRuleVO("1", 11L, 80, 90, "tcp", 23L, 5L, 2L,
                 FirewallRule.Purpose.Firewall, List.of("172.30.10.0/24"), null, null, null, null, FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User);
-        assertEquals("80-90", getPrivatePortRangeMethod().invoke(null, rule));
+        assertEquals("80-90", PortForwardingServiceProvider.getPrivatePortRange(rule));
     }
 
     @Test
     public void testGetPrivatePortRangeForACLWhenStartAndEndPortNumbersAreSame() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         NetworkACLItem rule = new NetworkACLItemVO(80, 80, "udp", 10L, List.of("172.30.10.0/24"), null, null, NetworkACLItem.TrafficType.Ingress, NetworkACLItem.Action.Allow,
         2, null);
-        assertEquals("80", getPrivatePortRangeForACLRuleMethod().invoke(null, rule));
+        assertEquals("80", PortForwardingServiceProvider.getPrivatePortRangeForACLRule(rule));
     }
 
     @Test
     public void testGetPrivatePortRangeForACLWhenStartAndEndPortNumbersAreDifferent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         NetworkACLItem rule = new NetworkACLItemVO(80, 90, "udp", 10L, List.of("172.30.10.0/24"), null, null, NetworkACLItem.TrafficType.Ingress, NetworkACLItem.Action.Allow,
                 2, null);
-        assertEquals("80-90", getPrivatePortRangeForACLRuleMethod().invoke(null, rule));
+        assertEquals("80-90", PortForwardingServiceProvider.getPrivatePortRangeForACLRule(rule));
     }
 
     @Test
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxOpObject.java b/server/src/main/java/com/cloud/network/SDNProviderOpObject.java
similarity index 94%
rename from plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxOpObject.java
rename to server/src/main/java/com/cloud/network/SDNProviderOpObject.java
index bb411249b88..902e47545e9 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxOpObject.java
+++ b/server/src/main/java/com/cloud/network/SDNProviderOpObject.java
@@ -14,14 +14,14 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package org.apache.cloudstack.resource;
+package com.cloud.network;
 
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.vpc.VpcVO;
 
 import java.util.Objects;
 
-public class NsxOpObject {
+public class SDNProviderOpObject {
     VpcVO vpcVO;
     NetworkVO networkVO;
     long accountId;
@@ -116,8 +116,8 @@ public class NsxOpObject {
             return this;
         }
 
-        public NsxOpObject build() {
-            NsxOpObject object = new NsxOpObject();
+        public SDNProviderOpObject build() {
+            SDNProviderOpObject object = new SDNProviderOpObject();
             object.setVpcVO(this.vpcVO);
             object.setNetworkVO(this.networkVO);
             object.setDomainId(this.domainId);
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index 121e0cbe223..aef3f06f28d 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -454,6 +454,24 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                             State.Enabled, null, false, false, false, NetworkOffering.NetworkMode.ROUTED, null, false);
 
                 }
+
+                // configure default vpc offering with Netris as network service provider in NAT mode
+                if (_vpcOffDao.findByUniqueName(VpcOffering.DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME) == null) {
+                    logger.debug(String.format("Creating default VPC offering for Netris network service provider %s in NAT mode", VpcOffering.DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME));
+                    final Map<Service, Set<Provider>> svcProviderMap = new HashMap<>();
+                    final Set<Provider> defaultProviders = Set.of(Provider.Netris);
+                    for (final Service svc : getSupportedServices()) {
+                        if (List.of(Service.UserData, Service.Dhcp, Service.Dns).contains(svc)) {
+                            final Set<Provider> userDataProvider = Set.of(Provider.VPCVirtualRouter);
+                            svcProviderMap.put(svc, userDataProvider);
+                        } else {
+                            svcProviderMap.put(svc, defaultProviders);
+                        }
+                    }
+                    createVpcOffering(VpcOffering.DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME, VpcOffering.DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME, svcProviderMap, false,
+                            State.Enabled, null, false, false, false, NetworkOffering.NetworkMode.NATTED, null, false);
+
+                }
             }
         });
 
diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
index f21b84cdc02..76efd339fa9 100644
--- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
@@ -1220,6 +1220,10 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
                 // Offering #14 - network offering for Netris provider for VPCs - ROUTED mode
                 createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_ROUTED_NETRIS_OFFERING_FOR_VPC, "Offering for Netris enabled networks on VPCs - ROUTED mode",
                         NetworkOffering.NetworkMode.ROUTED, true, true, Provider.Netris);
+
+                // Offering #15 - network offering for Netris provider for VPCs - NATTED mode
+                createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_NAT_NETRIS_OFFERING_FOR_VPC, "Offering for Netris enabled networks on VPCs - NAT mode",
+                        NetworkOffering.NetworkMode.NATTED, true, true, Provider.Netris);
             }
         });
     }


(cloudstack) 31/44: Add support to add IPv6 Public IP range as IPAM Allocation / Subnet on Netris (#36)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 57609c79c42bd1a0debab6cf9fbc37d8c6ca56a3
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Sat Jan 11 21:12:32 2025 -0500

    Add support to add IPv6 Public IP range as IPAM Allocation / Subnet on Netris (#36)
    
    * Add support to add IPv6 Public IP range as IPAM Allocation / Subnet on Netris
    
    * Add ipam alloc and subnet for the ipv6 subnet associated to the vpc tier network
    
    * remove commented code
---
 .../engine/orchestration/NetworkOrchestrator.java  |  8 +++
 .../src/main/java/com/cloud/dc/dao/VlanDao.java    |  2 +
 .../main/java/com/cloud/dc/dao/VlanDaoImpl.java    | 27 ++++++++
 .../agent/api/CreateNetrisVnetCommand.java         |  9 +++
 .../cloudstack/service/NetrisApiClientImpl.java    | 34 +++++++---
 .../cloudstack/service/NetrisServiceImpl.java      | 77 +++++++++++-----------
 .../java/com/cloud/network/NetworkServiceImpl.java |  4 --
 ui/src/views/infra/network/IpRangesTabPublic.vue   | 10 +++
 8 files changed, 121 insertions(+), 50 deletions(-)

diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 99a3ec3caca..7e1ca652f47 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -43,6 +43,7 @@ import com.cloud.bgp.BGPService;
 import com.cloud.dc.VlanDetailsVO;
 import com.cloud.dc.dao.ASNumberDao;
 import com.cloud.dc.dao.VlanDetailsDao;
+import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao;
 import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NsxProviderDao;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
@@ -361,6 +362,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
     private ASNumberDao asNumberDao;
     @Inject
     private BGPService bgpService;
+    @Inject
+    private Ipv6GuestPrefixSubnetNetworkMapDao ipv6GuestPrefixSubnetNetworkMapDao;
 
     @Override
     public List<NetworkGuru> getNetworkGurus() {
@@ -824,6 +827,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                         if (domainId != null && aclType == ACLType.Domain) {
                             _networksDao.addDomainToNetwork(id, domainId, subdomainAccess == null || subdomainAccess);
                         }
+                        String ipv6Cidr = network.getIp6Cidr();
+                        String ipv6Gateway = network.getIp6Gateway();
+                        if (StringUtils.isNoneBlank(ipv6Cidr, ipv6Gateway)) {
+                            ipv6Service.assignIpv6SubnetToNetwork(ipv6Cidr, networkPersisted.getId());
+                        }
                     }
                 });
                 guru.setup(network, relatedFile);
diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java
index 84f38f05441..a6c267bb189 100644
--- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java
+++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java
@@ -64,4 +64,6 @@ public interface VlanDao extends GenericDao<VlanVO, Long> {
     List<VlanVO> listIpv6RangeByZoneIdAndVlanId(long zoneId, String vlanId);
 
     List<VlanVO> listIpv6SupportingVlansByZone(long zoneId);
+
+    List<VlanVO> listVlansForExternalNetworkProvider(long zoneId, String detailKey);
 }
diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java
index 461a9a13b10..d9fad3cad12 100644
--- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.dc.VlanDetailsVO;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Component;
@@ -67,6 +68,8 @@ public class VlanDaoImpl extends GenericDaoBase<VlanVO, Long> implements VlanDao
     protected SearchBuilder<VlanVO> ZoneVlanIp6Search;
     protected SearchBuilder<VlanVO> ZoneIp6Search;
     protected SearchBuilder<VlanVO> ZoneVlansSearch;
+    protected SearchBuilder<VlanVO> ProviderVlanSearch;
+    protected SearchBuilder<VlanDetailsVO> VlanDetailsProviderSearch;
 
     protected SearchBuilder<AccountVlanMapVO> AccountVlanMapSearch;
     protected SearchBuilder<DomainVlanMapVO> DomainVlanMapSearch;
@@ -79,6 +82,8 @@ public class VlanDaoImpl extends GenericDaoBase<VlanVO, Long> implements VlanDao
     protected DomainVlanMapDao _domainVlanMapDao;
     @Inject
     protected IPAddressDao _ipAddressDao;
+    @Inject
+    protected VlanDetailsDao vlanDetailsDao;
 
     @Override
     public VlanVO findByZoneAndVlanId(long zoneId, String vlanId) {
@@ -277,6 +282,19 @@ public class VlanDaoImpl extends GenericDaoBase<VlanVO, Long> implements VlanDao
         ZoneVlansSearch.and("zoneId", ZoneVlansSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
         ZoneVlansSearch.and("vlan", ZoneVlansSearch.entity().getVlanTag(), SearchCriteria.Op.IN);
         ZoneVlansSearch.done();
+
+        ProviderVlanSearch = createSearchBuilder();
+        ProviderVlanSearch.and("removed", ProviderVlanSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
+        ProviderVlanSearch.and("dataCenterId", ProviderVlanSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
+        VlanDetailsProviderSearch = vlanDetailsDao.createSearchBuilder();
+        VlanDetailsProviderSearch.and("name", VlanDetailsProviderSearch.entity().getName(), SearchCriteria.Op.EQ);
+        VlanDetailsProviderSearch.and("value", VlanDetailsProviderSearch.entity().getValue(), SearchCriteria.Op.EQ);
+        ProviderVlanSearch.join("VlanDetailsProviderSearch", VlanDetailsProviderSearch, ProviderVlanSearch.entity().getId(),
+                VlanDetailsProviderSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
+
+        VlanDetailsProviderSearch.done();
+        ProviderVlanSearch.done();
+
         return result;
     }
 
@@ -434,4 +452,13 @@ public class VlanDaoImpl extends GenericDaoBase<VlanVO, Long> implements VlanDao
         return listBy(sc);
     }
 
+    @Override
+    public List<VlanVO> listVlansForExternalNetworkProvider(long zoneId, String detailKey) {
+        SearchCriteria<VlanVO> sc = ProviderVlanSearch.create();
+        sc.setParameters("dataCenterId", zoneId);
+        sc.setJoinParameters("VlanDetailsProviderSearch", "name", detailKey);
+        sc.setJoinParameters("VlanDetailsProviderSearch", "value", "true");
+        return search(sc, null);
+    }
+
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
index 7ba6c9250fd..7095c4549b1 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
@@ -23,6 +23,7 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
     private Integer vxlanId;
     private String gateway;
     private String netrisTag;
+    private String ipv6Cidr;
 
     public CreateNetrisVnetCommand(Long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, String cidr, String gateway, boolean isVpc) {
         super(zoneId, accountId, domainId, vNetName, networkId, isVpc);
@@ -64,4 +65,12 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
     public void setNetrisTag(String netrisTag) {
         this.netrisTag = netrisTag;
     }
+
+    public String getIpv6Cidr() {
+        return ipv6Cidr;
+    }
+
+    public void setIpv6Cidr(String ipv6Cidr) {
+        this.ipv6Cidr = ipv6Cidr;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 8d6d088407e..bfed67a915a 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -594,6 +594,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String netrisTag = cmd.getNetrisTag();
         String netmask = vnetCidr.split("/")[1];
         String netrisGateway = cmd.getGateway() + "/" + netmask;
+        String netrisV6Cidr = cmd.getIpv6Cidr();
         boolean isVpc = cmd.isVpc();
 
         String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
@@ -614,9 +615,18 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
 
         createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
+        if (Objects.nonNull(netrisV6Cidr)) {
+            String netrisV6IpamAllocationName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, netrisV6Cidr);
+            String netrisV6SubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, netrisV6Cidr) ;
+            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);
+        }
         logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
 
-        VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, netrisGateway, vxlanId, netrisTag);
+        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);
@@ -1107,25 +1117,33 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
-    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String netrisGateway, Integer vxlanId, String netrisTag) {
+    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String netrisGateway, String netrisV6Gateway, Integer vxlanId, String netrisTag) {
         logger.debug("Creating Netris VPC vNet {} for CIDR {}", netrisVnetName, netrisGateway);
         try {
             VnetAddBody vnetBody = new VnetAddBody();
 
             vnetBody.setCustomAnycastMac("");
 
-            VnetAddBodyGateways gateways = new VnetAddBodyGateways();
-            gateways.prefix(netrisGateway);
-            gateways.setDhcpEnabled(false);
+            VnetAddBodyGateways gatewayV4 = new VnetAddBodyGateways();
+            gatewayV4.prefix(netrisGateway);
+            gatewayV4.setDhcpEnabled(false);
             VnetAddBodyDhcp dhcp = new VnetAddBodyDhcp();
             dhcp.setEnd("");
             dhcp.setStart("");
             dhcp.setOptionSet(new VnetAddBodyDhcpOptionSet());
-            gateways.setDhcp(dhcp);
+            gatewayV4.setDhcp(dhcp);
             List<VnetAddBodyGateways> gatewaysList = new ArrayList<>();
-            gatewaysList.add(gateways);
-            vnetBody.setGateways(gatewaysList);
+            gatewaysList.add(gatewayV4);
+
+            if (Objects.nonNull(netrisV6Gateway)) {
+                VnetAddBodyGateways gatewayV6 = new VnetAddBodyGateways();
+                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);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index cb2047e32e8..0a9fe4ac239 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -18,17 +18,15 @@ package org.apache.cloudstack.service;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
-import com.cloud.dc.VlanDetailsVO;
 import com.cloud.dc.VlanVO;
 import com.cloud.dc.dao.VlanDao;
-import com.cloud.dc.dao.VlanDetailsDao;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.network.IpAddress;
+import com.cloud.network.Ipv6GuestPrefixSubnetNetworkMapVO;
 import com.cloud.network.Network;
 import com.cloud.network.Networks;
 import com.cloud.network.SDNProviderNetworkRule;
-import com.cloud.network.dao.IPAddressDao;
-import com.cloud.network.dao.IPAddressVO;
+import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao;
 import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
@@ -37,6 +35,7 @@ import com.cloud.network.dao.PhysicalNetworkVO;
 import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.netris.NetrisService;
 import com.cloud.network.vpc.Vpc;
+import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
 import inet.ipaddr.IPAddress;
@@ -82,11 +81,9 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
     @Inject
     private PhysicalNetworkDao physicalNetworkDao;
     @Inject
-    private IPAddressDao ipAddressDao;
-    @Inject
     private VlanDao vlanDao;
     @Inject
-    private VlanDetailsDao vlanDetailsDao;
+    private Ipv6GuestPrefixSubnetNetworkMapDao  ipv6PrefixNetworkMapDao;
 
     @Override
     public String getConfigComponentName() {
@@ -138,10 +135,10 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
     /**
      * Prepare the Netris Public Range to be used by CloudStack after the zone is created and the Netris provider is added
      */
-    public SetupNetrisPublicRangeCommand createSetupPublicRangeCommand(long zoneId, String gateway, String netmask, String ipRange) {
+    private Pair<String, String> getAllocationAndSubnet(String gateway, String netmask, String ipRange) {
         String superCidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, netmask);
         String subnetNatCidr = calculateSubnetCidrFromIpRange(ipRange);
-        return new SetupNetrisPublicRangeCommand(zoneId, superCidr, subnetNatCidr);
+        return new Pair<>(superCidr, subnetNatCidr);
     }
 
     @Override
@@ -151,38 +148,38 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         if (CollectionUtils.isEmpty(physicalNetworks)) {
             return false;
         }
-        for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
-            List<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
-            List<Long> vlanDbIds = publicIps.stream()
-                    .filter(x -> !x.isForSystemVms())
-                    .map(IPAddressVO::getVlanId)
-                    .collect(Collectors.toList());
-            if (CollectionUtils.isEmpty(vlanDbIds)) {
-                String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
-                logger.error(msg);
-                throw new CloudRuntimeException(msg);
+
+        List<VlanVO> providerVlanIds = vlanDao.listVlansForExternalNetworkProvider(zoneId, ApiConstants.NETRIS_DETAIL_KEY);
+        List<Long> vlanDbIds = providerVlanIds.stream().map(VlanVO::getId).collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(vlanDbIds)) {
+            String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
+            logger.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+        for (Long vlanDbId : vlanDbIds) {
+            VlanVO vlanRecord = vlanDao.findById(vlanDbId);
+
+            String gateway = Objects.nonNull(vlanRecord.getVlanGateway()) ? vlanRecord.getVlanGateway() : vlanRecord.getIp6Gateway();
+            String netmask = vlanRecord.getVlanNetmask();
+            String ipRange = vlanRecord.getIpRange();
+            String ip6Cidr = vlanRecord.getIp6Cidr();
+            SetupNetrisPublicRangeCommand cmd = null;
+            if (NetUtils.isValidIp4(gateway)) {
+                Pair<String, String> allocationAndSubnet = getAllocationAndSubnet(gateway, netmask, ipRange);
+                cmd = new SetupNetrisPublicRangeCommand(zoneId, allocationAndSubnet.first(), allocationAndSubnet.second());
+            } else if (NetUtils.isValidIp6(gateway)) {
+                cmd = new SetupNetrisPublicRangeCommand(zoneId, ip6Cidr, ip6Cidr);
             }
-            for (Long vlanDbId : vlanDbIds) {
-                VlanVO vlanRecord = vlanDao.findById(vlanDbId);
-                if (vlanRecord == null) {
-                    logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
-                    return false;
-                }
-                VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
-                if (vlanDetail == null) {
-                    logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
-                    continue;
-                }
-                String gateway = vlanRecord.getVlanGateway();
-                String netmask = vlanRecord.getVlanNetmask();
-                String ipRange = vlanRecord.getIpRange();
-                SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
-                NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
-                if (!answer.getResult()) {
-                    throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
-                }
+
+            if (cmd == null) {
+                throw new CloudRuntimeException("Incorrect gateway and netmask details provided for the Netris Public IP range setup");
+            }
+            NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+            if (!answer.getResult()) {
+                throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
             }
         }
+
         return true;
     }
 
@@ -208,6 +205,10 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         cmd.setVxlanId(Integer.parseInt(vxlanId));
         NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId);
         cmd.setNetrisTag(netrisProvider.getNetrisTag());
+        if (Objects.nonNull(networkId)) {
+            Ipv6GuestPrefixSubnetNetworkMapVO ipv6PrefixNetworkMapVO = ipv6PrefixNetworkMapDao.findByNetworkId(networkId);
+            cmd.setIpv6Cidr(ipv6PrefixNetworkMapVO.getSubnet());
+        }
         NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
         return answer.getResult();
     }
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 15ff36b6c9e..3d218f3b0a5 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -1800,10 +1800,6 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
             _networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.hideIpAddressUsage, String.valueOf(hideIpAddressUsage), false));
         }
 
-        if (ip6GatewayCidr != null) {
-            ipv6Service.assignIpv6SubnetToNetwork(ip6Cidr, network.getId());
-        }
-
         // assign to network
         if (NetworkOffering.NetworkMode.ROUTED.equals(ntwkOff.getNetworkMode())) {
             routedIpv4Manager.assignIpv4SubnetToNetwork(network);
diff --git a/ui/src/views/infra/network/IpRangesTabPublic.vue b/ui/src/views/infra/network/IpRangesTabPublic.vue
index 0f3610f5e05..62f2418c975 100644
--- a/ui/src/views/infra/network/IpRangesTabPublic.vue
+++ b/ui/src/views/infra/network/IpRangesTabPublic.vue
@@ -230,6 +230,16 @@
           <a-form-item name="ip6cidr" ref="ip6cidr" :label="$t('label.cidr')" class="form__item">
             <a-input v-model:value="form.ip6cidr" />
           </a-form-item>
+          <a-form-item name="provider" ref="provider">
+            <template #label>
+              <tooltip-label :title="$t('label.provider')"/>
+            </template>
+            <a-select v-model:value="form.provider">
+              <a-select-option value=""></a-select-option>
+              <a-select-option value="NSX">{{ $t('label.nsx') }}</a-select-option>
+              <a-select-option value="Netris">{{ $t('label.netris') }}</a-select-option>
+            </a-select>
+          </a-form-item>
         </div>
         <div v-else>
           <a-form-item name="gateway" ref="gateway" :label="$t('label.gateway')" class="form__item">


(cloudstack) 15/44: Merge tag '4.20.0.0' into netris-420-backport

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit a9982c4d4d269ccb8a1f45446c14246cadd7503d
Merge: c087de4adfe 2fe3fcef7c7
Author: nvazquez <ni...@gmail.com>
AuthorDate: Tue Dec 3 11:35:08 2024 -0300

    Merge tag '4.20.0.0' into netris-420-backport

 agent/pom.xml                                      |   2 +-
 api/pom.xml                                        |   2 +-
 .../main/java/com/cloud/user/AccountService.java   |   2 +
 .../command/user/backup/ListBackupScheduleCmd.java |   2 +-
 client/pom.xml                                     |   2 +-
 core/pom.xml                                       |   2 +-
 debian/changelog                                   |   6 +
 debian/control                                     |   2 +-
 developer/pom.xml                                  |   2 +-
 engine/api/pom.xml                                 |   2 +-
 engine/components-api/pom.xml                      |   2 +-
 engine/orchestration/pom.xml                       |   2 +-
 engine/pom.xml                                     |   2 +-
 engine/schema/pom.xml                              |   2 +-
 .../com/cloud/storage/dao/VolumeStatsDaoImpl.java  |   9 +-
 .../main/java/com/cloud/vm/dao/VmStatsDaoImpl.java |   9 +-
 engine/service/pom.xml                             |   2 +-
 engine/storage/cache/pom.xml                       |   2 +-
 engine/storage/configdrive/pom.xml                 |   2 +-
 engine/storage/datamotion/pom.xml                  |   2 +-
 .../motion/StorageSystemDataMotionStrategy.java    |  78 +++---
 engine/storage/image/pom.xml                       |   2 +-
 .../storage/image/TemplateServiceImpl.java         |   5 +
 engine/storage/integration-test/pom.xml            |   2 +-
 engine/storage/object/pom.xml                      |   2 +-
 engine/storage/pom.xml                             |   2 +-
 engine/storage/snapshot/pom.xml                    |   2 +-
 engine/storage/volume/pom.xml                      |   2 +-
 engine/userdata/cloud-init/pom.xml                 |   2 +-
 engine/userdata/pom.xml                            |   2 +-
 framework/agent-lb/pom.xml                         |   2 +-
 framework/ca/pom.xml                               |   2 +-
 framework/cluster/pom.xml                          |   2 +-
 framework/config/pom.xml                           |   2 +-
 framework/db/pom.xml                               |   2 +-
 .../main/java/com/cloud/utils/db/GenericDao.java   |   8 -
 .../java/com/cloud/utils/db/GenericDaoBase.java    |  48 ++--
 framework/direct-download/pom.xml                  |   2 +-
 framework/events/pom.xml                           |   2 +-
 framework/ipc/pom.xml                              |   2 +-
 framework/jobs/pom.xml                             |   2 +-
 framework/managed-context/pom.xml                  |   2 +-
 framework/pom.xml                                  |   2 +-
 framework/quota/pom.xml                            |   2 +-
 framework/rest/pom.xml                             |   2 +-
 framework/security/pom.xml                         |   2 +-
 framework/spring/lifecycle/pom.xml                 |   2 +-
 framework/spring/module/pom.xml                    |   2 +-
 packaging/el8/cloud.spec                           |   1 +
 plugins/acl/dynamic-role-based/pom.xml             |   2 +-
 plugins/acl/project-role-based/pom.xml             |   2 +-
 plugins/acl/static-role-based/pom.xml              |   2 +-
 .../explicit-dedication/pom.xml                    |   2 +-
 .../host-affinity/pom.xml                          |   2 +-
 .../host-anti-affinity/pom.xml                     |   2 +-
 .../non-strict-host-affinity/pom.xml               |   2 +-
 .../non-strict-host-anti-affinity/pom.xml          |   2 +-
 plugins/alert-handlers/snmp-alerts/pom.xml         |   2 +-
 plugins/alert-handlers/syslog-alerts/pom.xml       |   2 +-
 plugins/api/discovery/pom.xml                      |   2 +-
 plugins/api/rate-limit/pom.xml                     |   2 +-
 plugins/api/solidfire-intg-test/pom.xml            |   2 +-
 plugins/api/vmware-sioc/pom.xml                    |   2 +-
 plugins/backup/dummy/pom.xml                       |   2 +-
 plugins/backup/nas/pom.xml                         |   2 +-
 plugins/backup/networker/pom.xml                   |   2 +-
 plugins/backup/veeam/pom.xml                       |   2 +-
 plugins/ca/root-ca/pom.xml                         |   2 +-
 plugins/database/mysql-ha/pom.xml                  |   2 +-
 plugins/database/quota/pom.xml                     |   2 +-
 .../cloudstack/api/command/QuotaBalanceCmd.java    |  14 +-
 .../cloudstack/api/command/QuotaCreditsCmd.java    |   6 +
 .../cloudstack/api/command/QuotaStatementCmd.java  |   6 +
 plugins/dedicated-resources/pom.xml                |   2 +-
 .../implicit-dedication/pom.xml                    |   2 +-
 .../user-concentrated-pod/pom.xml                  |   2 +-
 .../deployment-planners/user-dispersing/pom.xml    |   2 +-
 plugins/drs/cluster/balanced/pom.xml               |   2 +-
 plugins/drs/cluster/condensed/pom.xml              |   2 +-
 plugins/event-bus/inmemory/pom.xml                 |   2 +-
 plugins/event-bus/kafka/pom.xml                    |   2 +-
 plugins/event-bus/rabbitmq/pom.xml                 |   2 +-
 plugins/event-bus/webhook/pom.xml                  |   2 +-
 plugins/ha-planners/skip-heurestics/pom.xml        |   2 +-
 plugins/host-allocators/random/pom.xml             |   2 +-
 plugins/hypervisors/baremetal/pom.xml              |   2 +-
 plugins/hypervisors/hyperv/pom.xml                 |   2 +-
 plugins/hypervisors/kvm/pom.xml                    |   2 +-
 .../kvm/resource/LibvirtComputingResource.java     |  19 +-
 .../kvm/resource/LibvirtStoragePoolDef.java        |   2 +-
 .../hypervisor/kvm/resource/MigrateKVMAsync.java   |   2 +
 .../LibvirtOvsFetchInterfaceCommandWrapper.java    |   2 +-
 .../kvm/storage/IscsiAdmStorageAdaptor.java        |   2 +-
 .../kvm/storage/KVMStoragePoolManager.java         |   2 +-
 .../kvm/storage/KVMStorageProcessor.java           |  31 ++-
 .../kvm/storage/LibvirtStorageAdaptor.java         |  58 ++++-
 .../kvm/storage/ManagedNfsStorageAdaptor.java      |   2 +-
 .../kvm/storage/MultipathSCSIAdapterBase.java      |   2 +-
 .../kvm/storage/ScaleIOStorageAdaptor.java         |   2 +-
 .../hypervisor/kvm/storage/StorageAdaptor.java     |   2 +-
 .../kvm/resource/LibvirtComputingResourceTest.java | 113 +++++++++
 .../kvm/storage/LibvirtStorageAdaptorTest.java     |   2 +-
 plugins/hypervisors/ovm/pom.xml                    |   2 +-
 plugins/hypervisors/ovm3/pom.xml                   |   2 +-
 plugins/hypervisors/simulator/pom.xml              |   2 +-
 plugins/hypervisors/ucs/pom.xml                    |   2 +-
 plugins/hypervisors/vmware/pom.xml                 |   2 +-
 plugins/hypervisors/xenserver/pom.xml              |   2 +-
 plugins/integrations/cloudian/pom.xml              |   2 +-
 plugins/integrations/kubernetes-service/pom.xml    |   2 +-
 plugins/integrations/prometheus/pom.xml            |   2 +-
 plugins/metrics/pom.xml                            |   2 +-
 plugins/network-elements/bigswitch/pom.xml         |   2 +-
 plugins/network-elements/brocade-vcs/pom.xml       |   2 +-
 plugins/network-elements/cisco-vnmc/pom.xml        |   2 +-
 plugins/network-elements/dns-notifier/pom.xml      |   2 +-
 .../network-elements/elastic-loadbalancer/pom.xml  |   2 +-
 plugins/network-elements/globodns/pom.xml          |   2 +-
 .../network-elements/internal-loadbalancer/pom.xml |   2 +-
 plugins/network-elements/juniper-contrail/pom.xml  |   2 +-
 .../contrail/management/MockAccountManager.java    |   5 +
 plugins/network-elements/netscaler/pom.xml         |   2 +-
 plugins/network-elements/nicira-nvp/pom.xml        |   2 +-
 plugins/network-elements/nsx/pom.xml               |   2 +-
 plugins/network-elements/opendaylight/pom.xml      |   2 +-
 plugins/network-elements/ovs/pom.xml               |   2 +-
 plugins/network-elements/palo-alto/pom.xml         |   2 +-
 plugins/network-elements/stratosphere-ssp/pom.xml  |   2 +-
 plugins/network-elements/tungsten/pom.xml          |   2 +-
 plugins/network-elements/vxlan/pom.xml             |   2 +-
 .../outofbandmanagement-drivers/ipmitool/pom.xml   |   2 +-
 .../nested-cloudstack/pom.xml                      |   2 +-
 .../outofbandmanagement-drivers/redfish/pom.xml    |   2 +-
 plugins/pom.xml                                    |   2 +-
 plugins/shutdown/pom.xml                           |   2 +-
 plugins/storage-allocators/random/pom.xml          |   2 +-
 plugins/storage/image/default/pom.xml              |   2 +-
 plugins/storage/image/s3/pom.xml                   |   2 +-
 plugins/storage/image/sample/pom.xml               |   2 +-
 plugins/storage/image/swift/pom.xml                |   2 +-
 plugins/storage/object/ceph/pom.xml                |   2 +-
 .../driver/CephObjectStoreDriverImpl.java          |  13 +-
 .../lifecycle/CephObjectStoreLifeCycleImpl.java    |  10 +-
 plugins/storage/object/minio/pom.xml               |   2 +-
 plugins/storage/object/simulator/pom.xml           |   2 +-
 plugins/storage/sharedfs/storagevm/pom.xml         |   2 +-
 plugins/storage/volume/adaptive/pom.xml            |   2 +-
 plugins/storage/volume/cloudbyte/pom.xml           |   2 +-
 plugins/storage/volume/datera/pom.xml              |   2 +-
 plugins/storage/volume/default/pom.xml             |   2 +-
 plugins/storage/volume/flasharray/pom.xml          |   2 +-
 plugins/storage/volume/linstor/CHANGELOG.md        |  13 +
 plugins/storage/volume/linstor/pom.xml             |   2 +-
 .../kvm/storage/LinstorStorageAdaptor.java         |  79 ++++--
 .../storage/datastore/util/LinstorUtil.java        |  24 ++
 plugins/storage/volume/nexenta/pom.xml             |   2 +-
 plugins/storage/volume/primera/pom.xml             |   2 +-
 plugins/storage/volume/sample/pom.xml              |   2 +-
 plugins/storage/volume/scaleio/pom.xml             |   2 +-
 plugins/storage/volume/solidfire/pom.xml           |   2 +-
 plugins/storage/volume/storpool/pom.xml            |   2 +-
 .../kvm/storage/StorPoolStorageAdaptor.java        |   2 +-
 .../storage/datastore/util/StorPoolUtil.java       |   4 +-
 .../storage/snapshot/StorPoolSnapshotStrategy.java |  58 +++--
 plugins/user-authenticators/ldap/pom.xml           |   2 +-
 plugins/user-authenticators/md5/pom.xml            |   2 +-
 plugins/user-authenticators/oauth2/pom.xml         |   2 +-
 plugins/user-authenticators/pbkdf2/pom.xml         |   2 +-
 plugins/user-authenticators/plain-text/pom.xml     |   2 +-
 plugins/user-authenticators/saml2/pom.xml          |   2 +-
 .../api/command/ListAndSwitchSAMLAccountCmd.java   |   7 +-
 .../apache/cloudstack/saml/SAML2AuthManager.java   |   6 +
 .../cloudstack/saml/SAML2AuthManagerImpl.java      |   3 +-
 .../java/org/apache/cloudstack/saml/SAMLUtils.java |  27 ++-
 .../command/ListAndSwitchSAMLAccountCmdTest.java   |  25 +-
 plugins/user-authenticators/sha256salted/pom.xml   |   2 +-
 .../static-pin/pom.xml                             |   2 +-
 .../user-two-factor-authenticators/totp/pom.xml    |   2 +-
 pom.xml                                            |   2 +-
 quickcloud/pom.xml                                 |   2 +-
 server/pom.xml                                     |   2 +-
 server/src/main/java/com/cloud/api/ApiServer.java  |  26 +-
 server/src/main/java/com/cloud/api/ApiServlet.java |  54 +++--
 .../com/cloud/api/dispatch/ParamProcessWorker.java |  42 ++--
 .../java/com/cloud/api/query/QueryManagerImpl.java |  26 +-
 .../java/com/cloud/user/AccountManagerImpl.java    |  14 ++
 .../cloud/api/dispatch/ParamProcessWorkerTest.java | 138 +++++++++--
 .../com/cloud/user/MockAccountManagerImpl.java     |   5 +
 .../java/com/cloud/user/MockUsageEventDao.java     |   5 -
 services/console-proxy/pom.xml                     |   2 +-
 services/console-proxy/rdpconsole/pom.xml          |   2 +-
 services/console-proxy/server/pom.xml              |   2 +-
 services/pom.xml                                   |   2 +-
 services/secondary-storage/controller/pom.xml      |   2 +-
 services/secondary-storage/pom.xml                 |   2 +-
 services/secondary-storage/server/pom.xml          |   2 +-
 .../storage/formatinspector/Qcow2HeaderField.java  |  51 ++++
 .../storage/formatinspector/Qcow2Inspector.java    | 268 +++++++++++++++++++++
 .../resource/NfsSecondaryStorageResource.java      |  14 +-
 .../storage/template/DownloadManagerImpl.java      |  39 ++-
 systemvm/debian/opt/cloud/bin/cs/CsFile.py         |   2 +-
 systemvm/pom.xml                                   |   2 +-
 test/integration/smoke/test_login.py               |   1 +
 test/pom.xml                                       |   2 +-
 tools/apidoc/pom.xml                               |   2 +-
 tools/build/build_asf.sh                           |  10 +-
 tools/checkstyle/pom.xml                           |   2 +-
 tools/devcloud-kvm/pom.xml                         |   2 +-
 tools/devcloud4/pom.xml                            |   2 +-
 tools/docker/Dockerfile                            |   2 +-
 tools/docker/Dockerfile.marvin                     |   4 +-
 tools/marvin/pom.xml                               |   2 +-
 tools/marvin/setup.py                              |   2 +-
 tools/pom.xml                                      |   2 +-
 ui/public/locales/pt_BR.json                       |   4 +-
 ui/src/api/index.js                                |  11 +-
 ui/src/components/view/DetailSettings.vue          |  13 +-
 ui/src/config/section/compute.js                   |   2 +-
 ui/src/store/modules/user.js                       |  10 +-
 ui/src/views/compute/KubernetesServiceTab.vue      |   2 +-
 ui/src/views/compute/StartVirtualMachine.vue       |  16 +-
 .../views/image/UpdateTemplateIsoPermissions.vue   |  18 +-
 ui/src/views/infra/UpdatePrimaryStorage.vue        |  10 +-
 ui/tests/unit/components/view/ActionButton.spec.js |  10 +
 ui/tests/unit/views/compute/MigrateWizard.spec.js  |  10 +
 ui/vue.config.js                                   |   6 +-
 usage/pom.xml                                      |   2 +-
 utils/pom.xml                                      |   2 +-
 utils/src/main/java/com/cloud/utils/HttpUtils.java |  42 +++-
 .../test/java/com/cloud/utils/HttpUtilsTest.java   |  86 ++++++-
 vmware-base/pom.xml                                |   2 +-
 231 files changed, 1527 insertions(+), 443 deletions(-)


(cloudstack) 17/44: Merge branch 'netris-release' into netris-phase2

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit b70f72abec22e4fb3efc4daa0985e80c4f71c68b
Merge: 7b0d982a448 d24b6f229d6
Author: nvazquez <ni...@gmail.com>
AuthorDate: Tue Dec 3 14:42:16 2024 -0300

    Merge branch 'netris-release' into netris-phase2

 agent/pom.xml                                      |   2 +-
 api/pom.xml                                        |   2 +-
 .../main/java/com/cloud/user/AccountService.java   |   2 +
 .../command/user/backup/ListBackupScheduleCmd.java |   2 +-
 client/pom.xml                                     |   2 +-
 core/pom.xml                                       |   2 +-
 debian/changelog                                   |   6 +
 debian/control                                     |   2 +-
 developer/pom.xml                                  |   2 +-
 engine/api/pom.xml                                 |   2 +-
 engine/components-api/pom.xml                      |   2 +-
 engine/orchestration/pom.xml                       |   2 +-
 engine/pom.xml                                     |   2 +-
 engine/schema/pom.xml                              |   2 +-
 .../com/cloud/storage/dao/VolumeStatsDaoImpl.java  |   9 +-
 .../main/java/com/cloud/vm/dao/VmStatsDaoImpl.java |   9 +-
 engine/service/pom.xml                             |   2 +-
 engine/storage/cache/pom.xml                       |   2 +-
 engine/storage/configdrive/pom.xml                 |   2 +-
 engine/storage/datamotion/pom.xml                  |   2 +-
 .../motion/StorageSystemDataMotionStrategy.java    |  78 +++---
 engine/storage/image/pom.xml                       |   2 +-
 .../storage/image/TemplateServiceImpl.java         |   5 +
 engine/storage/integration-test/pom.xml            |   2 +-
 engine/storage/object/pom.xml                      |   2 +-
 engine/storage/pom.xml                             |   2 +-
 engine/storage/snapshot/pom.xml                    |   2 +-
 engine/storage/volume/pom.xml                      |   2 +-
 engine/userdata/cloud-init/pom.xml                 |   2 +-
 engine/userdata/pom.xml                            |   2 +-
 framework/agent-lb/pom.xml                         |   2 +-
 framework/ca/pom.xml                               |   2 +-
 framework/cluster/pom.xml                          |   2 +-
 framework/config/pom.xml                           |   2 +-
 framework/db/pom.xml                               |   2 +-
 .../main/java/com/cloud/utils/db/GenericDao.java   |   8 -
 .../java/com/cloud/utils/db/GenericDaoBase.java    |  48 ++--
 framework/direct-download/pom.xml                  |   2 +-
 framework/events/pom.xml                           |   2 +-
 framework/ipc/pom.xml                              |   2 +-
 framework/jobs/pom.xml                             |   2 +-
 framework/managed-context/pom.xml                  |   2 +-
 framework/pom.xml                                  |   2 +-
 framework/quota/pom.xml                            |   2 +-
 framework/rest/pom.xml                             |   2 +-
 framework/security/pom.xml                         |   2 +-
 framework/spring/lifecycle/pom.xml                 |   2 +-
 framework/spring/module/pom.xml                    |   2 +-
 packaging/el8/cloud.spec                           |   1 +
 plugins/acl/dynamic-role-based/pom.xml             |   2 +-
 plugins/acl/project-role-based/pom.xml             |   2 +-
 plugins/acl/static-role-based/pom.xml              |   2 +-
 .../explicit-dedication/pom.xml                    |   2 +-
 .../host-affinity/pom.xml                          |   2 +-
 .../host-anti-affinity/pom.xml                     |   2 +-
 .../non-strict-host-affinity/pom.xml               |   2 +-
 .../non-strict-host-anti-affinity/pom.xml          |   2 +-
 plugins/alert-handlers/snmp-alerts/pom.xml         |   2 +-
 plugins/alert-handlers/syslog-alerts/pom.xml       |   2 +-
 plugins/api/discovery/pom.xml                      |   2 +-
 plugins/api/rate-limit/pom.xml                     |   2 +-
 plugins/api/solidfire-intg-test/pom.xml            |   2 +-
 plugins/api/vmware-sioc/pom.xml                    |   2 +-
 plugins/backup/dummy/pom.xml                       |   2 +-
 plugins/backup/nas/pom.xml                         |   2 +-
 plugins/backup/networker/pom.xml                   |   2 +-
 plugins/backup/veeam/pom.xml                       |   2 +-
 plugins/ca/root-ca/pom.xml                         |   2 +-
 plugins/database/mysql-ha/pom.xml                  |   2 +-
 plugins/database/quota/pom.xml                     |   2 +-
 .../cloudstack/api/command/QuotaBalanceCmd.java    |  14 +-
 .../cloudstack/api/command/QuotaCreditsCmd.java    |   6 +
 .../cloudstack/api/command/QuotaStatementCmd.java  |   6 +
 plugins/dedicated-resources/pom.xml                |   2 +-
 .../implicit-dedication/pom.xml                    |   2 +-
 .../user-concentrated-pod/pom.xml                  |   2 +-
 .../deployment-planners/user-dispersing/pom.xml    |   2 +-
 plugins/drs/cluster/balanced/pom.xml               |   2 +-
 plugins/drs/cluster/condensed/pom.xml              |   2 +-
 plugins/event-bus/inmemory/pom.xml                 |   2 +-
 plugins/event-bus/kafka/pom.xml                    |   2 +-
 plugins/event-bus/rabbitmq/pom.xml                 |   2 +-
 plugins/event-bus/webhook/pom.xml                  |   2 +-
 plugins/ha-planners/skip-heurestics/pom.xml        |   2 +-
 plugins/host-allocators/random/pom.xml             |   2 +-
 plugins/hypervisors/baremetal/pom.xml              |   2 +-
 plugins/hypervisors/hyperv/pom.xml                 |   2 +-
 plugins/hypervisors/kvm/pom.xml                    |   2 +-
 .../kvm/resource/LibvirtComputingResource.java     |  19 +-
 .../kvm/resource/LibvirtStoragePoolDef.java        |   2 +-
 .../hypervisor/kvm/resource/MigrateKVMAsync.java   |   2 +
 .../LibvirtOvsFetchInterfaceCommandWrapper.java    |   2 +-
 .../kvm/storage/IscsiAdmStorageAdaptor.java        |   2 +-
 .../kvm/storage/KVMStoragePoolManager.java         |   2 +-
 .../kvm/storage/KVMStorageProcessor.java           |  31 ++-
 .../kvm/storage/LibvirtStorageAdaptor.java         |  58 ++++-
 .../kvm/storage/ManagedNfsStorageAdaptor.java      |   2 +-
 .../kvm/storage/MultipathSCSIAdapterBase.java      |   2 +-
 .../kvm/storage/ScaleIOStorageAdaptor.java         |   2 +-
 .../hypervisor/kvm/storage/StorageAdaptor.java     |   2 +-
 .../kvm/resource/LibvirtComputingResourceTest.java | 113 +++++++++
 .../kvm/storage/LibvirtStorageAdaptorTest.java     |   2 +-
 plugins/hypervisors/ovm/pom.xml                    |   2 +-
 plugins/hypervisors/ovm3/pom.xml                   |   2 +-
 plugins/hypervisors/simulator/pom.xml              |   2 +-
 plugins/hypervisors/ucs/pom.xml                    |   2 +-
 plugins/hypervisors/vmware/pom.xml                 |   2 +-
 plugins/hypervisors/xenserver/pom.xml              |   2 +-
 plugins/integrations/cloudian/pom.xml              |   2 +-
 plugins/integrations/kubernetes-service/pom.xml    |   2 +-
 plugins/integrations/prometheus/pom.xml            |   2 +-
 plugins/metrics/pom.xml                            |   2 +-
 plugins/network-elements/bigswitch/pom.xml         |   2 +-
 plugins/network-elements/brocade-vcs/pom.xml       |   2 +-
 plugins/network-elements/cisco-vnmc/pom.xml        |   2 +-
 plugins/network-elements/dns-notifier/pom.xml      |   2 +-
 .../network-elements/elastic-loadbalancer/pom.xml  |   2 +-
 plugins/network-elements/globodns/pom.xml          |   2 +-
 .../network-elements/internal-loadbalancer/pom.xml |   2 +-
 plugins/network-elements/juniper-contrail/pom.xml  |   2 +-
 .../contrail/management/MockAccountManager.java    |   5 +
 plugins/network-elements/netscaler/pom.xml         |   2 +-
 plugins/network-elements/nicira-nvp/pom.xml        |   2 +-
 plugins/network-elements/nsx/pom.xml               |   2 +-
 plugins/network-elements/opendaylight/pom.xml      |   2 +-
 plugins/network-elements/ovs/pom.xml               |   2 +-
 plugins/network-elements/palo-alto/pom.xml         |   2 +-
 plugins/network-elements/stratosphere-ssp/pom.xml  |   2 +-
 plugins/network-elements/tungsten/pom.xml          |   2 +-
 plugins/network-elements/vxlan/pom.xml             |   2 +-
 .../outofbandmanagement-drivers/ipmitool/pom.xml   |   2 +-
 .../nested-cloudstack/pom.xml                      |   2 +-
 .../outofbandmanagement-drivers/redfish/pom.xml    |   2 +-
 plugins/pom.xml                                    |   2 +-
 plugins/shutdown/pom.xml                           |   2 +-
 plugins/storage-allocators/random/pom.xml          |   2 +-
 plugins/storage/image/default/pom.xml              |   2 +-
 plugins/storage/image/s3/pom.xml                   |   2 +-
 plugins/storage/image/sample/pom.xml               |   2 +-
 plugins/storage/image/swift/pom.xml                |   2 +-
 plugins/storage/object/ceph/pom.xml                |   2 +-
 .../driver/CephObjectStoreDriverImpl.java          |  13 +-
 .../lifecycle/CephObjectStoreLifeCycleImpl.java    |  10 +-
 plugins/storage/object/minio/pom.xml               |   2 +-
 plugins/storage/object/simulator/pom.xml           |   2 +-
 plugins/storage/sharedfs/storagevm/pom.xml         |   2 +-
 plugins/storage/volume/adaptive/pom.xml            |   2 +-
 plugins/storage/volume/cloudbyte/pom.xml           |   2 +-
 plugins/storage/volume/datera/pom.xml              |   2 +-
 plugins/storage/volume/default/pom.xml             |   2 +-
 plugins/storage/volume/flasharray/pom.xml          |   2 +-
 plugins/storage/volume/linstor/CHANGELOG.md        |  13 +
 plugins/storage/volume/linstor/pom.xml             |   2 +-
 .../kvm/storage/LinstorStorageAdaptor.java         |  79 ++++--
 .../storage/datastore/util/LinstorUtil.java        |  24 ++
 plugins/storage/volume/nexenta/pom.xml             |   2 +-
 plugins/storage/volume/primera/pom.xml             |   2 +-
 plugins/storage/volume/sample/pom.xml              |   2 +-
 plugins/storage/volume/scaleio/pom.xml             |   2 +-
 plugins/storage/volume/solidfire/pom.xml           |   2 +-
 plugins/storage/volume/storpool/pom.xml            |   2 +-
 .../kvm/storage/StorPoolStorageAdaptor.java        |   2 +-
 .../storage/datastore/util/StorPoolUtil.java       |   4 +-
 .../storage/snapshot/StorPoolSnapshotStrategy.java |  58 +++--
 plugins/user-authenticators/ldap/pom.xml           |   2 +-
 plugins/user-authenticators/md5/pom.xml            |   2 +-
 plugins/user-authenticators/oauth2/pom.xml         |   2 +-
 plugins/user-authenticators/pbkdf2/pom.xml         |   2 +-
 plugins/user-authenticators/plain-text/pom.xml     |   2 +-
 plugins/user-authenticators/saml2/pom.xml          |   2 +-
 .../api/command/ListAndSwitchSAMLAccountCmd.java   |   7 +-
 .../apache/cloudstack/saml/SAML2AuthManager.java   |   6 +
 .../cloudstack/saml/SAML2AuthManagerImpl.java      |   3 +-
 .../java/org/apache/cloudstack/saml/SAMLUtils.java |  27 ++-
 .../command/ListAndSwitchSAMLAccountCmdTest.java   |  25 +-
 plugins/user-authenticators/sha256salted/pom.xml   |   2 +-
 .../static-pin/pom.xml                             |   2 +-
 .../user-two-factor-authenticators/totp/pom.xml    |   2 +-
 pom.xml                                            |   2 +-
 quickcloud/pom.xml                                 |   2 +-
 server/pom.xml                                     |   2 +-
 server/src/main/java/com/cloud/api/ApiServer.java  |  26 +-
 server/src/main/java/com/cloud/api/ApiServlet.java |  54 +++--
 .../com/cloud/api/dispatch/ParamProcessWorker.java |  42 ++--
 .../java/com/cloud/api/query/QueryManagerImpl.java |  26 +-
 .../java/com/cloud/user/AccountManagerImpl.java    |  14 ++
 .../cloud/api/dispatch/ParamProcessWorkerTest.java | 138 +++++++++--
 .../com/cloud/user/MockAccountManagerImpl.java     |   5 +
 .../java/com/cloud/user/MockUsageEventDao.java     |   5 -
 services/console-proxy/pom.xml                     |   2 +-
 services/console-proxy/rdpconsole/pom.xml          |   2 +-
 services/console-proxy/server/pom.xml              |   2 +-
 services/pom.xml                                   |   2 +-
 services/secondary-storage/controller/pom.xml      |   2 +-
 services/secondary-storage/pom.xml                 |   2 +-
 services/secondary-storage/server/pom.xml          |   2 +-
 .../storage/formatinspector/Qcow2HeaderField.java  |  51 ++++
 .../storage/formatinspector/Qcow2Inspector.java    | 268 +++++++++++++++++++++
 .../resource/NfsSecondaryStorageResource.java      |  14 +-
 .../storage/template/DownloadManagerImpl.java      |  39 ++-
 systemvm/debian/opt/cloud/bin/cs/CsFile.py         |   2 +-
 systemvm/pom.xml                                   |   2 +-
 test/integration/smoke/test_login.py               |   1 +
 test/pom.xml                                       |   2 +-
 tools/apidoc/pom.xml                               |   2 +-
 tools/build/build_asf.sh                           |  10 +-
 tools/checkstyle/pom.xml                           |   2 +-
 tools/devcloud-kvm/pom.xml                         |   2 +-
 tools/devcloud4/pom.xml                            |   2 +-
 tools/docker/Dockerfile                            |   2 +-
 tools/docker/Dockerfile.marvin                     |   4 +-
 tools/marvin/pom.xml                               |   2 +-
 tools/marvin/setup.py                              |   2 +-
 tools/pom.xml                                      |   2 +-
 ui/public/locales/pt_BR.json                       |   4 +-
 ui/src/api/index.js                                |  11 +-
 ui/src/components/view/DetailSettings.vue          |  13 +-
 ui/src/config/section/compute.js                   |   2 +-
 ui/src/store/modules/user.js                       |  10 +-
 ui/src/views/compute/KubernetesServiceTab.vue      |   2 +-
 ui/src/views/compute/StartVirtualMachine.vue       |  16 +-
 .../views/image/UpdateTemplateIsoPermissions.vue   |  18 +-
 ui/src/views/infra/UpdatePrimaryStorage.vue        |  10 +-
 ui/tests/unit/components/view/ActionButton.spec.js |  10 +
 ui/tests/unit/views/compute/MigrateWizard.spec.js  |  10 +
 ui/vue.config.js                                   |   6 +-
 usage/pom.xml                                      |   2 +-
 utils/pom.xml                                      |   2 +-
 utils/src/main/java/com/cloud/utils/HttpUtils.java |  42 +++-
 .../test/java/com/cloud/utils/HttpUtilsTest.java   |  86 ++++++-
 vmware-base/pom.xml                                |   2 +-
 231 files changed, 1527 insertions(+), 443 deletions(-)



(cloudstack) 32/44: Phase5 - Support for ACLs in Netris (#31)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit f70cc1c3b722fcaa8dc2bd7e6200cc02529514a4
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Mon Feb 3 21:43:45 2025 -0500

    Phase5 - Support for ACLs in Netris (#31)
    
    * Add support for Netris ACLs
    
    * acl support
    
    * Make acl api call to netris to create the rule
    
    * refactor add acl rule to populate the right fields
    
    * support icmp type acl rule
    
    * acl rule creation - move netrisnetworkRule
    
    * Update ACL naming on Netris
    
    * Add support for Deletion of netris acls
    
    * Add support to delete and re-order ACL rules
    
    * support creation of default acl rules and replacing acl rules
    
    * fix NSXNetworkRule
---
 .../com/cloud/network/SDNProviderNetworkRule.java  | 196 ++++++++++++++++++---
 .../cloud/network/netris/NetrisNetworkRule.java    |  74 ++++++++
 .../com/cloud/network/netris/NetrisService.java    |   6 +
 .../agent/api/CreateNetrisACLCommand.java          | 120 +++++++++++++
 .../agent/api/DeleteNetrisACLCommand.java}         |  35 ++--
 ...NetrisNetworkRule.java => NetrisPortGroup.java} |   9 +-
 .../apache/cloudstack/resource/NetrisResource.java |  24 ++-
 .../resource/NetrisResourceObjectUtils.java        |   3 +-
 .../apache/cloudstack/service/NetrisApiClient.java |   4 +
 .../cloudstack/service/NetrisApiClientImpl.java    | 186 ++++++++++++++++---
 .../apache/cloudstack/service/NetrisElement.java   | 131 +++++++++++---
 .../cloudstack/service/NetrisServiceImpl.java      |  94 +++++++++-
 .../apache/cloudstack/resource/NsxNetworkRule.java |  11 +-
 .../apache/cloudstack/service/NsxApiClient.java    |  13 +-
 .../org/apache/cloudstack/service/NsxElement.java  | 110 ++++++------
 .../apache/cloudstack/service/NsxServiceImpl.java  |  17 +-
 .../cloudstack/service/NsxApiClientTest.java       |   6 +-
 .../cloudstack/service/NetrisServiceMockTest.java  |  14 ++
 18 files changed, 894 insertions(+), 159 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java
index 15238f85bb6..a22db4287dc 100644
--- a/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java
+++ b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java
@@ -20,27 +20,27 @@ import java.util.List;
 
 public class SDNProviderNetworkRule {
 
-    private long domainId;
-    private long accountId;
-    private long zoneId;
-    private Long networkResourceId;
-    private String networkResourceName;
-    private boolean isVpcResource;
-    private long vmId;
-    private long ruleId;
-    private String publicIp;
-    private String vmIp;
-    private String publicPort;
-    private String privatePort;
-    private String protocol;
-    private String algorithm;
-    private List<String> sourceCidrList;
-    private List<String> destinationCidrList;
-    private Integer icmpCode;
-
-    private Integer icmpType;
-    private String trafficType;
-    private Network.Service service;
+    protected long domainId;
+    protected long accountId;
+    protected long zoneId;
+    protected Long networkResourceId;
+    protected String networkResourceName;
+    protected boolean isVpcResource;
+    protected long vmId;
+    protected long ruleId;
+    protected String publicIp;
+    protected String vmIp;
+    protected String publicPort;
+    protected String privatePort;
+    protected String protocol;
+    protected String algorithm;
+    protected List<String> sourceCidrList;
+    protected List<String> destinationCidrList;
+    protected Integer icmpCode;
+
+    protected Integer icmpType;
+    protected String trafficType;
+    protected Network.Service service;
 
     public long getDomainId() {
         return domainId;
@@ -201,4 +201,158 @@ public class SDNProviderNetworkRule {
     public void setTrafficType(String trafficType) {
         this.trafficType = trafficType;
     }
+
+    public static class Builder {
+        public long domainId;
+        public long accountId;
+        public long zoneId;
+        public Long networkResourceId;
+        public String networkResourceName;
+        public boolean isVpcResource;
+        public long vmId;
+
+        public long ruleId;
+        public String publicIp;
+        public String vmIp;
+        public String publicPort;
+        public String privatePort;
+        public String protocol;
+        public String algorithm;
+        public List<String> sourceCidrList;
+        public List<String> destinationCidrList;
+        public String trafficType;
+        public Integer icmpType;
+        public Integer icmpCode;
+        public Network.Service service;
+
+        public Builder() {
+            // Default constructor
+        }
+
+        public Builder setDomainId(long domainId) {
+            this.domainId = domainId;
+            return this;
+        }
+
+        public Builder setAccountId(long accountId) {
+            this.accountId = accountId;
+            return this;
+        }
+
+        public Builder setZoneId(long zoneId) {
+            this.zoneId = zoneId;
+            return this;
+        }
+
+        public Builder setNetworkResourceId(Long networkResourceId) {
+            this.networkResourceId = networkResourceId;
+            return this;
+        }
+
+        public Builder setNetworkResourceName(String networkResourceName) {
+            this.networkResourceName = networkResourceName;
+            return this;
+        }
+
+        public Builder setVpcResource(boolean isVpcResource) {
+            this.isVpcResource = isVpcResource;
+            return this;
+        }
+
+
+        public Builder setVmId(long vmId) {
+            this.vmId = vmId;
+            return this;
+        }
+
+        public Builder setRuleId(long ruleId) {
+            this.ruleId = ruleId;
+            return this;
+        }
+
+        public Builder setPublicIp(String publicIp) {
+            this.publicIp = publicIp;
+            return this;
+        }
+
+        public Builder setVmIp(String vmIp) {
+            this.vmIp = vmIp;
+            return this;
+        }
+
+        public Builder setPublicPort(String publicPort) {
+            this.publicPort = publicPort;
+            return this;
+        }
+
+        public Builder setPrivatePort(String privatePort) {
+            this.privatePort = privatePort;
+            return this;
+        }
+
+        public Builder setProtocol(String protocol) {
+            this.protocol = protocol;
+            return this;
+        }
+
+        public Builder setAlgorithm(String algorithm) {
+            this.algorithm = algorithm;
+            return this;
+        }
+
+        public Builder setTrafficType(String trafficType) {
+            this.trafficType = trafficType;
+            return this;
+        }
+
+        public Builder setIcmpType(Integer icmpType) {
+            this.icmpType = icmpType;
+            return this;
+        }
+
+        public Builder setIcmpCode(Integer icmpCode) {
+            this.icmpCode = icmpCode;
+            return this;
+        }
+
+        public Builder setSourceCidrList(List<String> sourceCidrList) {
+            this.sourceCidrList = sourceCidrList;
+            return this;
+        }
+
+        public Builder setDestinationCidrList(List<String> destinationCidrList) {
+            this.destinationCidrList = destinationCidrList;
+            return this;
+        }
+
+        public Builder setService(Network.Service service) {
+            this.service = service;
+            return this;
+        }
+
+        public SDNProviderNetworkRule build() {
+            SDNProviderNetworkRule rule = new SDNProviderNetworkRule();
+            rule.setDomainId(this.domainId);
+            rule.setAccountId(this.accountId);
+            rule.setZoneId(this.zoneId);
+            rule.setNetworkResourceId(this.networkResourceId);
+            rule.setNetworkResourceName(this.networkResourceName);
+            rule.setVpcResource(this.isVpcResource);
+            rule.setVmId(this.vmId);
+            rule.setVmIp(this.vmIp);
+            rule.setPublicIp(this.publicIp);
+            rule.setPublicPort(this.publicPort);
+            rule.setPrivatePort(this.privatePort);
+            rule.setProtocol(this.protocol);
+            rule.setRuleId(this.ruleId);
+            rule.setAlgorithm(this.algorithm);
+            rule.setIcmpType(this.icmpType);
+            rule.setIcmpCode(this.icmpCode);
+            rule.setSourceCidrList(this.sourceCidrList);
+            rule.setDestinationCidrList(this.destinationCidrList);
+            rule.setTrafficType(this.trafficType);
+            rule.setService(service);
+            return rule;
+        }
+    }
 }
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
new file mode 100644
index 00000000000..67612141f65
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
@@ -0,0 +1,74 @@
+// 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 com.cloud.network.netris;
+
+import com.cloud.network.SDNProviderNetworkRule;
+
+
+public class NetrisNetworkRule {
+    public enum NetrisRuleAction {
+        PERMIT, DENY
+    }
+
+    private SDNProviderNetworkRule baseRule;
+    private NetrisRuleAction aclAction;
+    private String reason;
+
+    public NetrisNetworkRule(Builder builder) {
+        this.baseRule = builder.baseRule;
+        this.aclAction = builder.aclAction;
+        this.reason = builder.reason;
+    }
+
+    public NetrisRuleAction getAclAction() {
+        return aclAction;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    public SDNProviderNetworkRule getBaseRule() {
+        return baseRule;
+    }
+
+    // Builder class extending the parent builder
+    public static class Builder {
+        private SDNProviderNetworkRule baseRule;
+        private NetrisRuleAction aclAction;
+        private String reason;
+
+        public Builder baseRule(SDNProviderNetworkRule baseRule) {
+            this.baseRule = baseRule;
+            return this;
+        }
+
+        public Builder aclAction(NetrisRuleAction aclAction) {
+            this.aclAction = aclAction;
+            return this;
+        }
+
+        public Builder reason(String reason) {
+            this.reason = reason;
+            return this;
+        }
+
+        public NetrisNetworkRule build() {
+            return new NetrisNetworkRule(this);
+        }
+    }
+}
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index 6fd860f26be..750a82ffe70 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -17,9 +17,12 @@
 package com.cloud.network.netris;
 
 import com.cloud.network.IpAddress;
+import com.cloud.network.Network;
 import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.vpc.Vpc;
 
+import java.util.List;
+
 public interface NetrisService {
     boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId);
 
@@ -43,6 +46,9 @@ public interface NetrisService {
 
     boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp);
 
+    boolean addFirewallRules(Network network, List<NetrisNetworkRule> firewallRules);
+    boolean deleteFirewallRules(Network network, List<NetrisNetworkRule> firewallRules);
+
     boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute);
 
     boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisACLCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisACLCommand.java
new file mode 100644
index 00000000000..ea39cd7f366
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisACLCommand.java
@@ -0,0 +1,120 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+import org.apache.cloudstack.resource.NetrisPortGroup;
+
+public class CreateNetrisACLCommand extends NetrisCommand {
+    private String vpcName;
+    private Long vpcId;
+    private String action;
+    private String destPrefix;
+    private String sourcePrefix;
+    private Integer destPortStart;
+    private Integer destPortEnd;
+    private Integer icmpType;
+    private String protocol;
+    private NetrisPortGroup portGroup;
+    private String netrisAclName;
+    private String reason;
+
+
+    public CreateNetrisACLCommand(long zoneId, Long accountId, Long domainId, String name, Long id, String vpcName, Long vpcId, boolean isVpc, String action,
+                                  String sourcePrefix, String destPrefix, Integer destPortStart, Integer destPortEnd, String protocol) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.vpcName = vpcName;
+        this.vpcId = vpcId;
+        this.action = action;
+        this.sourcePrefix = sourcePrefix;
+        this.destPrefix = destPrefix;
+        this.destPortStart = destPortStart;
+        this.destPortEnd = destPortEnd;
+        this.protocol = protocol;
+    }
+
+    public String getVpcName() {
+        return vpcName;
+    }
+
+    public void setVpcName(String vpcName) {
+        this.vpcName = vpcName;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public void setVpcId(Long vpcId) {
+        this.vpcId = vpcId;
+    }
+
+    public String getAction() {
+        return action;
+    }
+
+    public String getDestPrefix() {
+        return destPrefix;
+    }
+
+    public String getSourcePrefix() {
+        return sourcePrefix;
+    }
+
+    public Integer getDestPortStart() {
+        return destPortStart;
+    }
+
+    public Integer getDestPortEnd() {
+        return destPortEnd;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public NetrisPortGroup getPortGroup() {
+        return portGroup;
+    }
+
+    public void setPortGroup(NetrisPortGroup portGroup) {
+        this.portGroup = portGroup;
+    }
+
+    public Integer getIcmpType() {
+        return icmpType;
+    }
+
+    public void setIcmpType(Integer icmpType) {
+        this.icmpType = icmpType;
+    }
+
+    public String getNetrisAclName() {
+        return netrisAclName;
+    }
+
+    public void setNetrisAclName(String netrisAclName) {
+        this.netrisAclName = netrisAclName;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    public void setReason(String reason) {
+        this.reason = reason;
+    }
+}
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisACLCommand.java
similarity index 54%
copy from plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisACLCommand.java
index 153143b6d18..e4a4993a257 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisACLCommand.java
@@ -14,34 +14,33 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package org.apache.cloudstack.resource;
-
-import com.cloud.network.SDNProviderNetworkRule;
+package org.apache.cloudstack.agent.api;
 
 import java.util.List;
 
-public class NsxNetworkRule extends SDNProviderNetworkRule {
-
-    public enum NsxRuleAction {
-        ALLOW, DROP
+public class DeleteNetrisACLCommand extends NetrisCommand {
+    Long vpcId;
+    String vpcName;
+    List<String> aclRuleNames;
+    public DeleteNetrisACLCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc, Long vpcId, String vpcName) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.vpcId = vpcId;
+        this.vpcName = vpcName;
     }
 
-    private List<NsxLoadBalancerMember> memberList;
-    private NsxRuleAction aclAction;
-
-    public List<NsxLoadBalancerMember> getMemberList() {
-        return memberList;
+    public Long getVpcId() {
+        return vpcId;
     }
 
-    public void setMemberList(List<NsxLoadBalancerMember> memberList) {
-        this.memberList = memberList;
+    public String getVpcName() {
+        return vpcName;
     }
 
-    public NsxRuleAction getAclAction() {
-        return aclAction;
+    public List<String> getAclRuleNames() {
+        return aclRuleNames;
     }
 
-    public void setAclAction(NsxRuleAction aclAction) {
-        this.aclAction = aclAction;
+    public void setAclRuleNames(List<String> aclRuleNames) {
+        this.aclRuleNames = aclRuleNames;
     }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisNetworkRule.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisPortGroup.java
similarity index 82%
rename from plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisNetworkRule.java
rename to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisPortGroup.java
index 2f4eadd443d..13c4c90cf3f 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisNetworkRule.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisPortGroup.java
@@ -16,7 +16,12 @@
 // under the License.
 package org.apache.cloudstack.resource;
 
-import com.cloud.network.SDNProviderNetworkRule;
+public class NetrisPortGroup {
+    private String ports;
 
-public class NetrisNetworkRule extends SDNProviderNetworkRule {
+    public NetrisPortGroup(String ports) {
+        this.ports = ports;
+    }
+
+    public String getPorts() { return ports; }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 349b83132c7..989987b9a41 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -28,10 +28,12 @@ import com.cloud.agent.api.StartupCommand;
 import com.cloud.host.Host;
 import com.cloud.resource.ServerResource;
 import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisACLCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
@@ -105,7 +107,11 @@ public class NetrisResource implements ServerResource {
         } else if (cmd instanceof DeleteNetrisNatRuleCommand) {
             return executeRequest((DeleteNetrisNatRuleCommand) cmd);
         } else if (cmd instanceof CreateOrUpdateNetrisNatCommand) {
-            return executeRequest((CreateOrUpdateNetrisNatCommand) cmd);
+          return executeRequest((CreateOrUpdateNetrisNatCommand) cmd);
+        } else if (cmd instanceof CreateNetrisACLCommand) {
+            return executeRequest((CreateNetrisACLCommand) cmd);
+        } else if (cmd instanceof DeleteNetrisACLCommand) {
+            return executeRequest((DeleteNetrisACLCommand) cmd);
         } else if (cmd instanceof DeleteNetrisStaticRouteCommand) {
             return executeRequest((DeleteNetrisStaticRouteCommand) cmd);
         } else if (cmd instanceof AddOrUpdateNetrisStaticRouteCommand) {
@@ -306,6 +312,22 @@ public class NetrisResource implements ServerResource {
         return new NetrisAnswer(cmd, true, "OK");
     }
 
+    private Answer executeRequest(CreateNetrisACLCommand cmd) {
+        boolean result = netrisApiClient.addAclRule(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Creation of Netris ACL rule: %s failed", cmd.getNetrisAclName()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
+    private Answer executeRequest(DeleteNetrisACLCommand cmd) {
+        boolean result = netrisApiClient.deleteAclRule(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Failed to delete Netris ACLs: %s", String.join("'", cmd.getAclRuleNames())));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
     private Answer executeRequest(AddOrUpdateNetrisStaticRouteCommand cmd) {
         boolean result = netrisApiClient.addOrUpdateStaticRoute(cmd);
         if (!result) {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
index f5f6072b604..32225fdd199 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -22,7 +22,7 @@ import org.apache.commons.lang3.ArrayUtils;
 public class NetrisResourceObjectUtils {
 
     public enum NetrisObjectType {
-        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT, STATICROUTE
+        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT, STATICROUTE, ACL
     }
 
     public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, NetrisObjectType netrisObjectType, String... suffixes) {
@@ -77,6 +77,7 @@ public class NetrisResourceObjectUtils {
                 suffixes = new String[0];
                 break;
             case VNET:
+            case ACL:
                break;
             default:
                 stringBuilder.append(String.format("-%s", objectName));
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index 83e8b5fe0a3..f5efb4b55ea 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -20,10 +20,12 @@ import io.netris.ApiException;
 import io.netris.model.GetSiteBody;
 import io.netris.model.VPCListing;
 import io.netris.model.response.TenantResponse;
+import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisACLCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
@@ -77,6 +79,8 @@ public interface NetrisApiClient {
     boolean createOrUpdateDNATRule(CreateOrUpdateNetrisNatCommand cmd);
     boolean createStaticNatRule(CreateOrUpdateNetrisNatCommand cmd);
     boolean deleteNatRule(DeleteNetrisNatRuleCommand cmd);
+    boolean addAclRule(CreateNetrisACLCommand cmd);
+    boolean deleteAclRule(DeleteNetrisACLCommand cmd);
     boolean addOrUpdateStaticRoute(AddOrUpdateNetrisStaticRouteCommand cmd);
     boolean deleteStaticRoute(DeleteNetrisStaticRouteCommand cmd);
     boolean releaseNatIp(ReleaseNatIpCommand cmd);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index bfed67a915a..77d5aa40591 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -21,6 +21,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
 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;
@@ -29,6 +30,11 @@ import io.netris.api.v2.IpamApi;
 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.AclGetBody;
+import io.netris.model.AclResponseGetOk;
 import io.netris.model.AllocationBody;
 import io.netris.model.AllocationBodyVpc;
 import io.netris.model.FilterBySites;
@@ -80,14 +86,17 @@ import io.netris.model.VnetsBody;
 import io.netris.model.response.AuthResponse;
 import io.netris.model.response.TenantResponse;
 import io.netris.model.response.TenantsResponse;
+import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 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.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.NetrisCommand;
 import org.apache.cloudstack.agent.api.ReleaseNatIpCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
@@ -98,14 +107,18 @@ 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.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"};
 
     private static ApiClient apiClient;
 
@@ -296,6 +309,120 @@ public class NetrisApiClientImpl implements NetrisApiClient {
     }
 
     @Override
+    public boolean addAclRule(CreateNetrisACLCommand cmd) {
+        String aclName = cmd.getNetrisAclName();
+        try {
+            AclApi aclApi = apiClient.getApiStubForMethod(AclApi.class);
+            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());
+            }
+            String netrisVpcName = getNetrisVpcName(cmd, cmd.getVpcId(), cmd.getVpcName());
+            VPCListing vpcResource = getNetrisVpcResource(netrisVpcName);
+            if (Objects.isNull(vpcResource)) {
+                return false;
+            }
+            AclBodyVpc vpc = new AclBodyVpc().id(vpcResource.getId());
+            aclAddItem.setVpc(vpc);
+            List<String> aclNames = List.of(aclName);
+            Pair<Boolean, List<BigDecimal>> resultAndMatchingAclIds = getMatchingAclIds(aclNames, netrisVpcName);
+            if (!resultAndMatchingAclIds.second().isEmpty()) {
+                logger.debug("Netris ACL rule: {} already exists", aclName);
+                return true;
+            }
+            aclApi.apiAclPost(aclAddItem);
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to create Netris ACL: %s", cmd.getNetrisAclName()), e);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean deleteAclRule(DeleteNetrisACLCommand cmd) {
+        List<String> aclNames = cmd.getAclRuleNames();
+        try {
+            AclApi aclApi = apiClient.getApiStubForMethod(AclApi.class);
+
+            String suffix = getNetrisVpcNameSuffix(cmd.getVpcId(), cmd.getVpcName(), cmd.getId(), cmd.getName(), cmd.isVpc());
+            String vpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
+            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();
@@ -491,13 +618,6 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return new Pair<>(false, null);
     }
 
-    private void deleteNatSubnet(Integer netrisVpcId, String natIp) {
-        FilterByVpc vpcFilter = new FilterByVpc();
-        vpcFilter.add(netrisVpcId);
-        String netrisSubnetName = natIp + "/32";
-        deleteSubnetInternal(vpcFilter, null, netrisSubnetName);
-    }
-
     public void deleteNatRule(String natRuleName, Integer snatRuleId, String netrisVpcName) {
         logger.debug("Deleting NAT rule on Netris: {} for VPC {}", natRuleName, netrisVpcName);
         try {
@@ -597,9 +717,8 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String netrisV6Cidr = cmd.getIpv6Cidr();
         boolean isVpc = cmd.isVpc();
 
-        String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
-        String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
-        VPCListing associatedVpc = getVpcByNameAndTenant(netrisVpcName);
+        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;
@@ -644,9 +763,8 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         boolean isVpc = cmd.isVpc();
         String vnetCidr = cmd.getVNetCidr();
         try {
-            String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
-            String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
-            VPCListing associatedVpc = getVpcByNameAndTenant(netrisVpcName);
+            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;
@@ -758,9 +876,8 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String vNetName = isVpc ?
                 String.format("V%s-N%s-%s", vpcId, networkId, networkName) :
                 String.format("N%s-%s", networkId, networkName);
-        String vpcSuffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
-        String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, vpcSuffix);
-        VPCListing vpcResource = getVpcByNameAndTenant(netrisVpcName);
+        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;
@@ -781,9 +898,9 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         NatGetBody existingNatRule = netrisNatRuleExists(ruleName);
         boolean ruleExists = Objects.nonNull(existingNatRule);
         if (!ruleExists) {
-            String destinationAddress = action == NatPostBody.ActionEnum.SNAT ? "0.0.0.0/0" : cmd.getDestinationAddress() + "/32";
+            String destinationAddress = action == NatPostBody.ActionEnum.SNAT ? ANY_IP : cmd.getDestinationAddress() + "/32";
             String destinationPort = cmd.getDestinationPort();
-            String sourceAddress = action == NatPostBody.ActionEnum.SNAT ? vpcCidr : "0.0.0.0/0";
+            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;
@@ -834,9 +951,8 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         boolean isVpc = cmd.isVpc();
 
         try {
-            String vpcSuffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
-            String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, vpcSuffix);
-            VPCListing vpcResource = getVpcByNameAndTenant(netrisVpcName);
+            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;
@@ -856,7 +972,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             site.setId(siteId);
             site.setName(siteName);
             natBody.setSite(site);
-            natBody.setSourceAddress("0.0.0.0/0");
+            natBody.setSourceAddress(ANY_IP);
             natBody.setDnatToIP(vmIp);
 
             NatBodyVpcVpc vpc = new NatBodyVpcVpc();
@@ -981,17 +1097,13 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return true;
     }
 
-    private void updateNatRequest(NatPostBody natBody) {
-
-    }
-
     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("0.0.0.0/0");
+            natBody.setDestinationAddress(ANY_IP);
             natBody.setName(snatRuleName);
             natBody.setProtocol(NatPutBody.ProtocolEnum.ALL);
 
@@ -1214,4 +1326,24 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             throw new CloudRuntimeException("Failed to list Netris NAT rules");
         }
     }
+
+    private VPCListing getNetrisVpcResource(String netrisVpcName) {
+        VPCListing vpcResource = getVpcByNameAndTenant(netrisVpcName);
+        if (vpcResource == null) {
+            logger.error("Could not find the Netris VPC resource with name {} and tenant ID {}", netrisVpcName, tenantId);
+        }
+        return vpcResource;
+    }
+
+    private String getNetrisVpcName(NetrisCommand cmd, Long vpcId, String vpcName) {
+        String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, cmd.getId(), cmd.getName(), cmd.isVpc());
+        return NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
+    }
+
+    private void deleteNatSubnet(Integer netrisVpcId, String natIp) {
+        FilterByVpc vpcFilter = new FilterByVpc();
+        vpcFilter.add(netrisVpcId);
+        String netrisSubnetName = natIp + "/32";
+        deleteSubnetInternal(vpcFilter, null, netrisSubnetName);
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 96b7379d285..7ffa3363046 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -41,6 +41,7 @@ import com.cloud.network.Network;
 import com.cloud.network.NetworkModel;
 import com.cloud.network.PhysicalNetworkServiceProvider;
 import com.cloud.network.PublicIpAddress;
+import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.SDNProviderOpObject;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
@@ -86,7 +87,7 @@ import com.cloud.vm.VirtualMachineProfile;
 import com.cloud.vm.dao.VMInstanceDao;
 import org.apache.cloudstack.StartupNetrisCommand;
 import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.resource.NetrisNetworkRule;
+import com.cloud.network.netris.NetrisNetworkRule;
 import org.apache.cloudstack.resourcedetail.FirewallRuleDetailVO;
 import org.apache.cloudstack.resourcedetail.dao.FirewallRuleDetailsDao;
 import org.apache.logging.log4j.LogManager;
@@ -95,6 +96,7 @@ import org.springframework.stereotype.Component;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -440,13 +442,93 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
     }
 
     @Override
-    public boolean applyNetworkACLs(Network config, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException {
-        return true;
+    public boolean applyNetworkACLs(Network network, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException {
+        if (!canHandle(network, Network.Service.NetworkACL)) {
+            return false;
+        }
+
+        List<NetrisNetworkRule> nsxDelNetworkRules = new ArrayList<>();
+        boolean success = true;
+        for (NetworkACLItem rule : rules) {
+            String privatePort = PortForwardingServiceProvider.getPrivatePortRangeForACLRule(rule);
+            NetrisNetworkRule networkRule = getNetrisNetworkRuleForAcl(rule, privatePort);
+            if (Arrays.asList(NetworkACLItem.State.Active, NetworkACLItem.State.Add).contains(rule.getState())) {
+                success = success && netrisService.addFirewallRules(network, List.of(networkRule));
+            } else if (NetworkACLItem.State.Revoke == rule.getState()) {
+                nsxDelNetworkRules.add(networkRule);
+            }
+        }
+
+        if (!nsxDelNetworkRules.isEmpty()) {
+            success = netrisService.deleteFirewallRules(network, nsxDelNetworkRules);
+            if (!success) {
+                logger.warn("Not all firewall rules were successfully deleted");
+            }
+        }
+        return success;
+    }
+
+    private NetrisNetworkRule getNetrisNetworkRuleForAcl(NetworkACLItem rule, String privatePort) {
+        SDNProviderNetworkRule baseNetworkRule = new SDNProviderNetworkRule.Builder()
+                .setRuleId(rule.getId())
+                .setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ? transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"))
+                .setTrafficType(rule.getTrafficType().toString())
+                .setProtocol(rule.getProtocol().toUpperCase())
+                .setPublicPort(String.valueOf(rule.getSourcePortStart()))
+                .setPrivatePort(String.valueOf(privatePort))
+                .setIcmpCode(rule.getIcmpCode())
+                .setIcmpType(rule.getIcmpType())
+                .setService(Network.Service.NetworkACL)
+                .build();
+        return new NetrisNetworkRule.Builder()
+                .baseRule(baseNetworkRule)
+                .aclAction(transformActionValue(rule.getAction()))
+                        .reason(rule.getReason())
+                .build();
+    }
+
+    protected List<String> transformCidrListValues(List<String> sourceCidrList) {
+        List<String> list = new ArrayList<>();
+        if (org.apache.commons.collections.CollectionUtils.isNotEmpty(sourceCidrList)) {
+            for (String cidr : sourceCidrList) {
+                if (cidr.equals("0.0.0.0/0")) {
+                    list.add("ANY");
+                } else {
+                    list.add(cidr);
+                }
+            }
+        }
+        return list;
+    }
+
+    protected NetrisNetworkRule.NetrisRuleAction transformActionValue(NetworkACLItem.Action action) {
+        if (action == NetworkACLItem.Action.Allow) {
+            return NetrisNetworkRule.NetrisRuleAction.PERMIT;
+        } else if (action == NetworkACLItem.Action.Deny) {
+            return NetrisNetworkRule.NetrisRuleAction.DENY;
+        }
+        String err = String.format("Unsupported action %s", action.toString());
+        logger.error(err);
+        throw new CloudRuntimeException(err);
     }
 
     @Override
     public boolean reorderAclRules(Vpc vpc, List<? extends Network> networks, List<? extends NetworkACLItem> networkACLItems) {
-        return false;
+        List<NetrisNetworkRule> aclRulesList = new ArrayList<>();
+        for (NetworkACLItem rule : networkACLItems) {
+            String privatePort = PortForwardingServiceProvider.getPrivatePortRangeForACLRule(rule);
+            aclRulesList.add(getNetrisNetworkRuleForAcl(rule, privatePort));
+        }
+        for (Network network: networks) {
+            netrisService.deleteFirewallRules(network, aclRulesList);
+        }
+        boolean success = true;
+        for (Network network : networks) {
+            for (NetrisNetworkRule aclRule : aclRulesList) {
+                success = success && netrisService.addFirewallRules(network, List.of(aclRule));
+            }
+        }
+        return success;
     }
 
     @Override
@@ -470,12 +552,12 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
         Long networkId = netrisObject.getNetworkVO() != null ? netrisObject.getNetworkVO().getId() : null;
         String networkName = netrisObject.getNetworkVO() != null ? netrisObject.getNetworkVO().getName() : null;
         String vpcCidr = netrisObject.getVpcVO() != null ? netrisObject.getVpcVO().getCidr() : null;
-
+        SDNProviderNetworkRule baseNetRule = networkRule.getBaseRule();
         return create ?
-                netrisService.createPortForwardingRule(networkRule.getZoneId(), networkRule.getAccountId(), networkRule.getDomainId(),
-                        vpcName, vpcId, networkName, networkId, netrisObject.isVpcResource(), vpcCidr, networkRule) :
-                netrisService.deletePortForwardingRule(networkRule.getZoneId(), networkRule.getAccountId(), networkRule.getDomainId(),
-                        vpcName, vpcId, networkName, networkId, netrisObject.isVpcResource(), vpcCidr, networkRule);
+                netrisService.createPortForwardingRule(baseNetRule.getZoneId(), baseNetRule.getAccountId(), baseNetRule.getDomainId(),
+                        vpcName, vpcId, networkName, networkId, netrisObject.isVpcResource(), vpcCidr, baseNetRule) :
+                netrisService.deletePortForwardingRule(baseNetRule.getZoneId(), baseNetRule.getAccountId(), baseNetRule.getDomainId(),
+                        vpcName, vpcId, networkName, networkId, netrisObject.isVpcResource(), vpcCidr, baseNetRule);
     }
 
     private boolean applyPFRulesInternal(Network network, List<PortForwardingRule> rules) {
@@ -492,20 +574,23 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
                 String privatePort = PortForwardingServiceProvider.getPrivatePFPortRange(rule);
                 FirewallRuleDetailVO ruleDetail = firewallRuleDetailsDao.findDetail(rule.getId(), ApiConstants.NETRIS_DETAIL_KEY);
 
-                NetrisNetworkRule networkRule = new NetrisNetworkRule();
-                networkRule.setDomainId(netrisObject.getDomainId());
-                networkRule.setAccountId(netrisObject.getAccountId());
-                networkRule.setZoneId(netrisObject.getZoneId());
-                networkRule.setNetworkResourceId(netrisObject.getNetworkResourceId());
-                networkRule.setNetworkResourceName(netrisObject.getNetworkResourceName());
-                networkRule.setVpcResource(netrisObject.isVpcResource());
-                networkRule.setVmId(Objects.nonNull(vm) ? vm.getId() : 0);
-                networkRule.setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null);
-                networkRule.setPublicIp(publicIp.getAddress().addr());
-                networkRule.setPrivatePort(privatePort);
-                networkRule.setPublicPort(publicPort);
-                networkRule.setRuleId(rule.getId());
-                networkRule.setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT));
+                SDNProviderNetworkRule baseNetworkRule = new SDNProviderNetworkRule.Builder()
+                        .setDomainId(netrisObject.getDomainId())
+                .setAccountId(netrisObject.getAccountId())
+                .setZoneId(netrisObject.getZoneId())
+                .setNetworkResourceId(netrisObject.getNetworkResourceId())
+                .setNetworkResourceName(netrisObject.getNetworkResourceName())
+                .setVpcResource(netrisObject.isVpcResource())
+                .setVmId(Objects.nonNull(vm) ? vm.getId() : 0)
+                .setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null)
+                .setPublicIp(publicIp.getAddress().addr())
+                .setPrivatePort(privatePort)
+                .setPublicPort(publicPort)
+                .setRuleId(rule.getId())
+                .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT))
+                        .build();
+
+                NetrisNetworkRule networkRule = new NetrisNetworkRule.Builder().baseRule(baseNetworkRule).build();
 
                 if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(rule.getState())) {
                     boolean pfRuleResult = addOrRemovePFRuleOnNetris(vm, rule, networkRule, netrisObject, true);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 0a9fe4ac239..a615090c429 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -35,16 +35,19 @@ import com.cloud.network.dao.PhysicalNetworkVO;
 import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.netris.NetrisService;
 import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.dao.VpcDao;
+import io.netris.model.NatPostBody;
+import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 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.model.NatPostBody;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisACLCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
@@ -56,6 +59,7 @@ import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
+import com.cloud.network.netris.NetrisNetworkRule;
 import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -77,6 +81,8 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
     @Inject
     private NetworkDao networkDao;
     @Inject
+    private VpcDao vpcDao;
+    @Inject
     private AgentManager agentMgr;
     @Inject
     private PhysicalNetworkDao physicalNetworkDao;
@@ -207,7 +213,9 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         cmd.setNetrisTag(netrisProvider.getNetrisTag());
         if (Objects.nonNull(networkId)) {
             Ipv6GuestPrefixSubnetNetworkMapVO ipv6PrefixNetworkMapVO = ipv6PrefixNetworkMapDao.findByNetworkId(networkId);
-            cmd.setIpv6Cidr(ipv6PrefixNetworkMapVO.getSubnet());
+            if (Objects.nonNull(ipv6PrefixNetworkMapVO)) {
+                cmd.setIpv6Cidr(ipv6PrefixNetworkMapVO.getSubnet());
+            }
         }
         NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
         return answer.getResult();
@@ -343,6 +351,88 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
     }
 
     @Override
+    public boolean addFirewallRules(Network network, List<NetrisNetworkRule> firewallRules) {
+        Long zoneId = network.getDataCenterId();
+        Long accountId = network.getAccountId();
+        Long domainId = network.getDomainId();
+        Long vpcId = network.getVpcId();
+        Long networkId = network.getId();
+        String vpcName = null;
+        Vpc vpc = null;
+        if (Objects.nonNull(vpcId)) {
+            vpc = vpcDao.findById(vpcId);
+            if (Objects.nonNull(vpc)) {
+                vpcName = vpc.getName();
+            }
+        }
+        String networkName = network.getName();
+        NetrisNetworkRule rule = firewallRules.get(0);
+        SDNProviderNetworkRule baseNetworkRule = rule.getBaseRule();
+        String trafficType = baseNetworkRule.getTrafficType().toUpperCase(Locale.ROOT);
+        String sourcePrefix;
+        String destinationPrefix;
+        if ("INGRESS".equals(trafficType)) {
+            sourcePrefix = baseNetworkRule.getSourceCidrList().get(0);
+            destinationPrefix = network.getCidr();
+        } else {
+            sourcePrefix = network.getCidr();
+            destinationPrefix = baseNetworkRule.getSourceCidrList().get(0);
+        }
+        String srcPort;
+        String dstPort;
+        if (baseNetworkRule.getPrivatePort().contains("-")) {
+            srcPort = baseNetworkRule.getPrivatePort().split("-")[0];
+            dstPort = baseNetworkRule.getPrivatePort().split("-")[1];
+        } else {
+            srcPort = dstPort = baseNetworkRule.getPrivatePort();
+        }
+        CreateNetrisACLCommand cmd = new CreateNetrisACLCommand(zoneId, accountId, domainId, networkName, networkId,
+                vpcName, vpcId, Objects.nonNull(vpcId), rule.getAclAction().name().toLowerCase(Locale.ROOT), getPrefix(sourcePrefix), getPrefix(destinationPrefix),
+                "null".equals(srcPort) ? 1 : Integer.parseInt(srcPort),
+                "null".equals(dstPort) ? 65535 : Integer.parseInt(dstPort), baseNetworkRule.getProtocol());
+        String aclName = String.format("V%s-N%s-ACL%s", vpcId, networkId, rule.getBaseRule().getRuleId());
+        String netrisAclName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.ACL, aclName);
+        cmd.setNetrisAclName(netrisAclName);
+        cmd.setReason(rule.getReason());
+        if ("ICMP".equals(baseNetworkRule.getProtocol())) {
+            cmd.setIcmpType(baseNetworkRule.getIcmpType());
+        }
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    private String getPrefix(String prefix) {
+        if ("ANY".equals(prefix)) {
+            return NetUtils.ALL_IP4_CIDRS;
+        }
+        return prefix;
+    }
+
+    @Override
+    public boolean deleteFirewallRules(Network network, List<NetrisNetworkRule> firewallRules) {
+        long zoneId = network.getDataCenterId();
+        Long accountId = network.getAccountId();
+        Long domainId = network.getDomainId();
+        String networkName = network.getName();
+        Long networkId = network.getId();
+        String vpcName = null;
+        Long vpcId = network.getVpcId();
+        if (Objects.nonNull(vpcId)) {
+            vpcName = vpcDao.findById(vpcId).getName();
+        }
+        DeleteNetrisACLCommand cmd = new DeleteNetrisACLCommand(zoneId, accountId, domainId, networkName, networkId, Objects.nonNull(network.getVpcId()), vpcId, vpcName);
+        List<String> aclRuleNames = firewallRules.stream()
+                .map(rule -> {
+                    String aclName = String.format("V%s-N%s-ACL%s", vpcId, networkId, rule.getBaseRule().getRuleId());
+                    return NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.ACL, aclName);
+                })
+                .collect(Collectors.toList());
+        cmd.setAclRuleNames(aclRuleNames);
+
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
     public boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute) {
         AddOrUpdateNetrisStaticRouteCommand cmd = new AddOrUpdateNetrisStaticRouteCommand(zoneId, accountId, domainId, networkResourceName, networkResourceId, isForVpc, prefix, nextHop, routeId, updateRoute);
         NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
index 153143b6d18..9c85c49adde 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
@@ -20,15 +20,24 @@ import com.cloud.network.SDNProviderNetworkRule;
 
 import java.util.List;
 
-public class NsxNetworkRule extends SDNProviderNetworkRule {
+public class NsxNetworkRule {
 
     public enum NsxRuleAction {
         ALLOW, DROP
     }
 
+    private SDNProviderNetworkRule baseRule;
     private List<NsxLoadBalancerMember> memberList;
     private NsxRuleAction aclAction;
 
+    public SDNProviderNetworkRule getBaseRule() {
+        return baseRule;
+    }
+
+    public void setBaseRule(SDNProviderNetworkRule baseRule) {
+        this.baseRule = baseRule;
+    }
+
     public List<NsxLoadBalancerMember> getMemberList() {
         return memberList;
     }
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java
index 1ba1cc0fcc3..86559f7e945 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.service;
 
 import com.cloud.network.Network;
+import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.nsx.NsxService;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.vmware.nsx.cluster.Status;
@@ -1025,7 +1026,8 @@ public class NsxApiClient {
     }
 
     public void deleteDistributedFirewallRules(String segmentName, List<NsxNetworkRule> nsxRules) {
-        for(NsxNetworkRule rule : nsxRules) {
+        for(NsxNetworkRule nsxRule : nsxRules) {
+            SDNProviderNetworkRule rule = nsxRule.getBaseRule();
             String ruleId = NsxControllerUtils.getNsxDistributedFirewallPolicyRuleId(segmentName, rule.getRuleId());
            String svcName = getServiceName(ruleId, rule.getPrivatePort(), rule.getProtocol(), rule.getIcmpType(), rule.getIcmpCode());
             // delete rules
@@ -1043,10 +1045,11 @@ public class NsxApiClient {
         if (Objects.isNull(groupPath)) {
             throw new CloudRuntimeException(String.format("Failed to find group for segment %s", segmentName));
         }
-        for (NsxNetworkRule rule : nsxRules) {
+        for (NsxNetworkRule nsxRule : nsxRules) {
+            SDNProviderNetworkRule rule = nsxRule.getBaseRule();
             String ruleId = NsxControllerUtils.getNsxDistributedFirewallPolicyRuleId(segmentName, rule.getRuleId());
             Rule ruleToAdd = new Rule.Builder()
-                    .setAction(rule.getAclAction().toString())
+                    .setAction(nsxRule.getAclAction().toString())
                     .setId(ruleId)
                     .setDisplayName(ruleId)
                     .setResourceType("SecurityPolicy")
@@ -1060,7 +1063,7 @@ public class NsxApiClient {
         return rules;
     }
 
-    private List<String> getServicesListForDistributedFirewallRule(NsxNetworkRule rule, String segmentName) {
+    private List<String> getServicesListForDistributedFirewallRule(SDNProviderNetworkRule rule, String segmentName) {
         List<String> services = List.of("ANY");
         if (!rule.getProtocol().equalsIgnoreCase("all")) {
             String ruleName = String.format("%s-R%s", segmentName, rule.getRuleId());
@@ -1071,7 +1074,7 @@ public class NsxApiClient {
         return services;
     }
 
-    protected List<String> getGroupsForTraffic(NsxNetworkRule rule,
+    protected List<String> getGroupsForTraffic(SDNProviderNetworkRule rule,
                                              String segmentName, boolean source) {
         List<String> segmentGroup = List.of(String.format("%s/%s", GROUPS_PATH_PREFIX, segmentName));
         List<String> sourceCidrList = rule.getSourceCidrList();
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
index a57868f864e..f56e69ab352 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
@@ -45,6 +45,7 @@ import com.cloud.network.NetworkModel;
 import com.cloud.network.Networks;
 import com.cloud.network.PhysicalNetworkServiceProvider;
 import com.cloud.network.PublicIpAddress;
+import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.VirtualRouterProvider;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
@@ -563,27 +564,31 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
 
                 String privatePort = PortForwardingServiceProvider.getPrivatePFPortRange(rule);
 
-                NsxNetworkRule networkRule = new NsxNetworkRule();
-                networkRule.setDomainId(nsxObject.getDomainId());
-                networkRule.setAccountId(nsxObject.getAccountId());
-                networkRule.setZoneId(nsxObject.getZoneId());
-                networkRule.setNetworkResourceId(nsxObject.getNetworkResourceId());
-                networkRule.setNetworkResourceName(nsxObject.getNetworkResourceName());
-                networkRule.setVpcResource(nsxObject.isVpcResource());
-                networkRule.setVmId(Objects.nonNull(vm) ? vm.getId() : 0);
-                networkRule.setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null);
-                networkRule.setPublicIp(publicIp.getAddress().addr());
-                networkRule.setPrivatePort(privatePort);
-                networkRule.setPublicPort(publicPort);
-                networkRule.setRuleId(rule.getId());
-                networkRule.setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT));
+                SDNProviderNetworkRule networkRule = new SDNProviderNetworkRule.Builder()
+                        .setDomainId(nsxObject.getDomainId())
+                        .setAccountId(nsxObject.getAccountId())
+                        .setZoneId(nsxObject.getZoneId())
+                        .setNetworkResourceId(nsxObject.getNetworkResourceId())
+                        .setNetworkResourceName(nsxObject.getNetworkResourceName())
+                        .setVpcResource(nsxObject.isVpcResource())
+                        .setVmId(Objects.nonNull(vm) ? vm.getId() : 0)
+                        .setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null)
+                        .setPublicIp(publicIp.getAddress().addr())
+                        .setPrivatePort(privatePort)
+                        .setPublicPort(publicPort)
+                        .setRuleId(rule.getId())
+                        .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT))
+                        .build();
+
+                NsxNetworkRule nsxNetworkRule = new NsxNetworkRule();
+                nsxNetworkRule.setBaseRule(networkRule);
 
                 FirewallRuleDetailVO ruleDetail = firewallRuleDetailsDao.findDetail(rule.getId(), ApiConstants.FOR_NSX);
                 if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(rule.getState())) {
                     if ((ruleDetail == null && FirewallRule.State.Add == rule.getState()) || (ruleDetail != null && !ruleDetail.getValue().equalsIgnoreCase("true"))) {
                         logger.debug("Creating port forwarding rule on NSX for VM {} to ports {} - {}",
                                 vm.getUuid(), rule.getDestinationPortStart(), rule.getDestinationPortEnd());
-                        NsxAnswer answer = nsxService.createPortForwardRule(networkRule);
+                        NsxAnswer answer = nsxService.createPortForwardRule(nsxNetworkRule);
                         boolean pfRuleResult = answer.getResult();
                         if (pfRuleResult && !answer.isObjectExistent()) {
                             logger.debug("Port forwarding rule {} created on NSX, adding detail on firewall rules details", rule.getId());
@@ -600,7 +605,7 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
                     }
                 } else if (rule.getState() == FirewallRule.State.Revoke) {
                     if (ruleDetail == null || (ruleDetail != null && ruleDetail.getValue().equalsIgnoreCase("true"))) {
-                        boolean pfRuleResult = nsxService.deletePortForwardRule(networkRule);
+                        boolean pfRuleResult = nsxService.deletePortForwardRule(nsxNetworkRule);
                         if (pfRuleResult && ruleDetail != null) {
                             logger.debug("Updating firewall rule detail {} for rule {}, set to false", ruleDetail.getId(), rule.getId());
                             ruleDetail.setValue("false");
@@ -678,20 +683,23 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
             SDNProviderOpObject nsxObject = getNsxOpObject(network);
 
             List<NsxLoadBalancerMember> lbMembers = getLoadBalancerMembers(loadBalancingRule);
+            SDNProviderNetworkRule baseNetRule = new SDNProviderNetworkRule.Builder()
+            .setDomainId(nsxObject.getDomainId())
+            .setAccountId(nsxObject.getAccountId())
+            .setZoneId(nsxObject.getZoneId())
+            .setNetworkResourceId(nsxObject.getNetworkResourceId())
+            .setNetworkResourceName(nsxObject.getNetworkResourceName())
+            .setVpcResource(nsxObject.isVpcResource())
+            .setPublicIp(LoadBalancerContainer.Scheme.Public == loadBalancingRule.getScheme() ?
+                            publicIp.getAddress().addr() : loadBalancingRule.getSourceIp().addr())
+            .setPublicPort(String.valueOf(loadBalancingRule.getSourcePortStart()))
+            .setPrivatePort(String.valueOf(loadBalancingRule.getDefaultPortStart()))
+            .setRuleId(loadBalancingRule.getId())
+            .setProtocol(loadBalancingRule.getLbProtocol().toUpperCase(Locale.ROOT))
+            .setAlgorithm(loadBalancingRule.getAlgorithm())
+                    .build();
             NsxNetworkRule networkRule = new NsxNetworkRule();
-            networkRule.setDomainId(nsxObject.getDomainId());
-            networkRule.setAccountId(nsxObject.getAccountId());
-            networkRule.setZoneId(nsxObject.getZoneId());
-            networkRule.setNetworkResourceId(nsxObject.getNetworkResourceId());
-            networkRule.setNetworkResourceName(nsxObject.getNetworkResourceName());
-            networkRule.setVpcResource(nsxObject.isVpcResource());
-            networkRule.setPublicIp(LoadBalancerContainer.Scheme.Public == loadBalancingRule.getScheme() ?
-                            publicIp.getAddress().addr() : loadBalancingRule.getSourceIp().addr());
-            networkRule.setPublicPort(String.valueOf(loadBalancingRule.getSourcePortStart()));
-            networkRule.setPrivatePort(String.valueOf(loadBalancingRule.getDefaultPortStart()));
-            networkRule.setRuleId(loadBalancingRule.getId());
-            networkRule.setProtocol(loadBalancingRule.getLbProtocol().toUpperCase(Locale.ROOT));
-            networkRule.setAlgorithm(loadBalancingRule.getAlgorithm());
+            networkRule.setBaseRule(baseNetRule);
             networkRule.setMemberList(lbMembers);
             if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(loadBalancingRule.getState())) {
                 result &= nsxService.createLbRule(networkRule);
@@ -776,15 +784,17 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
 
     private NsxNetworkRule getNsxNetworkRuleForAcl(NetworkACLItem rule, String privatePort) {
         NsxNetworkRule nsxNetworkRule = new NsxNetworkRule();
-        nsxNetworkRule.setRuleId(rule.getId());
-        nsxNetworkRule.setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ? transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"));
-        nsxNetworkRule.setTrafficType(rule.getTrafficType().toString());
-        nsxNetworkRule.setProtocol(rule.getProtocol().toUpperCase());
-        nsxNetworkRule.setPublicPort(String.valueOf(rule.getSourcePortStart()));
-        nsxNetworkRule.setPrivatePort(privatePort);
-        nsxNetworkRule.setIcmpCode(rule.getIcmpCode());
-        nsxNetworkRule.setIcmpType(rule.getIcmpType());
-        nsxNetworkRule.setService(Network.Service.NetworkACL);
+        SDNProviderNetworkRule networkRule = new SDNProviderNetworkRule.Builder()
+        .setRuleId(rule.getId())
+        .setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ? transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"))
+        .setTrafficType(rule.getTrafficType().toString())
+        .setProtocol(rule.getProtocol().toUpperCase())
+        .setPublicPort(String.valueOf(rule.getSourcePortStart()))
+        .setPrivatePort(privatePort)
+        .setIcmpCode(rule.getIcmpCode())
+        .setIcmpType(rule.getIcmpType())
+        .setService(Network.Service.NetworkACL).build();
+        nsxNetworkRule.setBaseRule(networkRule);
         nsxNetworkRule.setAclAction(transformActionValue(rule.getAction()));
         return nsxNetworkRule;
     }
@@ -798,17 +808,19 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
         List<NsxNetworkRule> nsxDelNetworkRules = new ArrayList<>();
         for (FirewallRule rule : rules) {
             NsxNetworkRule networkRule = new NsxNetworkRule();
-            networkRule.setRuleId(rule.getId());
-            networkRule.setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ?
-                        transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"));
-            networkRule.setDestinationCidrList(Objects.nonNull(rule.getDestinationCidrList()) ?
-                        transformCidrListValues(rule.getDestinationCidrList()) : List.of("ANY"));
-            networkRule.setIcmpCode(rule.getIcmpCode());
-            networkRule.setIcmpType(rule.getIcmpType());
-            networkRule.setPrivatePort(PortForwardingServiceProvider.getPrivatePortRange(rule));
-            networkRule.setTrafficType(rule.getTrafficType().toString());
-            networkRule.setService(Network.Service.Firewall);
-            networkRule.setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT));
+            SDNProviderNetworkRule baseNetRule = new SDNProviderNetworkRule.Builder()
+            .setRuleId(rule.getId())
+            .setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ?
+                        transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY"))
+            .setDestinationCidrList(Objects.nonNull(rule.getDestinationCidrList()) ?
+                        transformCidrListValues(rule.getDestinationCidrList()) : List.of("ANY"))
+            .setIcmpCode(rule.getIcmpCode())
+            .setIcmpType(rule.getIcmpType())
+            .setPrivatePort(PortForwardingServiceProvider.getPrivatePortRange(rule))
+            .setTrafficType(rule.getTrafficType().toString())
+            .setService(Network.Service.Firewall)
+            .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT)).build();
+            networkRule.setBaseRule(baseNetRule);
             networkRule.setAclAction(NsxNetworkRule.NsxRuleAction.ALLOW);
             if (rule.getState() == FirewallRule.State.Add) {
                 nsxAddNetworkRules.add(networkRule);
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java
index 139d8a55e59..fb74e14ff43 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java
@@ -18,6 +18,7 @@ package org.apache.cloudstack.service;
 
 import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
+import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.nsx.NsxService;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.vpc.Vpc;
@@ -138,7 +139,8 @@ public class NsxServiceImpl implements NsxService, Configurable {
         return result.getResult();
     }
 
-    public NsxAnswer createPortForwardRule(NsxNetworkRule netRule) {
+    public NsxAnswer createPortForwardRule(NsxNetworkRule nsxNetRule) {
+        SDNProviderNetworkRule netRule = nsxNetRule.getBaseRule();
         // TODO: if port doesn't exist in default list of services, create a service entry
         CreateNsxPortForwardRuleCommand createPortForwardCmd = new CreateNsxPortForwardRuleCommand(netRule.getDomainId(),
                 netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(),
@@ -147,7 +149,8 @@ public class NsxServiceImpl implements NsxService, Configurable {
         return nsxControllerUtils.sendNsxCommand(createPortForwardCmd, netRule.getZoneId());
     }
 
-    public boolean deletePortForwardRule(NsxNetworkRule netRule) {
+    public boolean deletePortForwardRule(NsxNetworkRule nsxNetRule) {
+        SDNProviderNetworkRule netRule = nsxNetRule.getBaseRule();
         DeleteNsxNatRuleCommand deleteCmd = new DeleteNsxNatRuleCommand(netRule.getDomainId(),
                 netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(),
                 netRule.getNetworkResourceName(), netRule.isVpcResource(),  netRule.getVmId(), netRule.getRuleId(), netRule.getPrivatePort(), netRule.getProtocol());
@@ -156,20 +159,22 @@ public class NsxServiceImpl implements NsxService, Configurable {
         return result.getResult();
     }
 
-    public boolean createLbRule(NsxNetworkRule netRule) {
+    public boolean createLbRule(NsxNetworkRule nsxNetRule) {
+        SDNProviderNetworkRule netRule = nsxNetRule.getBaseRule();
         CreateNsxLoadBalancerRuleCommand command = new CreateNsxLoadBalancerRuleCommand(netRule.getDomainId(),
                 netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(),
-                netRule.getNetworkResourceName(), netRule.isVpcResource(),  netRule.getMemberList(), netRule.getRuleId(),
+                netRule.getNetworkResourceName(), netRule.isVpcResource(),  nsxNetRule.getMemberList(), netRule.getRuleId(),
                 netRule.getPublicPort(), netRule.getPrivatePort(), netRule.getAlgorithm(), netRule.getProtocol());
         command.setPublicIp(netRule.getPublicIp());
         NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, netRule.getZoneId());
         return result.getResult();
     }
 
-    public boolean deleteLbRule(NsxNetworkRule netRule) {
+    public boolean deleteLbRule(NsxNetworkRule nsxNetRule) {
+        SDNProviderNetworkRule netRule = nsxNetRule.getBaseRule();
         DeleteNsxLoadBalancerRuleCommand command = new DeleteNsxLoadBalancerRuleCommand(netRule.getDomainId(),
                 netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(),
-                netRule.getNetworkResourceName(), netRule.isVpcResource(),  netRule.getMemberList(), netRule.getRuleId(),
+                netRule.getNetworkResourceName(), netRule.isVpcResource(),  nsxNetRule.getMemberList(), netRule.getRuleId(),
                 netRule.getVmId());
         NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, netRule.getZoneId());
         return result.getResult();
diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java
index fbcca86a28b..f9ee8daea4b 100644
--- a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java
+++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.service;
 
 import com.cloud.network.Network;
+import com.cloud.network.SDNProviderNetworkRule;
 import com.vmware.nsx.cluster.Status;
 import com.vmware.nsx.model.ClusterStatus;
 import com.vmware.nsx.model.ControllerClusterStatus;
@@ -24,7 +25,6 @@ import com.vmware.nsx_policy.infra.domains.Groups;
 import com.vmware.nsx_policy.model.Group;
 import com.vmware.nsx_policy.model.PathExpression;
 import com.vmware.vapi.bindings.Service;
-import org.apache.cloudstack.resource.NsxNetworkRule;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -73,7 +73,7 @@ public class NsxApiClientTest {
 
     @Test
     public void testGetGroupsForTrafficIngress() {
-        NsxNetworkRule rule = Mockito.mock(NsxNetworkRule.class);
+        SDNProviderNetworkRule rule = Mockito.mock(SDNProviderNetworkRule.class);
         Mockito.when(rule.getSourceCidrList()).thenReturn(List.of("ANY"));
         Mockito.when(rule.getTrafficType()).thenReturn("Ingress");
         Mockito.when(rule.getService()).thenReturn(Network.Service.NetworkACL);
@@ -86,7 +86,7 @@ public class NsxApiClientTest {
 
     @Test
     public void testGetGroupsForTrafficEgress() {
-        NsxNetworkRule rule = Mockito.mock(NsxNetworkRule.class);
+        SDNProviderNetworkRule rule = Mockito.mock(SDNProviderNetworkRule.class);
         Mockito.when(rule.getSourceCidrList()).thenReturn(List.of("ANY"));
         Mockito.when(rule.getTrafficType()).thenReturn("Egress");
         Mockito.when(rule.getService()).thenReturn(Network.Service.NetworkACL);
diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index 05a6cd20213..ff715db4027 100644
--- a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -17,10 +17,14 @@
 package org.apache.cloudstack.service;
 
 import com.cloud.network.IpAddress;
+import com.cloud.network.Network;
 import com.cloud.network.SDNProviderNetworkRule;
+import com.cloud.network.netris.NetrisNetworkRule;
 import com.cloud.network.netris.NetrisService;
 import com.cloud.network.vpc.Vpc;
 
+import java.util.List;
+
 public class NetrisServiceMockTest implements NetrisService {
     @Override
     public boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId) {
@@ -77,6 +81,16 @@ public class NetrisServiceMockTest implements NetrisService {
         return true;
     }
 
+    @Override
+    public boolean addFirewallRules(Network network, List<NetrisNetworkRule> firewallRules) {
+        return true;
+    }
+
+    @Override
+    public boolean deleteFirewallRules(Network network, List<NetrisNetworkRule> firewallRules) {
+        return true;
+    }
+
     @Override
     public boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute) {
         return true;


(cloudstack) 12/44: Pass network gateway instead of network CIDR for Netris vNet creation (#21)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 8163d3278c5721adb35047221d4d4263a667a25b
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Tue Nov 26 14:18:53 2024 -0500

    Pass network gateway instead of network CIDR for Netris vNet creation (#21)
---
 .../apache/cloudstack/agent/api/CreateNetrisVnetCommand.java  |  8 +++++++-
 .../org/apache/cloudstack/service/NetrisApiClientImpl.java    | 11 +++++++----
 .../java/org/apache/cloudstack/service/NetrisServiceImpl.java |  2 +-
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
index ed02a09b42c..12ef9482ea8 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
@@ -21,12 +21,14 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
     private Long vpcId;
     private String cidr;
     private Integer vxlanId;
+    private String gateway;
 
-    public CreateNetrisVnetCommand(Long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, String cidr, boolean isVpc) {
+    public CreateNetrisVnetCommand(Long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, String cidr, String gateway, boolean isVpc) {
         super(zoneId, accountId, domainId, vNetName, networkId, isVpc);
         this.vpcId = vpcId;
         this.vpcName = vpcName;
         this.cidr = cidr;
+        this.gateway = gateway;
     }
 
 
@@ -49,4 +51,8 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
     public void setVxlanId(Integer vxlanId) {
         this.vxlanId = vxlanId;
     }
+
+    public String getGateway() {
+        return gateway;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index d7999142945..2bf9b172608 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -320,6 +320,9 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         Long networkId = cmd.getId();
         String vnetCidr = cmd.getCidr();
         Integer vxlanId = cmd.getVxlanId();
+        String gateway = cmd.getGateway();
+        String netmask = vnetCidr.split("/")[1];
+        String netrisGateway = cmd.getGateway() + "/" + netmask;
         boolean isVpc = cmd.isVpc();
 
         String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
@@ -342,7 +345,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
         logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
 
-        VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr, vxlanId);
+        VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, netrisGateway, vxlanId);
         if (vnetResponse == null || !vnetResponse.isIsSuccess()) {
             String reason = vnetResponse == null ? "Empty response" : "Operation failed on Netris";
             logger.debug("The Netris vNet creation {} failed: {}", vNetName, reason);
@@ -536,15 +539,15 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
-    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String vNetCidr, Integer vxlanId) {
-        logger.debug("Creating Netris VPC vNet {} for CIDR {}", netrisVnetName, vNetCidr);
+    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String netrisGateway, Integer vxlanId) {
+        logger.debug("Creating Netris VPC vNet {} for CIDR {}", netrisVnetName, netrisGateway);
         try {
             VnetAddBody vnetBody = new VnetAddBody();
 
             vnetBody.setCustomAnycastMac("");
 
             VnetAddBodyGateways gateways = new VnetAddBodyGateways();
-            gateways.prefix(vNetCidr);
+            gateways.prefix(netrisGateway);
             gateways.setDhcpEnabled(false);
             VnetAddBodyDhcp dhcp = new VnetAddBodyDhcp();
             dhcp.setEnd("");
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 90c80ddabf1..d679eae258d 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -98,7 +98,7 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
     public boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
         NetworkVO network = networkDao.findById(networkId);
         String vxlanId = Networks.BroadcastDomainType.getValue(network.getBroadcastUri());
-        CreateNetrisVnetCommand cmd = new CreateNetrisVnetCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, cidr, !Objects.isNull(vpcId));
+        CreateNetrisVnetCommand cmd = new CreateNetrisVnetCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, cidr, network.getGateway(), !Objects.isNull(vpcId));
         cmd.setVxlanId(Integer.parseInt(vxlanId));
         NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
         return answer.getResult();


(cloudstack) 44/44: add license

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 793f1f2958ce548439d7edf2932251ea44402b3a
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Wed Mar 5 17:51:13 2025 -0500

    add license
---
 .../cloudstack/agent/api/UpdateNetrisVnetCommand.java    | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java
index e3307cb8842..99a266e15ad 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java
@@ -1,3 +1,19 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
 package org.apache.cloudstack.agent.api;
 
 public class UpdateNetrisVnetCommand extends NetrisCommand{


(cloudstack) 05/44: Fix unit tests

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 78efab46d519dd1534cdba366204a78f36d6fb12
Author: nvazquez <ni...@gmail.com>
AuthorDate: Tue Oct 22 22:06:24 2024 -0300

    Fix unit tests
---
 .../java/com/cloud/configuration/ConfigurationManagerImplTest.java    | 4 ++++
 .../network/router/deployment/RouterDeploymentDefinitionTest.java     | 3 +++
 .../network/router/deployment/VpcRouterDeploymentDefinitionTest.java  | 3 +++
 3 files changed, 10 insertions(+)

diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java
index 0a045296821..f348382159a 100644
--- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java
+++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java
@@ -33,6 +33,7 @@ import com.cloud.network.NetworkModel;
 import com.cloud.network.NetworkService;
 import com.cloud.network.Networks;
 import com.cloud.network.dao.IPAddressDao;
+import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NsxProviderDao;
 import com.cloud.network.dao.PhysicalNetworkDao;
 import com.cloud.network.element.NsxProviderVO;
@@ -124,6 +125,8 @@ public class ConfigurationManagerImplTest {
     @Mock
     NsxProviderDao nsxProviderDao;
     @Mock
+    NetrisProviderDao netrisProviderDao;
+    @Mock
     DataCenterDao zoneDao;
     @Mock
     HostDao hostDao;
@@ -402,6 +405,7 @@ public class ConfigurationManagerImplTest {
         DataCenterVO dataCenterVO = Mockito.mock(DataCenterVO.class);
 
         when(nsxProviderDao.findByZoneId(anyLong())).thenReturn(nsxProviderVO);
+        when(netrisProviderDao.findByZoneId(anyLong())).thenReturn(null);
         when(zoneDao.findById(anyLong())).thenReturn(dataCenterVO);
         lenient().when(hostDao.findByDataCenterId(anyLong())).thenReturn(Collections.emptyList());
         when(podDao.listByDataCenterId(anyLong())).thenReturn(Collections.emptyList());
diff --git a/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java b/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java
index b5c842b8806..e7196b9dd5e 100644
--- a/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java
+++ b/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java
@@ -30,6 +30,7 @@ import com.cloud.network.Network.Service;
 import com.cloud.network.Networks.TrafficType;
 import com.cloud.network.VirtualRouterProvider.Type;
 import com.cloud.network.addr.PublicIp;
+import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.dao.NsxProviderDao;
 import com.cloud.network.dao.PhysicalNetworkServiceProviderVO;
@@ -76,6 +77,8 @@ public class RouterDeploymentDefinitionTest extends RouterDeploymentDefinitionTe
     protected NetworkVO mockNw;
     @Mock
     protected NsxProviderDao nsxProviderDao;
+    @Mock
+    protected NetrisProviderDao netrisProviderDao;
 
     protected RouterDeploymentDefinition deployment;
 
diff --git a/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java b/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java
index d3ab6d8904b..e7f75f5dbbb 100644
--- a/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java
+++ b/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java
@@ -24,6 +24,7 @@ import com.cloud.exception.InsufficientServerCapacityException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.exception.StorageUnavailableException;
 import com.cloud.network.addr.PublicIp;
+import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NsxProviderDao;
 import com.cloud.network.dao.PhysicalNetworkDao;
 import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
@@ -65,6 +66,8 @@ public class VpcRouterDeploymentDefinitionTest extends RouterDeploymentDefinitio
     @Mock
     protected NsxProviderDao nsxProviderDao;
     @Mock
+    protected NetrisProviderDao netrisProviderDao;
+    @Mock
     protected PhysicalNetworkDao mockPhNwDao;
     protected PhysicalNetworkServiceProviderDao mockPhProviderDao;
 


(cloudstack) 06/44: Add support to delete VNets and Subnets (#13)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 0f913b48a7222225ed7fefd8d6776ff93ebca8e4
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Wed Oct 23 10:01:35 2024 -0400

    Add support to delete VNets and Subnets (#13)
    
    * Add support to delete VNets and Subnets
    
    * Add support to delete vnet resources
    
    * Add support to delete vnet resources
    
    * extract code to method
    
    ---------
    
    Co-authored-by: nvazquez <ni...@gmail.com>
---
 .../com/cloud/network/netris/NetrisService.java    |   1 +
 .../agent/api/DeleteNetrisVnetCommand.java         |  29 ++++--
 .../apache/cloudstack/resource/NetrisResource.java |  18 ++++
 .../resource/NetrisResourceObjectUtils.java        |   4 +-
 .../apache/cloudstack/service/NetrisApiClient.java |   3 +
 .../cloudstack/service/NetrisApiClientImpl.java    | 114 ++++++++++++++++++++-
 .../apache/cloudstack/service/NetrisElement.java   |  55 +++++++++-
 .../cloudstack/service/NetrisServiceImpl.java      |   8 ++
 8 files changed, 218 insertions(+), 14 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index e11fb640632..f6203874c2d 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -22,4 +22,5 @@ public interface NetrisService {
     boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
     boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
     boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
+    boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
 }
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisVnetCommand.java
similarity index 53%
copy from api/src/main/java/com/cloud/network/netris/NetrisService.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisVnetCommand.java
index e11fb640632..8fbe9e04a71 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisVnetCommand.java
@@ -14,12 +14,29 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.network.netris;
+package org.apache.cloudstack.agent.api;
 
-import com.cloud.network.vpc.Vpc;
+public class DeleteNetrisVnetCommand extends NetrisCommand {
+    private String vpcName;
+    private Long vpcId;
+    private final String vNetCidr;
 
-public interface NetrisService {
-    boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
-    boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
-    boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
+    public DeleteNetrisVnetCommand(long zoneId, long accountId, long domainId, String name, long id, String vpcName, Long vpcId, String vNetCidr, boolean isVpc) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.vpcName = vpcName;
+        this.vpcId = vpcId;
+        this.vNetCidr = vNetCidr;
+    }
+
+    public String getVpcName() {
+        return vpcName;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public String getVNetCidr() {
+        return vNetCidr;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index ac3f06e34f5..0fdb0913053 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -30,6 +30,7 @@ import com.cloud.resource.ServerResource;
 import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.NetrisAnswer;
 import org.apache.cloudstack.StartupNetrisCommand;
@@ -91,6 +92,8 @@ public class NetrisResource implements ServerResource {
             return executeRequest((DeleteNetrisVpcCommand) cmd);
         } else if (cmd instanceof CreateNetrisVnetCommand) {
             return executeRequest((CreateNetrisVnetCommand) cmd);
+        } else if (cmd instanceof DeleteNetrisVnetCommand) {
+          return executeRequest((DeleteNetrisVnetCommand) cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
@@ -224,6 +227,21 @@ public class NetrisResource implements ServerResource {
         }
     }
 
+    private Answer executeRequest(DeleteNetrisVnetCommand cmd) {
+        try {
+            String networkName = cmd.getName();
+            boolean result = netrisApiClient.deleteVnet(cmd);
+            if (!result) {
+                return new NetrisAnswer(cmd, false, String.format("Netris vNet: %s deletion failed", networkName));
+            }
+            return new NetrisAnswer(cmd, true, "OK");
+        } catch (CloudRuntimeException e) {
+            String msg = String.format("Error deleting Netris vNet: %s", e.getMessage());
+            logger.error(msg, e);
+            return new NetrisAnswer(cmd, new CloudRuntimeException(msg));
+        }
+    }
+
     private Answer executeRequest(DeleteNetrisVpcCommand cmd) {
         boolean result = netrisApiClient.deleteVpc(cmd);
         if (!result) {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
index 8cfc5ea7918..127636dd7d5 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -46,9 +46,11 @@ public class NetrisResourceObjectUtils {
                 }
                 break;
             case IPAM_ALLOCATION:
-            case IPAM_SUBNET:
                 stringBuilder.append(String.format("%s%s", prefix, objectId));
                 break;
+            case IPAM_SUBNET:
+                stringBuilder.append(String.format("-N%s", objectId));
+                break;
             case VNET:
                break;
             default:
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index f0317687b95..42cb9a772fc 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -22,6 +22,7 @@ import io.netris.model.VPCListing;
 import io.netris.model.response.TenantResponse;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 
 import java.util.List;
@@ -47,4 +48,6 @@ public interface NetrisApiClient {
     boolean deleteVpc(DeleteNetrisVpcCommand cmd);
 
     boolean createVnet(CreateNetrisVnetCommand cmd);
+
+    boolean deleteVnet(DeleteNetrisVnetCommand cmd);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 5acaafefe17..16f5f7fc0d8 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -29,12 +29,16 @@ import io.netris.api.v2.VNetApi;
 import io.netris.api.v2.VpcApi;
 import io.netris.model.AllocationBody;
 import io.netris.model.AllocationBodyVpc;
+import io.netris.model.FilterBySites;
+import io.netris.model.FilterByVpc;
 import io.netris.model.GetSiteBody;
 import io.netris.model.InlineResponse2004;
 import io.netris.model.IpTreeAllocationTenant;
+import io.netris.model.IpTreeSubnet;
 import io.netris.model.IpTreeSubnetSites;
 import io.netris.model.SitesResponseOK;
 import io.netris.model.SubnetBody;
+import io.netris.model.SubnetResBody;
 import io.netris.model.VPCAdminTenant;
 import io.netris.model.VPCCreate;
 import io.netris.model.VPCListing;
@@ -49,11 +53,15 @@ import io.netris.model.VnetAddBodyDhcpOptionSet;
 import io.netris.model.VnetAddBodyGateways;
 import io.netris.model.VnetAddBodyVpc;
 import io.netris.model.VnetResAddBody;
+import io.netris.model.VnetResDeleteBody;
+import io.netris.model.VnetResListBody;
+import io.netris.model.VnetsBody;
 import io.netris.model.response.AuthResponse;
 import io.netris.model.response.TenantResponse;
 import io.netris.model.response.TenantsResponse;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
 import org.apache.commons.collections.CollectionUtils;
@@ -63,6 +71,7 @@ import org.apache.logging.log4j.Logger;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 public class NetrisApiClientImpl implements NetrisApiClient {
@@ -238,6 +247,10 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             VpcApi vpcApi = apiClient.getApiStubForMethod(VpcApi.class);
             VPCResponseResourceOK vpcResourcesResponse = vpcApi.apiV2VpcVpcIdResourcesGet(vpcResource.getId());
             VPCResourceIpam vpcAllocationResource = getVpcAllocationResource(vpcResourcesResponse);
+            if (Objects.isNull(vpcAllocationResource)) {
+                logger.info("No VPC IPAM Allocation found for VPC {}", vpcCidr);
+                return;
+            }
             IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
             logger.debug("Removing the IPAM allocation {} with ID {}", vpcAllocationResource.getName(), vpcAllocationResource.getId());
             ipamApi.apiV2IpamTypeIdDelete("allocation", vpcAllocationResource.getId());
@@ -304,11 +317,11 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String vnetCidr = cmd.getCidr();
         boolean isVpc = cmd.isVpc();
 
-        String suffix = String.format("%s-%s", vpcId, vpcName);
+        String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
         String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
         VPCListing associatedVpc = getVpcByNameAndTenant(netrisVpcName);
         if (associatedVpc == null) {
-            logger.error(String.format("Failed to find Netris VPC with name: %s, to create the corresponding vNet for network %s", vpcName, networkName));
+            logger.error("Failed to find Netris VPC with name: {}, to create the corresponding vNet for network {}", netrisVpcName, networkName);
             return false;
         }
 
@@ -324,20 +337,102 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         InlineResponse2004 subnetResponse = createVpcSubnetInternal(associatedVpc, vNetName, vnetCidr, netrisSubnetName);
         if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
             String reason = subnetResponse == null ? "Empty response" : "Operation failed on Netris";
-            logger.debug("The Netris Subnet {} for VPC {} for network {} creation failed: {}", vnetCidr, vpcName, networkName, reason);
+            logger.debug("The Netris Subnet {} for network {} creation failed: {}", vnetCidr, networkName, reason);
             return false;
         }
-        logger.debug("Successfully created VPC {} and its IPAM Subnet {} on Netris", vpcName, vnetCidr);
+        logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
 
         VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr);
         if (vnetResponse == null || !vnetResponse.isIsSuccess()) {
             String reason = vnetResponse == null ? "Empty response" : "Operation failed on Netris";
-            logger.debug("The Netris vNet creation {} for VPC {} failed: {}", vNetName, vpcName, reason);
+            logger.debug("The Netris vNet creation {} failed: {}", vNetName, reason);
             return false;
         }
         return true;
     }
 
+    @Override
+    public boolean deleteVnet(DeleteNetrisVnetCommand cmd) {
+        String vpcName = cmd.getVpcName();
+        Long vpcId = cmd.getVpcId();
+        String networkName = cmd.getName();
+        Long networkId = cmd.getId();
+        boolean isVpc = cmd.isVpc();
+        String vnetCidr = cmd.getVNetCidr();
+        try {
+            String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
+            String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
+            VPCListing associatedVpc = getVpcByNameAndTenant(netrisVpcName);
+            if (associatedVpc == null) {
+                logger.error("Failed to find Netris VPC with name: {}, to create the corresponding vNet for network {}", netrisVpcName, networkName);
+                return false;
+            }
+
+            String vNetName;
+            if (isVpc) {
+                vNetName = String.format("V%s-N%s-%s", vpcId, networkId, networkName);
+            } else {
+                vNetName = String.format("N%s-%s", networkId, networkName);
+            }
+
+            String netrisVnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VNET, vNetName) ;
+            String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(associatedVpc.getId());
+            FilterBySites siteFilter = new FilterBySites();
+            siteFilter.add(siteId);
+            deleteVnetInternal(associatedVpc, siteFilter, vpcFilter, netrisVnetName, vNetName);
+
+            logger.debug("Successfully deleted vNet {}", vNetName);
+            deleteSubnetInternal(vpcFilter, netrisVnetName, netrisSubnetName);
+
+        } catch (Exception e) {
+            throw new CloudRuntimeException(String.format("Failed to delete Netris vNet %s", networkName), e);
+        }
+        return true;
+    }
+
+    private void deleteVnetInternal(VPCListing associatedVpc, FilterBySites siteFilter, FilterByVpc vpcFilter, String netrisVnetName, String vNetName) {
+        try {
+            VNetApi vNetApi = apiClient.getApiStubForMethod(VNetApi.class);
+            VnetResListBody vnetList = vNetApi.apiV2VnetGet(siteFilter, vpcFilter);
+            if (vnetList == null || !vnetList.isIsSuccess()) {
+                throw new CloudRuntimeException(String.format("Failed to list vNets for the given VPC: %s and site: %s", associatedVpc.getName(), siteName));
+            }
+            List<VnetsBody> vnetsList = vnetList.getData().stream().filter(vnet -> vnet.getName().equals(netrisVnetName)).collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(vnetsList)) {
+                logger.debug("vNet: {} for the given VPC: {} appears to already be deleted on Netris", vNetName, associatedVpc.getName());
+                return;
+            }
+            VnetsBody vnetsBody = vnetsList.get(0);
+
+            VnetResDeleteBody deleteVnetResponse = vNetApi.apiV2VnetIdDelete(vnetsBody.getId().intValue());
+            if (deleteVnetResponse == null || !deleteVnetResponse.isIsSuccess()) {
+                throw new CloudRuntimeException(String.format("Failed to delete vNet: %s", vNetName));
+            }
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to delete vNet: %s", netrisVnetName), e);
+        }
+    }
+
+    private void deleteSubnetInternal(FilterByVpc vpcFilter, String netrisVnetName, String netrisSubnetName) {
+        try {
+            logger.debug("Deleting Netris VPC IPAM Subnet {} for vNet: {}", netrisSubnetName, netrisVnetName);
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            SubnetResBody subnetsResponse = ipamApi.apiV2IpamSubnetsGet(vpcFilter);
+            List<IpTreeSubnet> subnets = subnetsResponse.getData();
+            List<IpTreeSubnet> matchedSubnets = subnets.stream().filter(subnet -> subnet.getName().equals(netrisSubnetName)).collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(matchedSubnets)) {
+                logger.debug("IPAM subnet: {} for the given vNet: {} appears to already be deleted on Netris", netrisSubnetName, netrisVnetName);
+                return;
+            }
+
+            ipamApi.apiV2IpamTypeIdDelete("subnet", matchedSubnets.get(0).getId().intValue());
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Failed to delete vNet: %s", netrisVnetName), e);
+        }
+    }
+
     private InlineResponse2004 createVpcSubnetInternal(VPCListing associatedVpc, String vNetName, String vNetCidr, String netrisSubnetName) {
         logger.debug("Creating Netris VPC Subnet {} for VPC {} for vNet {}", vNetCidr, associatedVpc.getName(), vNetName);
         try {
@@ -427,4 +522,13 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             return null;
         }
     }
+
+    private String getNetrisVpcNameSuffix(Long vpcId, String vpcName, Long networkId, String networkName, boolean isVpc) {
+        String suffix = null;
+        if (isVpc) {
+            suffix = String.format("%s-%s", vpcId, vpcName);
+        } else {
+            suffix = String.format("%s-%s", networkId, networkName);
+        }
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index f15090dc3f4..9a29a410862 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -23,7 +23,11 @@ import com.cloud.agent.api.AgentControlCommand;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
 import com.cloud.agent.api.StartupCommand;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.deploy.DeployDestination;
+import com.cloud.domain.DomainVO;
+import com.cloud.domain.dao.DomainDao;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.ConnectionException;
 import com.cloud.exception.InsufficientCapacityException;
@@ -35,8 +39,11 @@ import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.NetworkModel;
 import com.cloud.network.PhysicalNetworkServiceProvider;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.element.DhcpServiceProvider;
 import com.cloud.network.element.DnsServiceProvider;
+import com.cloud.network.element.NetworkACLServiceProvider;
 import com.cloud.network.element.VirtualRouterElement;
 import com.cloud.network.element.VpcProvider;
 import com.cloud.network.netris.NetrisService;
@@ -45,12 +52,17 @@ import com.cloud.network.vpc.NetworkACLItem;
 import com.cloud.network.vpc.PrivateGateway;
 import com.cloud.network.vpc.StaticRouteProfile;
 import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.resource.ResourceManager;
 import com.cloud.resource.ResourceStateAdapter;
 import com.cloud.resource.ServerResource;
 import com.cloud.resource.UnableDeleteHostException;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
 import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.NicProfile;
 import com.cloud.vm.ReservationContext;
 import com.cloud.vm.VirtualMachineProfile;
@@ -64,11 +76,12 @@ import javax.naming.ConfigurationException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 @Component
-public class NetrisElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, ResourceStateAdapter,
-        Listener, VpcProvider {
+public class NetrisElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider,
+        NetworkACLServiceProvider, ResourceStateAdapter, Listener {
 
     @Inject
     NetworkModel networkModel;
@@ -78,6 +91,16 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
     ResourceManager resourceManager;
     @Inject
     private NetrisService netrisService;
+    @Inject
+    private AccountManager accountManager;
+    @Inject
+    private DataCenterDao dataCenterDao;
+    @Inject
+    private NetworkDao networkDao;
+    @Inject
+    private DomainDao domainDao;
+    @Inject
+    private VpcDao vpcDao;
 
     protected Logger logger = LogManager.getLogger(getClass());
 
@@ -268,6 +291,24 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
 
     @Override
     public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
+        Account account = accountManager.getAccount(network.getAccountId());
+        NetworkVO networkVO = networkDao.findById(network.getId());
+        DataCenterVO zone = dataCenterDao.findById(network.getDataCenterId());
+        DomainVO domain = domainDao.findById(account.getDomainId());
+        if (Objects.isNull(zone)) {
+            String msg = String.format("Cannot find zone with ID %s", network.getDataCenterId());
+            logger.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+        String vpcName = null;
+        Long vpcId = network.getVpcId();
+        if (Objects.nonNull(vpcId)) {
+            VpcVO vpc = vpcDao.findById(vpcId);
+            if (Objects.nonNull(vpc)) {
+                vpcName = vpc.getName();
+            }
+        }
+        netrisService.deleteVnetResource(zone.getId(), account.getId(), domain.getId(), vpcName, vpcId, networkVO.getName(), network.getId(), network.getCidr());
         return true;
     }
 
@@ -347,4 +388,14 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
     public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
         return true;
     }
+
+    @Override
+    public boolean applyNetworkACLs(Network config, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean reorderAclRules(Vpc vpc, List<? extends Network> networks, List<? extends NetworkACLItem> networkACLItems) {
+        return false;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 66d972b043e..eb2046718c2 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -25,6 +25,7 @@ import com.cloud.network.netris.NetrisService;
 import com.cloud.network.vpc.Vpc;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.NetrisAnswer;
 import org.apache.cloudstack.agent.api.NetrisCommand;
@@ -94,4 +95,11 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
         return answer.getResult();
     }
+
+    @Override
+    public boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
+        DeleteNetrisVnetCommand cmd = new DeleteNetrisVnetCommand(zoneId, accountId, domainId, networkName, networkId, vpcName, vpcId, cidr, Objects.nonNull(vpcName));
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
 }


(cloudstack) 19/44: List only Netris Public IPs for NAT operations (#26)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit aef61973f3ee281cb599c58db9847d270204fd15
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Thu Dec 5 10:33:10 2024 -0500

    List only Netris Public IPs for NAT operations (#26)
    
    * List only Netris Public IPs for NAT operations
    
    * rename getter and change type
    
    * fix failing unit tests
    
    * list all IPs if forProvider is not passed
    
    * fix list public IPs for external providers with additional IP range
    
    * filter provider Ips in a zone with external provider setup
    
    * Prevent acquiring IP that is not from the external provider range
    
    * formating
    
    ---------
    
    Co-authored-by: nvazquez <ni...@gmail.com>
---
 .../org/apache/cloudstack/api/ApiConstants.java    |  1 +
 .../user/address/ListPublicIpAddressesCmd.java     |  7 ++++
 .../cloudstack/api/response/IPAddressResponse.java |  6 ++++
 .../main/java/com/cloud/api/ApiResponseHelper.java |  3 ++
 .../com/cloud/network/IpAddressManagerImpl.java    | 39 ++++++++++++++++++++++
 .../com/cloud/server/ManagementServerImpl.java     | 32 ++++++++++++++++--
 .../com/cloud/server/ManagementServerImplTest.java | 10 ++++++
 ui/src/views/AutogenView.vue                       | 17 +++++++---
 ui/src/views/network/IpAddressesTab.vue            | 22 ++++++++++--
 9 files changed, 128 insertions(+), 9 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index b0d01e89c7d..c0b478977c8 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -210,6 +210,7 @@ public class ApiConstants {
     public static final String FORMAT = "format";
     public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
     public static final String FOR_SYSTEM_VMS = "forsystemvms";
+    public static final String FOR_PROVIDER = "forprovider";
     public static final String FULL_PATH = "fullpath";
     public static final String GATEWAY = "gateway";
     public static final String IP6_GATEWAY = "ip6gateway";
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java
index 5760ca3ba1c..a9d651ae5fe 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java
@@ -108,6 +108,9 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC
     @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if range is dedicated for system VMs", since = "4.20.0")
     private Boolean forSystemVMs;
 
+    @Parameter(name = ApiConstants.FOR_PROVIDER, type = CommandType.BOOLEAN, description = "true if range is dedicated for external network provider", since = "4.20.0")
+    private Boolean forProvider;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -183,6 +186,10 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC
         return BooleanUtils.isTrue(forSystemVMs);
     }
 
+    public boolean isForProvider() {
+        return BooleanUtils.isTrue(forProvider);
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java
index 0018edc8638..0f339d5d5b1 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java
@@ -175,6 +175,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co
     @Param(description="true if range is dedicated for System VMs")
     private boolean forSystemVms;
 
+    @SerializedName(ApiConstants.FOR_PROVIDER)
+    @Param(description="true if range is dedicated for external network providers")
+    private boolean forProvider;
+
     public void setIpAddress(String ipAddress) {
         this.ipAddress = ipAddress;
     }
@@ -332,4 +336,6 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co
     public void setForSystemVms(boolean forSystemVms) {
         this.forSystemVms = forSystemVms;
     }
+
+    public void setForProvider(boolean forProvider) { this.forProvider = forProvider; }
 }
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 2f5c6d2832a..43b3932b00c 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -1168,6 +1168,9 @@ public class ApiResponseHelper implements ResponseGenerator {
 
         ipResponse.setPortable(ipAddr.isPortable());
         ipResponse.setForSystemVms(ipAddr.isForSystemVms());
+        if (Objects.nonNull(getProviderFromVlanDetailKey(vlan))) {
+            ipResponse.setForProvider(true);
+        }
 
         //set tag information
         List<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(ResourceObjectType.PublicIpAddress, ipAddr.getId());
diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
index e6be174abcd..e440d39462e 100644
--- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
@@ -33,7 +33,13 @@ import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
+import com.cloud.dc.VlanDetailsVO;
+import com.cloud.dc.dao.VlanDetailsDao;
+import com.cloud.network.dao.NetrisProviderDao;
+import com.cloud.network.dao.NsxProviderDao;
 import com.cloud.network.dao.PublicIpQuarantineDao;
+import com.cloud.network.element.NetrisProviderVO;
+import com.cloud.network.element.NsxProviderVO;
 import com.cloud.network.vo.PublicIpQuarantineVO;
 import com.cloud.resourcelimit.CheckedReservation;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
@@ -189,6 +195,7 @@ import com.cloud.vm.dao.NicIpAliasDao;
 import com.cloud.vm.dao.NicSecondaryIpDao;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.commons.lang3.ObjectUtils;
 
 public class IpAddressManagerImpl extends ManagerBase implements IpAddressManager, Configurable {
 
@@ -313,6 +320,12 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
     private AnnotationDao annotationDao;
     @Inject
     MessageBus messageBus;
+    @Inject
+    NsxProviderDao nsxProviderDao;
+    @Inject
+    NetrisProviderDao netrisProviderDao;
+    @Inject
+    VlanDetailsDao vlanDetailsDao;
 
     @Inject
     PublicIpQuarantineDao publicIpQuarantineDao;
@@ -1301,6 +1314,30 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
         }
     }
 
+    /**
+     * When the zone is linked to external provider NSX or Netris: check if the IP to be associated is from the suitable pool
+     * Otherwise, no checks are performed
+     */
+    private void checkPublicIpOnExternalProviderZone(DataCenter zone, String ip) {
+        long zoneId = zone.getId();
+        NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId);
+        NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId);
+        if (ObjectUtils.allNull(netrisProvider, nsxProvider)) {
+            return;
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findByIpAndDcId(zoneId, ip);
+        if (ipAddress != null) {
+            String detailKey = nsxProvider != null ? ApiConstants.NSX_DETAIL_KEY : ApiConstants.NETRIS_DETAIL_KEY;
+            VlanDetailsVO vlanDetailVO = vlanDetailsDao.findDetail(ipAddress.getVlanId(), detailKey);
+            if (vlanDetailVO == null || vlanDetailVO.getValue().equalsIgnoreCase("false")) {
+                String msg = String.format("Cannot acquire IP %s on the zone %s as the IP is not from the reserved pool " +
+                        "for the external provider", ip, zone.getName());
+                logger.error(msg);
+                throw new CloudRuntimeException(msg);
+            }
+        }
+    }
+
     @DB
     @Override
     public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Account caller, long callerUserId, final DataCenter zone, final Boolean displayIp, final String ipaddress)
@@ -1309,6 +1346,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
         final VlanType vlanType = VlanType.VirtualNetwork;
         final boolean assign = false;
 
+        checkPublicIpOnExternalProviderZone(zone, ipaddress);
+
         if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) {
             // zone is of type DataCenter. See DataCenterVO.java.
             PermissionDeniedException ex = new PermissionDeniedException(generateErrorMessageForOperationOnDisabledZone("allocate IP addresses", zone));
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index 87b3dd3cbd9..c071d3626ed 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -44,6 +44,11 @@ import javax.crypto.spec.SecretKeySpec;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.dc.VlanDetailsVO;
+import com.cloud.dc.dao.VlanDetailsDao;
+import com.cloud.network.dao.NetrisProviderDao;
+import com.cloud.network.dao.NsxProviderDao;
+
 import com.cloud.utils.security.CertificateHelper;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.acl.SecurityChecker;
@@ -885,6 +890,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
     @Inject
     private VlanDao _vlanDao;
     @Inject
+    private VlanDetailsDao vlanDetailsDao;
+    @Inject
     private AccountVlanMapDao _accountVlanMapDao;
     @Inject
     private PodVlanMapDao _podVlanMapDao;
@@ -927,7 +934,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
     @Inject
     private StoragePoolJoinDao _poolJoinDao;
     @Inject
-    private NetworkDao networkDao;
+    protected NetworkDao networkDao;
     @Inject
     private StorageManager _storageMgr;
     @Inject
@@ -997,7 +1004,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
     @Inject
     private NetworkModel _networkMgr;
     @Inject
-    private VpcDao _vpcDao;
+    protected VpcDao _vpcDao;
     @Inject
     private DomainVlanMapDao _domainVlanMapDao;
     @Inject
@@ -1027,6 +1034,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
     protected AffinityGroupVMMapDao _affinityGroupVMMapDao;
     @Inject
     ResourceLimitService resourceLimitService;
+    @Inject
+    NsxProviderDao nsxProviderDao;
+    @Inject
+    NetrisProviderDao netrisProviderDao;
 
     private LockControllerListener _lockControllerListener;
     private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
@@ -2571,6 +2582,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         final String address = cmd.getIpAddress();
         final Boolean forLoadBalancing = cmd.isForLoadBalancing();
         final Map<String, String> tags = cmd.getTags();
+        boolean forProvider = cmd.isForProvider();
 
         sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
         sb.and("address", sb.entity().getAddress(), SearchCriteria.Op.EQ);
@@ -2616,13 +2628,21 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         if (isAllocated != null && isAllocated) {
             sb.and("allocated", sb.entity().getAllocatedTime(), SearchCriteria.Op.NNULL);
         }
+
+        if (forProvider) {
+            SearchBuilder<VlanDetailsVO> vlanDetailsSearch = vlanDetailsDao.createSearchBuilder();
+            vlanDetailsSearch.and("name", vlanDetailsSearch.entity().getName(), SearchCriteria.Op.IN);
+            vlanDetailsSearch.and("value", vlanDetailsSearch.entity().getValue(), SearchCriteria.Op.EQ);
+            sb.join("vlanDetailSearch", vlanDetailsSearch, sb.entity().getVlanId(), vlanDetailsSearch.entity().getResourceId(), JoinType.LEFT);
+        }
     }
 
     protected void setParameters(SearchCriteria<IPAddressVO> sc, final ListPublicIpAddressesCmd cmd, VlanType vlanType, Boolean isAllocated) {
         final Object keyword = cmd.getKeyword();
         final Long physicalNetworkId = cmd.getPhysicalNetworkId();
         final Long sourceNetworkId = cmd.getNetworkId();
-        final Long zone = cmd.getZoneId();
+        final Long vpcId = cmd.getVpcId();
+        Long zone = cmd.getZoneId();
         final String address = cmd.getIpAddress();
         final Long vlan = cmd.getVlanId();
         final Long ipId = cmd.getId();
@@ -2631,6 +2651,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         final Boolean forDisplay = cmd.getDisplay();
         final String state = cmd.getState();
         final Boolean forSystemVms = cmd.getForSystemVMs();
+        final boolean forProvider = cmd.isForProvider();
         final Map<String, String> tags = cmd.getTags();
 
         sc.setJoinParameters("vlanSearch", "vlanType", vlanType);
@@ -2696,6 +2717,11 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         } else {
             sc.setParameters(FOR_SYSTEMVMS, forSystemVms);
         }
+
+        if (forProvider) {
+            sc.setJoinParameters("vlanDetailSearch", "name", ApiConstants.NETRIS_DETAIL_KEY, ApiConstants.NSX_DETAIL_KEY);
+            sc.setJoinParameters("vlanDetailSearch", "value", "true");
+        }
     }
 
     @Override
diff --git a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java
index b26cd455cfb..7d828236ab3 100644
--- a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java
+++ b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java
@@ -25,6 +25,8 @@ import com.cloud.host.dao.HostDetailsDao;
 import com.cloud.network.IpAddress;
 import com.cloud.network.IpAddressManagerImpl;
 import com.cloud.network.dao.IPAddressVO;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.vpc.dao.VpcDao;
 import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.user.Account;
@@ -123,6 +125,12 @@ public class ManagementServerImplTest {
     @Mock
     UserDataManager userDataManager;
 
+    @Mock
+    VpcDao vpcDao;
+
+    @Mock
+    NetworkDao networkDao;
+
     @Spy
     ManagementServerImpl spy = new ManagementServerImpl();
 
@@ -145,6 +153,8 @@ public class ManagementServerImplTest {
         spy._UserVmDetailsDao = userVmDetailsDao;
         spy._detailsDao = hostDetailsDao;
         spy.userDataManager = userDataManager;
+        spy._vpcDao = vpcDao;
+        spy.networkDao = networkDao;
     }
 
     @After
diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue
index bfdde77191e..54524ccc6b8 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -1001,6 +1001,19 @@ export default {
         }
 
         this.items = json[responseName][objectName]
+        var filteredItems = []
+        if (this.apiName === 'listPublicIpAddresses') {
+          for (var zone of this.$store.getters.zones) {
+            const zoneIps = this.items.filter(item => item.zoneid === zone.id)
+            const providerIps = zoneIps.filter(item => item.forprovider === true)
+            if (providerIps.length === 0) {
+              filteredItems.push(...zoneIps)
+            } else {
+              filteredItems.push(...providerIps)
+            }
+          }
+          this.items = filteredItems
+        }
         if (!this.items || this.items.length === 0) {
           this.items = []
         }
@@ -1931,7 +1944,6 @@ export default {
           this.rules[field.name].push(rule)
           break
         case (this.currentAction.mapping && field.name in this.currentAction.mapping && 'options' in this.currentAction.mapping[field.name]):
-          console.log('op: ' + field)
           rule.required = field.required
           rule.message = this.$t('message.error.select')
           this.rules[field.name].push(rule)
@@ -1942,20 +1954,17 @@ export default {
           this.rules[field.name].push(rule)
           break
         case (field.type === 'uuid'):
-          console.log('uuid: ' + field)
           rule.required = field.required
           rule.message = this.$t('message.error.select')
           this.rules[field.name].push(rule)
           break
         case (field.type === 'list'):
-          console.log('list: ' + field)
           rule.type = 'array'
           rule.required = field.required
           rule.message = this.$t('message.error.select')
           this.rules[field.name].push(rule)
           break
         case (field.type === 'long'):
-          console.log(field)
           rule.type = 'number'
           rule.required = field.required
           rule.message = this.$t('message.validate.number')
diff --git a/ui/src/views/network/IpAddressesTab.vue b/ui/src/views/network/IpAddressesTab.vue
index 37b5b210a96..8f232129381 100644
--- a/ui/src/views/network/IpAddressesTab.vue
+++ b/ui/src/views/network/IpAddressesTab.vue
@@ -282,10 +282,12 @@ export default {
       acquireLoading: false,
       acquireIp: null,
       listPublicIpAddress: [],
-      changeSourceNat: false
+      changeSourceNat: false,
+      zoneExtNetProvider: ''
     }
   },
-  created () {
+  async created () {
+    await this.fetchZones()
     this.fetchData()
   },
   watch: {
@@ -321,6 +323,9 @@ export default {
       } else {
         params.associatednetworkid = this.resource.id
       }
+      if (['nsx', 'netris'].includes(this.zoneExtNetProvider?.toLowerCase())) {
+        params.forprovider = true
+      }
       this.fetchLoading = true
       api('listPublicIpAddresses', params).then(json => {
         this.totalIps = json.listpublicipaddressesresponse.count || 0
@@ -329,6 +334,16 @@ export default {
         this.fetchLoading = false
       })
     },
+    fetchZones () {
+      return new Promise((resolve, reject) => {
+        api('listZones', {
+          id: this.resource.zoneid
+        }).then(json => {
+          this.zoneExtNetProvider = json?.listzonesresponse?.zone?.[0]?.provider || null
+          resolve(this.zoneExtNetProvider)
+        }).catch(reject)
+      })
+    },
     fetchListPublicIpAddress () {
       return new Promise((resolve, reject) => {
         const params = {
@@ -338,6 +353,9 @@ export default {
           forvirtualnetwork: true,
           allocatedonly: false
         }
+        if (['nsx', 'netris'].includes(this.zoneExtNetProvider?.toLowerCase())) {
+          params.forprovider = true
+        }
         api('listPublicIpAddresses', params).then(json => {
           const listPublicIps = json.listpublicipaddressesresponse.publicipaddress || []
           resolve(listPublicIps)


(cloudstack) 11/44: Fix VR Public IP address (#20)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 3ee9ed9233e64c23fb187e2097e2355421aeb8de
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Tue Nov 26 15:25:46 2024 +0100

    Fix VR Public IP address (#20)
    
    * Fix VR Public IP address
    
    * Do not set the Public IP range on Netris side that is not part of the Netris IP Public Pool
    
    * Leave only systemvms tag for the first element
    
    * Fix NSX compatibility
---
 .../service/NetrisProviderServiceImpl.java         | 41 +++++++++++++---------
 .../java/com/cloud/network/vpc/VpcManagerImpl.java |  5 ++-
 ui/src/views/infra/zone/IpAddressRangeForm.vue     |  6 ++--
 3 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
index 2887e03a1f5..119a4c80f94 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
@@ -18,9 +18,11 @@ package org.apache.cloudstack.service;
 
 import com.cloud.agent.api.Answer;
 import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.VlanDetailsVO;
 import com.cloud.dc.VlanVO;
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.dc.dao.VlanDao;
+import com.cloud.dc.dao.VlanDetailsDao;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.host.DetailVO;
 import com.cloud.host.Host;
@@ -45,6 +47,7 @@ import com.google.common.annotations.VisibleForTesting;
 import inet.ipaddr.IPAddress;
 import inet.ipaddr.IPAddressString;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseResponse;
 import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
 import org.apache.cloudstack.api.command.DeleteNetrisProviderCmd;
@@ -87,6 +90,8 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
     private IPAddressDao ipAddressDao;
     @Inject
     private VlanDao vlanDao;
+    @Inject
+    private VlanDetailsDao vlanDetailsDao;
 
     @Override
     public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
@@ -193,22 +198,26 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
                 logger.error(msg);
                 throw new CloudRuntimeException(msg);
             }
-            if (vlanDbIds.size() > 1) {
-                logger.warn("Expected one Netris Public IP range but found {}. Using the first one to create the Netris IPAM allocation and NAT subnet", vlanDbIds.size());
-            }
-            VlanVO vlanRecord = vlanDao.findById(vlanDbIds.get(0));
-            if (vlanRecord == null) {
-                logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
-                return;
-            }
-            String gateway = vlanRecord.getVlanGateway();
-            String netmask = vlanRecord.getVlanNetmask();
-            String ipRange = vlanRecord.getIpRange();
-            SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
-            Answer answer = netrisResource.executeRequest(cmd);
-            boolean result = answer != null && answer.getResult();
-            if (!result) {
-                throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
+            for (Long vlanDbId : vlanDbIds) {
+                VlanVO vlanRecord = vlanDao.findById(vlanDbId);
+                if (vlanRecord == null) {
+                    logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
+                    return;
+                }
+                VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
+                if (vlanDetail == null) {
+                    logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
+                    continue;
+                }
+                String gateway = vlanRecord.getVlanGateway();
+                String netmask = vlanRecord.getVlanNetmask();
+                String ipRange = vlanRecord.getIpRange();
+                SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
+                Answer answer = netrisResource.executeRequest(cmd);
+                boolean result = answer != null && answer.getResult();
+                if (!result) {
+                    throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
+                }
             }
         }
     }
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index de17c25a72d..0c74d464fe3 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -3364,7 +3364,10 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
             ipToReturn = PublicIp.createFromAddrAndVlan(sourceNatIp, _vlanDao.findById(sourceNatIp.getVlanId()));
         } else {
             if (forNsx || forNetris) {
-                ipToReturn = _ipAddrMgr.assignPublicIpAddress(dcId, podId, owner, Vlan.VlanType.VirtualNetwork, null, null, false, true);
+                // Assign VR (helper VM) public NIC IP address from the separate provider Public IP range/pool
+                // NSX: VR uses Public IP from the system VM range
+                // Netris: VR uses Public IP from the non system VM range
+                ipToReturn = _ipAddrMgr.assignPublicIpAddress(dcId, podId, owner, Vlan.VlanType.VirtualNetwork, null, null, false, forNsx);
             } else {
                 ipToReturn = _ipAddrMgr.assignDedicateIpAddress(owner, null, vpc.getId(), dcId, true);
             }
diff --git a/ui/src/views/infra/zone/IpAddressRangeForm.vue b/ui/src/views/infra/zone/IpAddressRangeForm.vue
index 04d7ad4152f..4c9af84a8f8 100644
--- a/ui/src/views/infra/zone/IpAddressRangeForm.vue
+++ b/ui/src/views/infra/zone/IpAddressRangeForm.vue
@@ -30,12 +30,12 @@
         :columns="columns"
         :pagination="false"
         style="margin-bottom: 24px; width: 100%" >
-        <template #bodyCell="{ column, record }">
+        <template #bodyCell="{ column, record, index }">
           <template v-if="column.key === 'gateway'">
             <div> {{  record.gateway }}</div>
             <div v-if="record.fornsx"> <a-tag color="processing"> {{ $t('label.tag.nsx') }} </a-tag> </div>
             <div v-else-if="record.fornetris"> <a-tag color="processing"> {{ $t('label.tag.netris') }} </a-tag> </div>
-            <div v-else-if="isNsxZone || isNetrisZone"> <a-tag color="processing"> {{ $t('label.tag.systemvm') }}  </a-tag> </div>
+            <div v-else-if="index === 0 && (isNsxZone || isNetrisZone)"> <a-tag color="processing"> {{ $t('label.tag.systemvm') }}  </a-tag> </div>
           </template>
           <template v-if="column.key === 'actions'">
             <tooltip-button
@@ -281,7 +281,7 @@ export default {
           endIp: values.endIp,
           fornsx: this.forNsx,
           fornetris: this.forNetris,
-          forsystemvms: (this.isNsxZone && !this.forNsx) || (this.isNetrisZone && !this.forNetris)
+          forsystemvms: (this.isNsxZone && !this.forNsx) || (this.isNetrisZone && !this.forNetris && key === 0) // Set only the first public IP range for system VMs on a Netris Zone creation
         })
         this.formRef.value.resetFields()
       }).catch(error => {


(cloudstack) 13/44: Run moodifyvxlan script if broadcast domain type is Netris (#18)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 4c07565bf04cdefa5f87f0b70fa3fd05dff61da8
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Tue Nov 26 14:20:25 2024 -0500

    Run moodifyvxlan script if broadcast domain type is Netris (#18)
---
 .../com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java  | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
index 26b8de53083..8ed780a9714 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
@@ -51,6 +51,10 @@ public class BridgeVifDriver extends VifDriverBase {
     private String _controlCidr = NetUtils.getLinkLocalCIDR();
     private Long libvirtVersion;
 
+    private static boolean isVxlanOrNetris(String protocol) {
+        return protocol.equals(Networks.BroadcastDomainType.Vxlan.scheme()) || protocol.equals(Networks.BroadcastDomainType.Netris.scheme());
+    }
+
     @Override
     public void configure(Map<String, Object> params) throws ConfigurationException {
 
@@ -177,7 +181,7 @@ public class BridgeVifDriver extends VifDriverBase {
 
     protected boolean isBroadcastTypeVlanOrVxlan(final NicTO nic) {
         return nic != null && (nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan
-                || nic.getBroadcastType() == Networks.BroadcastDomainType.Vxlan);
+                || nic.getBroadcastType() == Networks.BroadcastDomainType.Vxlan || nic.getBroadcastType() == Networks.BroadcastDomainType.Netris);
     }
 
     protected boolean isValidProtocolAndVnetId(final String vNetId, final String protocol) {
@@ -284,7 +288,7 @@ public class BridgeVifDriver extends VifDriverBase {
 
     private String createVnetBr(String vNetId, String pifKey, String protocol) throws InternalErrorException {
         String nic = _pifs.get(pifKey);
-        if (nic == null || protocol.equals(Networks.BroadcastDomainType.Vxlan.scheme())) {
+        if (nic == null || isVxlanOrNetris(protocol)) {
             // if not found in bridge map, maybe traffic label refers to pif already?
             File pif = new File("/sys/class/net/" + pifKey);
             if (pif.isDirectory()) {
@@ -292,7 +296,7 @@ public class BridgeVifDriver extends VifDriverBase {
             }
         }
         String brName = "";
-        if (protocol.equals(Networks.BroadcastDomainType.Vxlan.scheme())) {
+        if (isVxlanOrNetris(protocol)) {
             brName = generateVxnetBrName(nic, vNetId);
         } else {
             brName = generateVnetBrName(nic, vNetId);
@@ -304,7 +308,7 @@ public class BridgeVifDriver extends VifDriverBase {
     private void createVnet(String vnetId, String pif, String brName, String protocol) throws InternalErrorException {
         synchronized (_vnetBridgeMonitor) {
             String script = _modifyVlanPath;
-            if (protocol.equals(Networks.BroadcastDomainType.Vxlan.scheme())) {
+            if (isVxlanOrNetris(protocol)) {
                 script = _modifyVxlanPath;
             }
             final Script command = new Script(script, _timeout, logger);
@@ -358,6 +362,7 @@ public class BridgeVifDriver extends VifDriverBase {
             }
 
             String scriptPath = null;
+            // TODO: check for netris as well
             if (cmdout != null && cmdout.contains("vxlan")) {
                 scriptPath = _modifyVxlanPath;
             } else {


(cloudstack) 01/44: Support for listing VPCs (#5)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 09afd261e868fe409bae77a424e72130e1c60dd4
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Fri Oct 11 09:02:34 2024 -0400

    Support for listing VPCs (#5)
---
 .../org/apache/cloudstack/service/NetrisApiClient.java |  3 +++
 .../apache/cloudstack/service/NetrisApiClientImpl.java | 18 ++++++++++++++++--
 .../cloudstack/service/NetrisApiClientImplTest.java    |  7 +++++++
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index ac59f3f8c3c..cc6ffee1102 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -17,10 +17,13 @@
 package org.apache.cloudstack.service;
 
 import io.netris.model.GetSiteBody;
+import io.netris.model.VPCListing;
 
 import java.util.List;
 
 public interface NetrisApiClient {
     boolean isSessionAlive();
     List<GetSiteBody> listSites();
+    List<VPCListing> listVPCs();
+
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 1654954017e..3be955954fe 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -20,11 +20,14 @@ import com.cloud.utils.exception.CloudRuntimeException;
 import io.netris.ApiClient;
 import io.netris.ApiException;
 import io.netris.ApiResponse;
-import io.netris.api.AuthenticationApi;
-import io.netris.api.SitesApi;
+import io.netris.api.v1.AuthenticationApi;
+import io.netris.api.v1.SitesApi;
+import io.netris.api.v2.VpcApi;
 import io.netris.model.AuthSchema;
 import io.netris.model.GetSiteBody;
 import io.netris.model.SitesResponseOK;
+import io.netris.model.VPCListing;
+import io.netris.model.VPCResponseOK;
 import io.netris.model.response.AuthResponse;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -93,4 +96,15 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             throw new CloudRuntimeException(e);
         }
     }
+
+    @Override
+    public List<VPCListing> listVPCs() {
+        VpcApi api = new VpcApi(apiClient);
+        try {
+            VPCResponseOK response = api.apiV2VpcGet();
+            return response.getData();
+        } catch (ApiException e) {
+            throw new CloudRuntimeException(e);
+        }
+    }
 }
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
index b8324dad4b0..dcdcafd18cb 100644
--- a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.service;
 
 import io.netris.model.GetSiteBody;
+import io.netris.model.VPCListing;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -40,4 +41,10 @@ public class NetrisApiClientImplTest {
         List<GetSiteBody> sites = client.listSites();
         Assert.assertTrue(sites.size() > 0);
     }
+
+    @Test
+    public void testListVpcs() {
+        List<VPCListing> vpcs = client.listVPCs();
+        Assert.assertTrue(vpcs.size() > 0);
+    }
 }


(cloudstack) 04/44: Support adding netris provider to CloudStack and Netris VPC Creation (#6)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit bfb017426d5426d1c5a44972722db78d1ae9b964
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Tue Oct 22 10:31:05 2024 -0400

    Support adding netris provider to CloudStack and Netris VPC Creation (#6)
    
    * Support adding netris provider to CloudStack
    
    * revert marvin change
    
    * add license and perform session check when provider is added
    
    * add license and remove unused import
    
    * fix build failure - uunused imports
    
    * address comments
    
    * fix provider name
    
    * add Netris network element
    
    * add license
    
    * Add netris management APIs and netris service provider
    
    * add license
    
    * revert change
    
    * remove other network elements from Netris element
    
    * fix api name in doc generator
    
    * remove logs
    
    * move session alive check to CheckHealthCommand exec
    
    * Fix zone creation wizard to configure netris provider
    
    * Upgrade GSON version - from PR 8756
    
    * Add additional parametes to the add Netris provider API
    
    * add netris as a host
    
    * add additional params to the resoponse and update UI
    
    * Rename site to site_name
    
    * Create Netris VPC (#8)
    
    * Delegate API classes creation to the SDK and simply invoke the desired API class through CloudStack (#7)
    
    * Delegate API classes creation to the SDK and simply invoke the desired API class through CloudStack
    
    * Pass default auth scheme for now
    
    * Drop for_nsx and for_tungten columns in favour of checking the provider on the ntwserviceprovider map table
    
    * Remove missing setForTungsten occurrence
    
    * Remove forNsx from VPC offerings
    
    * Create Netris VPC
    
    * Fix VPC offerings listing and remove unused dao
    
    * Create VPC fixes
    
    * Upgrade GSON version - from PR 8756
    
    * Fix VPC creation response by using the latest SDK code
    
    * Fix unit test
    
    * Remove unused import
    
    * Fix NSX unit tests after refactoring
    
    * Add Netris key to the VLAN Details table (#10)
    
    * Add Netris key to the VLAN Details table
    
    * update for_<provider> column to be generic
    
    * Fix VPC and add IPAM allocation for the VPC CIDR (#9)
    
    * Fix VPC and add IPAM allocation for the VPC CIDR
    
    * Remove VPC logic
    
    * Use zoneId accountId and domainId on resources creation
    
    * Fix naming
    
    * Fix VR public nic issue
    
    * Fix Netris Public IP for VPC source NAT allocation
    
    * Add Netris VPC Subnets and vNets (#11)
    
    * Add Netris VPC Subnets and vNets
    
    * fix compilation errors
    
    * Add netris subnet
    
    * refactor naming convention to differentiate between VPC tiers and Isolated networks
    
    * revert marvin change
    
    * fix constructor - build failure
    
    * Add support to filter netris offerings, delete netris provider when zone is being deleted
    
    * Fix build
    
    * Fix VPC creation
    
    * Fix vnet creation
    
    * unnecesary log
    
    ---------
    
    Co-authored-by: nvazquez <ni...@gmail.com>
    
    ---------
    
    Co-authored-by: Pearl Dsilva <pe...@gmail.com>
    
    ---------
    
    Co-authored-by: nvazquez <ni...@gmail.com>
---
 .../cloud/configuration/ConfigurationService.java  |  16 +
 api/src/main/java/com/cloud/network/Networks.java  |   3 +-
 .../com/cloud/network/netris/NetrisProvider.java   |   2 +
 .../{NetrisProvider.java => NetrisService.java}    |  13 +-
 .../java/com/cloud/network/vpc/VpcOffering.java    |   2 -
 .../java/com/cloud/offering/NetworkOffering.java   |   4 -
 .../org/apache/cloudstack/api/ApiConstants.java    |   5 +
 .../command/admin/vlan/CreateVlanIpRangeCmd.java   |  13 +-
 .../cloudstack/api/response/AsyncJobResponse.java  |   9 -
 .../response/ClusterDrsPlanMigrationResponse.java  |  12 +-
 .../cloudstack/api/response/NetworkResponse.java   |  16 -
 .../cloudstack/api/response/SystemVmResponse.java  |   8 -
 .../api/response/VlanIpRangeResponse.java          |  10 +-
 .../cloudstack/api/response/ZoneResponse.java      |  13 +
 client/pom.xml                                     |   5 +
 .../cloud/agent/transport/ArrayTypeAdaptor.java    |  12 +-
 .../cloud/configuration/ConfigurationManager.java  |   2 +-
 .../engine/orchestration/NetworkOrchestrator.java  |  23 +-
 .../com/cloud/network/dao/NetrisProviderDao.java   |  14 +-
 .../cloud/network/dao/NetrisProviderDaoImpl.java   |  52 +++
 .../cloud/network/element/NetrisProviderVO.java    | 121 +++++++
 .../java/com/cloud/network/vpc/VpcOfferingVO.java  |  11 -
 .../network/vpc/dao/VpcOfferingServiceMapDao.java  |   3 +
 .../vpc/dao/VpcOfferingServiceMapDaoImpl.java      |   9 +
 .../com/cloud/offerings/NetworkOfferingVO.java     |  24 --
 .../spring-engine-schema-core-daos-context.xml     |   1 +
 .../resources/META-INF/db/schema-41910to42000.sql  |  13 +-
 .../db/views/cloud.network_offering_view.sql       |   2 -
 .../META-INF/db/views/cloud.vpc_offering_view.sql  |   1 -
 .../cluster/KubernetesClusterManagerImpl.java      |   1 -
 .../cloudstack/metrics/MetricsServiceImpl.java     |   2 +-
 .../cloudstack/response/HostMetricsResponse.java   |  10 -
 .../apache/cloudstack/StartupNetrisCommand.java    |  16 +-
 .../agent/api/CreateNetrisVnetCommand.java         |  52 +++
 .../api/CreateNetrisVpcCommand.java}               |  22 +-
 .../api/DeleteNetrisVpcCommand.java}               |  21 +-
 .../api/NetrisAnswer.java}                         |  17 +-
 .../apache/cloudstack/agent/api/NetrisCommand.java |  67 ++++
 .../api/command/AddNetrisProviderCmd.java          |  90 +++++-
 .../api/command/DeleteNetrisProviderCmd.java}      |  45 ++-
 .../api/command/ListNetrisProvidersCmd.java        |  73 +++++
 .../api/response/NetrisProviderResponse.java       |  38 ++-
 .../apache/cloudstack/resource/NetrisResource.java | 244 ++++++++++++++
 .../resource/NetrisResourceObjectUtils.java        |  65 ++++
 .../apache/cloudstack/service/NetrisApiClient.java |  19 ++
 .../cloudstack/service/NetrisApiClientImpl.java    | 328 ++++++++++++++++++-
 .../apache/cloudstack/service/NetrisElement.java   | 350 +++++++++++++++++++++
 .../service/NetrisGuestNetworkGuru.java}           | 126 ++------
 .../cloudstack/service/NetrisProviderService.java  |   7 +
 .../service/NetrisProviderServiceImpl.java         | 171 +++++++++-
 .../service/NetrisPublicNetworkGuru.java}          |  63 +---
 .../cloudstack/service/NetrisServiceImpl.java      |  97 ++++++
 .../spring-netris-core-managers-context.xml}       |   3 +-
 .../cloudstack/netris/spring-netris-context.xml    |  11 +
 .../resource/NetrisResourceObjectUtilsTest.java    |  57 ++++
 .../service/NetrisApiClientImplTest.java           |   4 +-
 .../api/command/DeleteNsxControllerCmd.java        |   3 +-
 .../cloudstack/service/NsxGuestNetworkGuru.java    |   9 -
 .../cloudstack/service/NsxPublicNetworkGuru.java   |  18 +-
 .../service/NsxPublicNetworkGuruTest.java          |  10 +-
 .../command/ConfigTungstenFabricServiceCmd.java    |   1 -
 plugins/pom.xml                                    |   1 +
 pom.xml                                            |   2 +-
 .../main/java/com/cloud/api/ApiResponseHelper.java |  17 +-
 .../cloud/api/query/dao/DataCenterJoinDaoImpl.java |  23 +-
 .../api/query/dao/VpcOfferingJoinDaoImpl.java      |   9 +-
 .../cloud/api/query/vo/NetworkOfferingJoinVO.java  |  20 --
 .../com/cloud/api/query/vo/VpcOfferingJoinVO.java  |   8 -
 .../cloud/api/response/ApiResponseSerializer.java  |   4 +-
 .../configuration/ConfigurationManagerImpl.java    |  55 +++-
 .../deploy/DeploymentPlanningManagerImpl.java      |   2 +-
 .../java/com/cloud/network/NetworkModelImpl.java   |   8 +-
 .../java/com/cloud/network/NetworkServiceImpl.java |  35 ++-
 .../com/cloud/network/guru/GuestNetworkGuru.java   |   9 +
 .../com/cloud/network/guru/PublicNetworkGuru.java  |  14 +-
 .../cloud/network/router/CommandSetupHelper.java   |   9 +-
 .../java/com/cloud/network/vpc/VpcManagerImpl.java |  80 ++---
 .../com/cloud/server/ConfigurationServerImpl.java  |   3 -
 .../deployment/RouterDeploymentDefinition.java     |  19 +-
 .../RouterDeploymentDefinitionBuilder.java         |   4 +
 .../deployment/VpcRouterDeploymentDefinition.java  |   6 +-
 .../com/cloud/network/NetworkModelImplTest.java    |   8 +-
 .../cloud/vpc/MockConfigurationManagerImpl.java    |   2 +-
 .../vpc/dao/MockVpcOfferingServiceMapDaoImpl.java  |   6 +
 .../src/test/resources/createNetworkOffering.xml   |   1 +
 tools/apidoc/gen_toc.py                            |   3 +
 ui/public/locales/en.json                          |   8 +
 ui/src/config/section/infra/phynetworks.js         |   2 +-
 ui/src/views/infra/network/ServiceProvidersTab.vue |  45 ++-
 ui/src/views/infra/network/TrafficTypesTab.vue     |   2 +-
 ui/src/views/infra/zone/PhysicalNetworksTab.vue    |   2 +-
 ui/src/views/infra/zone/ZoneWizardLaunchZone.vue   |  37 ++-
 .../infra/zone/ZoneWizardNetworkSetupStep.vue      |  20 +-
 ui/src/views/network/VpcTiersTab.vue               |   6 +-
 .../test/java/com/cloud/utils/LogUtilsTest.java    |   2 +-
 95 files changed, 2403 insertions(+), 528 deletions(-)

diff --git a/api/src/main/java/com/cloud/configuration/ConfigurationService.java b/api/src/main/java/com/cloud/configuration/ConfigurationService.java
index 97d4b42974b..5a4dcb3f569 100644
--- a/api/src/main/java/com/cloud/configuration/ConfigurationService.java
+++ b/api/src/main/java/com/cloud/configuration/ConfigurationService.java
@@ -17,7 +17,11 @@
 package com.cloud.configuration;
 
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
+import com.cloud.network.Network;
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd;
 import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
 import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd;
@@ -372,4 +376,16 @@ public interface ConfigurationService {
     List<? extends PortableIp> listPortableIps(long id);
 
     Boolean isAccountAllowedToCreateOfferingsWithTags(IsAccountAllowedToCreateOfferingsWithTagsCmd cmd);
+
+    public static final Map<String, String> ProviderDetailKeyMap = Map.of(
+            Network.Provider.Nsx.getName(), ApiConstants.NSX_DETAIL_KEY,
+            Network.Provider.Netris.getName(), ApiConstants.NETRIS_DETAIL_KEY
+    );
+
+    public static boolean IsIpRangeForProvider(Network.Provider provider) {
+        if (Objects.isNull(provider)) {
+            return false;
+        }
+        return ProviderDetailKeyMap.containsKey(provider.getName());
+    }
 }
diff --git a/api/src/main/java/com/cloud/network/Networks.java b/api/src/main/java/com/cloud/network/Networks.java
index dfa0ddb84ca..9f06a044111 100644
--- a/api/src/main/java/com/cloud/network/Networks.java
+++ b/api/src/main/java/com/cloud/network/Networks.java
@@ -129,7 +129,8 @@ public class Networks {
         UnDecided(null, null),
         OpenDaylight("opendaylight", String.class),
         TUNGSTEN("tf", String.class),
-        NSX("nsx", String.class);
+        NSX("nsx", String.class),
+        Netris("netris", String.class);
 
         private final String scheme;
         private final Class<?> type;
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
index b7892484bb9..82dc5a271a7 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
@@ -25,4 +25,6 @@ public interface NetrisProvider extends InternalIdentity, Identity {
     String getHostname();
     String getPort();
     String getUsername();
+    String getSiteName();
+    String getTenantName();
 }
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
similarity index 64%
copy from api/src/main/java/com/cloud/network/netris/NetrisProvider.java
copy to api/src/main/java/com/cloud/network/netris/NetrisService.java
index b7892484bb9..e11fb640632 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -16,13 +16,10 @@
 // under the License.
 package com.cloud.network.netris;
 
-import org.apache.cloudstack.api.Identity;
-import org.apache.cloudstack.api.InternalIdentity;
+import com.cloud.network.vpc.Vpc;
 
-public interface NetrisProvider extends InternalIdentity, Identity {
-    long getZoneId();
-    String getName();
-    String getHostname();
-    String getPort();
-    String getUsername();
+public interface NetrisService {
+    boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
+    boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
+    boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
 }
diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
index 1358b13a4fe..77d52991130 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
@@ -57,8 +57,6 @@ public interface VpcOffering extends InternalIdentity, Identity {
      */
     boolean isDefault();
 
-    boolean isForNsx();
-
     NetworkOffering.NetworkMode getNetworkMode();
 
     /**
diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java
index e70e8949ad7..4df01bd8986 100644
--- a/api/src/main/java/com/cloud/offering/NetworkOffering.java
+++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java
@@ -103,10 +103,6 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity,
 
     boolean isForVpc();
 
-    boolean isForTungsten();
-
-    boolean isForNsx();
-
     NetworkMode getNetworkMode();
 
     TrafficType getTrafficType();
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index bb16b0ff90d..b0d01e89c7d 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -325,6 +325,8 @@ public class ApiConstants {
     public static final String MIN_CPU_NUMBER = "mincpunumber";
     public static final String MIN_MEMORY = "minmemory";
     public static final String MIGRATION_TYPE = "migrationtype";
+    public static final String MIGRATION_JOB_ID = "migrationjobid";
+    public static final String MIGRATION_JOB_STATUS = "migrationjobstatus";
     public static final String MIGRATIONS = "migrations";
     public static final String MEMORY = "memory";
     public static final String MODE = "mode";
@@ -448,6 +450,7 @@ public class ApiConstants {
     public static final String SIGNATURE = "signature";
     public static final String SIGNATURE_VERSION = "signatureversion";
     public static final String SINCE = "since";
+    public static final String SITE_NAME = "sitename";
     public static final String SIZE = "size";
     public static final String SIZEGB = "sizegb";
     public static final String SNAPSHOT = "snapshot";
@@ -490,6 +493,7 @@ public class ApiConstants {
     public static final String TIMEOUT = "timeout";
     public static final String TIMEZONE = "timezone";
     public static final String TIMEZONEOFFSET = "timezoneoffset";
+    public static final String TENANT_NAME = "tenantname";
     public static final String TOTAL = "total";
     public static final String TOTAL_SUBNETS = "totalsubnets";
     public static final String TYPE = "type";
@@ -1143,6 +1147,7 @@ public class ApiConstants {
     public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid";
     public static final String HAS_RULES = "hasrules";
     public static final String NSX_DETAIL_KEY = "forNsx";
+    public static final String NETRIS_DETAIL_KEY = "forNetris";
     public static final String DISK_PATH = "diskpath";
     public static final String IMPORT_SOURCE = "importsource";
     public static final String TEMP_PATH = "temppath";
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java
index c0ba99a8233..87a4bd07a68 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java
@@ -16,6 +16,8 @@
 // under the License.
 package org.apache.cloudstack.api.command.admin.vlan;
 
+import com.cloud.configuration.ConfigurationService;
+import com.cloud.network.Network;
 import com.cloud.utils.net.NetUtils;
 
 import org.apache.cloudstack.api.APICommand;
@@ -39,7 +41,6 @@ import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.user.Account;
 
-import java.util.Objects;
 
 @APICommand(name = "createVlanIpRange", description = "Creates a VLAN IP range.", responseObject = VlanIpRangeResponse.class,
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -114,8 +115,8 @@ public class CreateVlanIpRangeCmd extends BaseCmd {
     @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if IP range is set to system vms, false if not")
     private Boolean forSystemVms;
 
-    @Parameter(name = ApiConstants.FOR_NSX, type = CommandType.BOOLEAN, description = "true if the IP range is used for NSX resource", since = "4.20.0")
-    private boolean forNsx;
+    @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "Provider name for which the IP range is reserved for", since = "4.20.0")
+    private String provider;
 
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
@@ -157,12 +158,12 @@ public class CreateVlanIpRangeCmd extends BaseCmd {
         return startIp;
     }
 
-    public boolean isForNsx() {
-        return !Objects.isNull(forNsx) && forNsx;
+    public Network.Provider getProvider() {
+        return Network.Provider.getProvider(provider);
     }
 
     public String getVlan() {
-        if ((vlan == null || vlan.isEmpty()) && !isForNsx()) {
+        if ((vlan == null || vlan.isEmpty()) && !ConfigurationService.IsIpRangeForProvider(getProvider())) {
             vlan = "untagged";
         }
         return vlan;
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java
index 3eeaaef2afa..e1db5aa6824 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java
@@ -55,10 +55,6 @@ public class AsyncJobResponse extends BaseResponse {
     @Param(description = "the async command executed")
     private String cmd;
 
-    @SerializedName("jobstatus")
-    @Param(description = "the current job status-should be 0 for PENDING")
-    private Integer jobStatus;
-
     @SerializedName("jobprocstatus")
     @Param(description = "the progress information of the PENDING job")
     private Integer jobProcStatus;
@@ -119,11 +115,6 @@ public class AsyncJobResponse extends BaseResponse {
         this.cmd = cmd;
     }
 
-    @Override
-    public void setJobStatus(Integer jobStatus) {
-        this.jobStatus = jobStatus;
-    }
-
     public void setJobProcStatus(Integer jobProcStatus) {
         this.jobProcStatus = jobProcStatus;
     }
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java
index 4114c228e26..f399c1260f7 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java
@@ -50,13 +50,13 @@ public class ClusterDrsPlanMigrationResponse extends BaseResponse {
     @Param(description = "Destination host for VM migration")
     String destHostName;
 
-    @SerializedName(ApiConstants.JOB_ID)
+    @SerializedName(ApiConstants.MIGRATION_JOB_ID)
     @Param(description = "id of VM migration async job")
-    private Long jobId;
+    private Long migrationJobId;
 
-    @SerializedName(ApiConstants.JOB_STATUS)
+    @SerializedName(ApiConstants.MIGRATION_JOB_STATUS)
     @Param(description = "Job status of VM migration async job")
-    private JobInfo.Status jobStatus;
+    private JobInfo.Status migrationJobStatus;
 
 
     public ClusterDrsPlanMigrationResponse(String vmId, String vmName, String srcHostId, String srcHostName,
@@ -68,8 +68,8 @@ public class ClusterDrsPlanMigrationResponse extends BaseResponse {
         this.srcHostName = srcHostName;
         this.destHostId = destHostId;
         this.destHostName = destHostName;
-        this.jobId = jobId;
-        this.jobStatus = jobStatus;
+        this.migrationJobId = jobId;
+        this.migrationJobStatus = jobStatus;
         this.setObjectName(ApiConstants.MIGRATIONS);
     }
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
index a80317c83cd..e7ad244e1da 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
@@ -208,14 +208,6 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement
     @Param(description = "Name of the VPC to which this network belongs", since = "4.15")
     private String vpcName;
 
-    @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID)
-    @Param(description = "the ID of the Network associated with this network")
-    private String associatedNetworkId;
-
-    @SerializedName(ApiConstants.ASSOCIATED_NETWORK)
-    @Param(description = "the name of the Network associated with this network")
-    private String associatedNetworkName;
-
     @SerializedName(ApiConstants.TUNGSTEN_VIRTUAL_ROUTER_UUID)
     @Param(description = "Tungsten-Fabric virtual router the network belongs to")
     private String tungstenVirtualRouterUuid;
@@ -612,14 +604,6 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement
         this.vpcName = vpcName;
     }
 
-    public void setAssociatedNetworkId(String associatedNetworkId) {
-        this.associatedNetworkId = associatedNetworkId;
-    }
-
-    public void setAssociatedNetworkName(String associatedNetworkName) {
-        this.associatedNetworkName = associatedNetworkName;
-    }
-
     @Override
     public void setResourceIconResponse(ResourceIconResponse icon) {
         this.icon = icon;
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java
index 31a8b731491..c98235b2347 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java
@@ -38,14 +38,6 @@ public class SystemVmResponse extends BaseResponseWithAnnotations {
     @Param(description = "the system VM type")
     private String systemVmType;
 
-    @SerializedName("jobid")
-    @Param(description = "the job ID associated with the system VM. This is only displayed if the router listed is part of a currently running asynchronous job.")
-    private String jobId;
-
-    @SerializedName("jobstatus")
-    @Param(description = "the job status associated with the system VM.  This is only displayed if the router listed is part of a currently running asynchronous job.")
-    private Integer jobStatus;
-
     @SerializedName("zoneid")
     @Param(description = "the Zone ID for the system VM")
     private String zoneId;
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
index 1492c23e882..2ee748f0c59 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
@@ -127,9 +127,9 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit
     @Param(description = "indicates whether VLAN IP range is dedicated to system vms or not")
     private Boolean forSystemVms;
 
-    @SerializedName(ApiConstants.FOR_NSX)
-    @Param(description = "indicates whether IP range is dedicated to NSX resources or not")
-    private Boolean forNsx;
+    @SerializedName(ApiConstants.PROVIDER)
+    @Param(description = "indicates whether IP range is dedicated to NSX resources or not", since = "4.20.0")
+    private String provider;
 
     public void setId(String id) {
         this.id = id;
@@ -249,7 +249,7 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit
         this.ip6Cidr = ip6Cidr;
     }
 
-    public void setForNsx(Boolean forNsx) {
-        this.forNsx = forNsx;
+    public void setProvider(String provider) {
+        this.provider = provider;
     }
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
index 143dfad0eaf..475c8b9eef2 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
@@ -145,10 +145,15 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
     @Param(description = "the type of the zone - core or edge", since = "4.18.0")
     String type;
 
+    @Deprecated(since = "4.20")
     @SerializedName(ApiConstants.NSX_ENABLED)
     @Param(description = "true, if zone is NSX enabled", since = "4.20.0")
     private boolean nsxEnabled = false;
 
+    @SerializedName(ApiConstants.PROVIDER)
+    @Param(description = "External network provider if any", since = "4.20.0")
+    private String provider = null;
+
     @SerializedName(ApiConstants.MULTI_ARCH)
     @Param(description = "true, if zone contains clusters and hosts from different CPU architectures", since = "4.20")
     private boolean multiArch;
@@ -368,6 +373,14 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
         return nsxEnabled;
     }
 
+    public String getProvider() {
+        return provider;
+    }
+
+    public void setProvider(String provider) {
+        this.provider = provider;
+    }
+
     @Override
     public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) {
         this.resourceIconResponse = resourceIconResponse;
diff --git a/client/pom.xml b/client/pom.xml
index d89dbbbbe9d..dd9432b82da 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -1097,6 +1097,11 @@
                     <artifactId>cloud-plugin-network-nsx</artifactId>
                     <version>${project.version}</version>
                 </dependency>
+                <dependency>
+                    <groupId>org.apache.cloudstack</groupId>
+                    <artifactId>cloud-plugin-network-netris</artifactId>
+                    <version>${project.version}</version>
+                </dependency>
                 <dependency>
                     <groupId>org.apache.cloudstack</groupId>
                     <artifactId>cloud-plugin-network-tungsten</artifactId>
diff --git a/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java b/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java
index 7229f0903b7..eabcc82040d 100644
--- a/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java
+++ b/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java
@@ -75,13 +75,17 @@ public class ArrayTypeAdaptor<T> implements JsonDeserializer<T[]>, JsonSerialize
             try {
                 clazz = Class.forName(name);
             } catch (ClassNotFoundException e) {
-                throw new CloudRuntimeException("can't find " + name);
+                throw new JsonParseException("can't find " + name);
             }
             T cmd = (T)_gson.fromJson(entry.getValue(), clazz);
             cmds.add(cmd);
         }
-        Class<?> type = ((Class<?>)typeOfT).getComponentType();
-        T[] ts = (T[])Array.newInstance(type, cmds.size());
-        return cmds.toArray(ts);
+        try {
+            Class<?> type =  Class.forName(typeOfT.getTypeName().replace("[]", ""));
+            T[] ts = (T[])Array.newInstance(type, cmds.size());
+            return cmds.toArray(ts);
+        } catch (ClassNotFoundException e) {
+            throw new CloudRuntimeException("can't find " + typeOfT.getTypeName());
+        }
     }
 }
diff --git a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
index 01fd54430d6..330fada48c3 100644
--- a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
+++ b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
@@ -227,7 +227,7 @@ public interface ConfigurationManager {
                                             NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber);
 
     Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, boolean forSystemVms, Long podId, String startIP, String endIP,
-        String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr, boolean forNsx)
+        String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr, Provider provider)
         throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException;
 
     void createDefaultSystemNetworks(long zoneId) throws ConcurrentOperationException;
diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index dfa4d480b94..1d6a9ee6946 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -43,6 +43,7 @@ import com.cloud.bgp.BGPService;
 import com.cloud.dc.VlanDetailsVO;
 import com.cloud.dc.dao.ASNumberDao;
 import com.cloud.dc.dao.VlanDetailsDao;
+import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NsxProviderDao;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.annotation.AnnotationService;
@@ -355,6 +356,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
     @Inject
     private NsxProviderDao nsxProviderDao;
     @Inject
+    private NetrisProviderDao netrisProviderDao;
+    @Inject
     private ASNumberDao asNumberDao;
     @Inject
     private BGPService bgpService;
@@ -1082,10 +1085,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                 return null;
             }
 
-            if (isNicAllocatedForNsxPublicNetworkOnVR(network, profile, vm)) {
+            if (isNicAllocatedForProviderPublicNetworkOnVR(network, profile, vm, Provider.Nsx)) {
                 String guruName = "NsxPublicNetworkGuru";
                 NetworkGuru nsxGuru = AdapterBase.getAdapterByName(networkGurus, guruName);
                 nsxGuru.allocate(network, profile, vm);
+            } else if (isNicAllocatedForProviderPublicNetworkOnVR(network, profile, vm, Provider.Netris)) {
+                String guruName = "NetrisPublicNetworkGuru";
+                NetworkGuru nsxGuru = AdapterBase.getAdapterByName(networkGurus, guruName);
+                nsxGuru.allocate(network, profile, vm);
             }
 
             if (isDefaultNic != null) {
@@ -1148,7 +1155,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
         return new Pair<NicProfile, Integer>(vmNic, Integer.valueOf(deviceId));
     }
 
-    private boolean isNicAllocatedForNsxPublicNetworkOnVR(Network network, NicProfile requested, VirtualMachineProfile vm) {
+    private boolean isNicAllocatedForProviderPublicNetworkOnVR(Network network, NicProfile requested, VirtualMachineProfile vm, Provider provider) {
         if (ObjectUtils.anyNull(network, requested, vm)) {
             return false;
         }
@@ -1158,7 +1165,9 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
             return false;
         }
         long dataCenterId = vm.getVirtualMachine().getDataCenterId();
-        if (nsxProviderDao.findByZoneId(dataCenterId) == null) {
+        if (Provider.Nsx == provider && nsxProviderDao.findByZoneId(dataCenterId) == null) {
+            return false;
+        } else if (Provider.Netris == provider && netrisProviderDao.findByZoneId(dataCenterId) == null) {
             return false;
         }
 
@@ -1168,14 +1177,16 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
         if (CollectionUtils.isEmpty(ips)) {
             return false;
         }
+
         ips = ips.stream().filter(x -> !x.getAddress().addr().equals(requested.getIPv4Address())).collect(Collectors.toList());
         IPAddressVO ip = ips.get(0);
-        VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(ip.getVlanId(), ApiConstants.NSX_DETAIL_KEY);
+        String detailKey = Provider.Nsx == provider ? ApiConstants.NSX_DETAIL_KEY : ApiConstants.NETRIS_DETAIL_KEY;
+        VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(ip.getVlanId(), detailKey);
         if (vlanDetail == null) {
             return false;
         }
-        boolean isForNsx = vlanDetail.getValue().equalsIgnoreCase("true");
-        return isForNsx && !ip.isForSystemVms();
+        boolean isForProvider = vlanDetail.getValue().equalsIgnoreCase("true");
+        return isForProvider && !ip.isForSystemVms();
     }
 
     private void setMtuDetailsInVRNic(final Pair<NetworkVO, VpcVO> networks, Network network, NicVO vo) {
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java
similarity index 71%
copy from api/src/main/java/com/cloud/network/netris/NetrisProvider.java
copy to engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java
index b7892484bb9..fe21f72e4db 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java
@@ -14,15 +14,11 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.network.netris;
+package com.cloud.network.dao;
 
-import org.apache.cloudstack.api.Identity;
-import org.apache.cloudstack.api.InternalIdentity;
+import com.cloud.network.element.NetrisProviderVO;
+import com.cloud.utils.db.GenericDao;
 
-public interface NetrisProvider extends InternalIdentity, Identity {
-    long getZoneId();
-    String getName();
-    String getHostname();
-    String getPort();
-    String getUsername();
+public interface NetrisProviderDao extends GenericDao<NetrisProviderVO, Long> {
+    NetrisProviderVO findByZoneId(long zoneId);
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java
new file mode 100644
index 00000000000..5eba3f60cbf
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java
@@ -0,0 +1,52 @@
+// 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 com.cloud.network.dao;
+
+import com.cloud.network.element.NetrisProviderVO;
+import com.cloud.utils.db.DB;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import org.springframework.stereotype.Component;
+
+@Component
+@DB()
+public class NetrisProviderDaoImpl extends GenericDaoBase<NetrisProviderVO, Long> implements NetrisProviderDao {
+
+    final SearchBuilder<NetrisProviderVO> allFieldsSearch;
+
+    public NetrisProviderDaoImpl() {
+        super();
+        allFieldsSearch = createSearchBuilder();
+        allFieldsSearch.and("id", allFieldsSearch.entity().getId(),
+                SearchCriteria.Op.EQ);
+        allFieldsSearch.and("uuid", allFieldsSearch.entity().getUuid(),
+                SearchCriteria.Op.EQ);
+        allFieldsSearch.and("hostname", allFieldsSearch.entity().getHostname(),
+                SearchCriteria.Op.EQ);
+        allFieldsSearch.and("zone_id", allFieldsSearch.entity().getZoneId(),
+                SearchCriteria.Op.EQ);
+        allFieldsSearch.done();
+    }
+
+    @Override
+    public NetrisProviderVO findByZoneId(long zoneId) {
+        SearchCriteria<NetrisProviderVO> sc = allFieldsSearch.create();
+        sc.setParameters("zone_id", zoneId);
+        return findOneBy(sc);
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java
index 3a759bceb11..2df1faae077 100644
--- a/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java
@@ -24,6 +24,7 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.Table;
+import java.util.Date;
 import java.util.UUID;
 
 @Entity
@@ -59,6 +60,18 @@ public class NetrisProviderVO implements NetrisProvider {
     @Column(name = "password")
     private String password;
 
+    @Column(name = "site_name")
+    private String siteName;
+
+    @Column(name = "tenant_name")
+    private String tenantName;
+
+    @Column(name = "created")
+    private Date created;
+
+    @Column(name = "removed")
+    private Date removed;
+
     public NetrisProviderVO() {
         this.uuid = UUID.randomUUID().toString();
     }
@@ -141,4 +154,112 @@ public class NetrisProviderVO implements NetrisProvider {
     public void setPassword(String password) {
         this.password = password;
     }
+
+    public String getSiteName() {
+        return siteName;
+    }
+
+    public void setSiteName(String siteName) {
+        this.siteName = siteName;
+    }
+
+    public String getTenantName() {
+        return tenantName;
+    }
+
+    public void setTenantName(String tenantName) {
+        this.tenantName = tenantName;
+    }
+
+    public Date getCreated() {
+        return created;
+    }
+
+    public void setCreated(Date created) {
+        this.created = created;
+    }
+
+    public Date getRemoved() {
+        return removed;
+    }
+
+    public void setRemoved(Date removed) {
+        this.removed = removed;
+    }
+
+    public static final class Builder {
+        private long zoneId;
+        private long hostId;
+        private String name;
+        private String hostname;
+        private String port;
+        private String username;
+        private String password;
+        private String siteName;
+        private String tenantName;
+
+        public Builder() {
+            // Default constructor
+        }
+
+        public Builder setZoneId(long zoneId) {
+            this.zoneId = zoneId;
+            return this;
+        }
+
+        public Builder setHostId(long hostId) {
+            this.hostId = hostId;
+            return this;
+        }
+
+        public Builder setName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public Builder setHostname(String hostname) {
+            this.hostname = hostname;
+            return this;
+        }
+
+        public Builder setPort(String port) {
+            this.port = port;
+            return this;
+        }
+
+        public Builder setUsername(String username) {
+            this.username = username;
+            return this;
+        }
+
+        public Builder setPassword(String password) {
+            this.password = password;
+            return this;
+        }
+
+        public Builder setSiteName(String siteName) {
+            this.siteName = siteName;
+            return this;
+        }
+
+        public Builder setTenantName(String tenantName) {
+            this.tenantName = tenantName;
+            return this;
+        }
+        public NetrisProviderVO build() {
+            NetrisProviderVO provider = new NetrisProviderVO();
+            provider.setZoneId(this.zoneId);
+            provider.setHostId(this.hostId);
+            provider.setUuid(UUID.randomUUID().toString());
+            provider.setName(this.name);
+            provider.setHostname(this.hostname);
+            provider.setPort(this.port);
+            provider.setUsername(this.username);
+            provider.setPassword(this.password);
+            provider.setSiteName(this.siteName);
+            provider.setTenantName(this.tenantName);
+            provider.setCreated(new Date());
+            return provider;
+        }
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java
index 41254ba4a8b..7945642c1d6 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java
@@ -59,9 +59,6 @@ public class VpcOfferingVO implements VpcOffering {
     @Column(name = "default")
     boolean isDefault = false;
 
-    @Column(name = "for_nsx")
-    boolean forNsx = false;
-
     @Column(name = "network_mode")
     NetworkOffering.NetworkMode networkMode;
 
@@ -158,14 +155,6 @@ public class VpcOfferingVO implements VpcOffering {
         return isDefault;
     }
 
-    public boolean isForNsx() {
-        return forNsx;
-    }
-
-    public void setForNsx(boolean forNsx) {
-        this.forNsx = forNsx;
-    }
-
     public NetworkOffering.NetworkMode getNetworkMode() {
         return networkMode;
     }
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java
index 06cfd25e670..9519cd08a0b 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java
@@ -18,6 +18,7 @@ package com.cloud.network.vpc.dao;
 
 import java.util.List;
 
+import com.cloud.network.Network;
 import com.cloud.network.Network.Service;
 import com.cloud.network.vpc.VpcOfferingServiceMapVO;
 import com.cloud.utils.db.GenericDao;
@@ -37,4 +38,6 @@ public interface VpcOfferingServiceMapDao extends GenericDao<VpcOfferingServiceM
 
     VpcOfferingServiceMapVO findByServiceProviderAndOfferingId(String service, String provider, long vpcOfferingId);
 
+    boolean isProviderForVpcOffering(Network.Provider provider, long vpcOfferingId);
+
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java
index c7400f6edfd..672955a3ecc 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java
@@ -19,6 +19,7 @@ package com.cloud.network.vpc.dao;
 import java.util.List;
 
 
+import com.cloud.network.Network;
 import org.springframework.stereotype.Component;
 
 import com.cloud.network.Network.Service;
@@ -110,4 +111,12 @@ public class VpcOfferingServiceMapDaoImpl extends GenericDaoBase<VpcOfferingServ
 
         return findOneBy(sc);
     }
+
+    @Override
+    public boolean isProviderForVpcOffering(Network.Provider provider, long vpcOfferingId) {
+        SearchCriteria<VpcOfferingServiceMapVO> sc = AllFieldsSearch.create();
+        sc.setParameters("vpcOffId", vpcOfferingId);
+        sc.setParameters("provider", provider.getName());
+        return findOneBy(sc) != null;
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java
index 0bf110757d7..cf766551421 100644
--- a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java
+++ b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java
@@ -133,12 +133,6 @@ public class NetworkOfferingVO implements NetworkOffering {
     @Column(name = "for_vpc")
     boolean forVpc;
 
-    @Column(name = "for_tungsten")
-    boolean forTungsten = false;
-
-    @Column(name = "for_nsx")
-    boolean forNsx = false;
-
     @Column(name = "network_mode")
     NetworkMode networkMode;
 
@@ -199,24 +193,6 @@ public class NetworkOfferingVO implements NetworkOffering {
         this.forVpc = isForVpc;
     }
 
-    @Override
-    public boolean isForTungsten() {
-        return forTungsten;
-    }
-
-    public void setForTungsten(boolean forTungsten) {
-        this.forTungsten = forTungsten;
-    }
-
-    @Override
-    public boolean isForNsx() {
-        return forNsx;
-    }
-
-    public void setForNsx(boolean forNsx) {
-        this.forNsx = forNsx;
-    }
-
     @Override
     public NetworkMode getNetworkMode() {
         return networkMode;
diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index 171685ce413..9a0bd416aca 100644
--- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -137,6 +137,7 @@
   <bean id="objectInDataStoreDaoImpl" class="org.apache.cloudstack.storage.db.ObjectInDataStoreDaoImpl" />
   <bean id="ovsProviderDaoImpl" class="com.cloud.network.dao.OvsProviderDaoImpl" />
   <bean id="nsxControllerDaoImpl" class="com.cloud.network.dao.NsxProviderDaoImpl" />
+  <bean id="netrisProviderDaoImpl" class="com.cloud.network.dao.NetrisProviderDaoImpl" />
   <bean id="tungstenControllerDaoImpl" class="com.cloud.network.dao.TungstenProviderDaoImpl"/>
   <bean id="physicalNetworkDaoImpl" class="com.cloud.network.dao.PhysicalNetworkDaoImpl" />
   <bean id="physicalNetworkIsolationMethodDaoImpl" class="com.cloud.network.dao.PhysicalNetworkIsolationMethodDaoImpl" />
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
index 7a1a3678973..9873c355baa 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
@@ -437,9 +437,18 @@ CREATE TABLE `cloud`.`netris_providers` (
     `port` varchar(255),
     `username` varchar(255) NOT NULL,
     `password` varchar(255) NOT NULL,
-    `created` datetime NOT NULL COMMENT 'date created',
-    `removed` datetime COMMENT 'date removed if not null',
+    `site_name` varchar(255) NOT NULL,
+    `tenant_name` varchar(255) NOT NULL,
+    `created` datetime NOT NULL COMMENT 'created date',
+    `removed` datetime COMMENT 'removed date if not null',
     PRIMARY KEY (`id`),
     CONSTRAINT `fk_netris_providers__zone_id` FOREIGN KEY `fk_netris_providers__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE,
     INDEX `i_netris_providers__zone_id`(`zone_id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- Drop the Tungsten and NSX columns from the network offerings (replaced by checking the provider on the ntwk_offering_service_map table)
+ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_tungsten`;
+ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_nsx`;
+
+-- Drop the Tungsten and NSX columns from the VPC offerings (replaced by checking the provider on the vpc_offering_service_map table)
+ALTER TABLE `cloud`.`vpc_offerings` DROP COLUMN `for_nsx`;
diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql
index b6abaabcd48..4fb12ce0388 100644
--- a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql
+++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql
@@ -59,8 +59,6 @@ SELECT
     `network_offerings`.`supports_public_access` AS `supports_public_access`,
     `network_offerings`.`supports_vm_autoscaling` AS `supports_vm_autoscaling`,
     `network_offerings`.`for_vpc` AS `for_vpc`,
-    `network_offerings`.`for_tungsten` AS `for_tungsten`,
-    `network_offerings`.`for_nsx` AS `for_nsx`,
     `network_offerings`.`network_mode` AS `network_mode`,
     `network_offerings`.`service_package_id` AS `service_package_id`,
     `network_offerings`.`routing_mode` AS `routing_mode`,
diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql
index c74d50590de..751d8f91a25 100644
--- a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql
+++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql
@@ -28,7 +28,6 @@ select
     `vpc_offerings`.`display_text` AS `display_text`,
     `vpc_offerings`.`state` AS `state`,
     `vpc_offerings`.`default` AS `default`,
-    `vpc_offerings`.`for_nsx` AS `for_nsx`,
     `vpc_offerings`.`network_mode` AS `network_mode`,
     `vpc_offerings`.`created` AS `created`,
     `vpc_offerings`.`removed` AS `removed`,
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
index 71be8b1a475..b5afc13dadd 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
@@ -2008,7 +2008,6 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
                         forVpc, true, false, false);
         if (forNsx) {
             defaultKubernetesServiceNetworkOffering.setNetworkMode(NetworkOffering.NetworkMode.NATTED);
-            defaultKubernetesServiceNetworkOffering.setForNsx(true);
         }
         defaultKubernetesServiceNetworkOffering.setSupportsVmAutoScaling(true);
         defaultKubernetesServiceNetworkOffering.setState(NetworkOffering.State.Enabled);
diff --git a/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java b/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java
index 6025a41d69c..b6cdbccf69c 100644
--- a/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java
+++ b/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java
@@ -712,7 +712,7 @@ public class MetricsServiceImpl extends MutualExclusiveIdsManagerBase implements
             metricsResponse.setCpuTotal(hostResponse.getCpuNumber(), hostResponse.getCpuSpeed());
             metricsResponse.setCpuUsed(hostResponse.getCpuUsed(), hostResponse.getCpuNumber(), hostResponse.getCpuSpeed());
             metricsResponse.setCpuAllocated(hostResponse.getCpuAllocated(), hostResponse.getCpuNumber(), hostResponse.getCpuSpeed());
-            metricsResponse.setLoadAverage(hostResponse.getAverageLoad());
+            metricsResponse.setCpuAverageLoad(hostResponse.getAverageLoad());
             metricsResponse.setMemTotal(hostResponse.getMemoryTotal());
             metricsResponse.setMemAllocated(hostResponse.getMemoryAllocated());
             metricsResponse.setMemUsed(hostResponse.getMemoryUsed());
diff --git a/plugins/metrics/src/main/java/org/apache/cloudstack/response/HostMetricsResponse.java b/plugins/metrics/src/main/java/org/apache/cloudstack/response/HostMetricsResponse.java
index 8e12d2cbb6d..39d759f333f 100644
--- a/plugins/metrics/src/main/java/org/apache/cloudstack/response/HostMetricsResponse.java
+++ b/plugins/metrics/src/main/java/org/apache/cloudstack/response/HostMetricsResponse.java
@@ -52,10 +52,6 @@ public class HostMetricsResponse extends HostResponse {
     @Param(description = "the total cpu allocated in Ghz")
     private String cpuAllocated;
 
-    @SerializedName("cpuloadaverage")
-    @Param(description = "the average cpu load the last minute")
-    private Double loadAverage;
-
     @SerializedName("memorytotalgb")
     @Param(description = "the total memory capacity in GiB")
     private String memTotal;
@@ -132,12 +128,6 @@ public class HostMetricsResponse extends HostResponse {
         }
     }
 
-    public void setLoadAverage(final Double loadAverage) {
-        if (loadAverage != null) {
-            this.loadAverage = loadAverage;
-        }
-    }
-
     public void setCpuAllocated(final String cpuAllocated, final Integer cpuNumber, final Long cpuSpeed) {
         if (cpuAllocated != null && cpuNumber != null && cpuSpeed != null) {
             this.cpuAllocated = String.format("%.2f Ghz", parseCPU(cpuAllocated) * cpuNumber * cpuSpeed / (100.0 * 1000.0));
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/StartupNetrisCommand.java
similarity index 71%
copy from api/src/main/java/com/cloud/network/netris/NetrisProvider.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/StartupNetrisCommand.java
index b7892484bb9..04be93067a6 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/StartupNetrisCommand.java
@@ -14,15 +14,13 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.network.netris;
+package org.apache.cloudstack;
 
-import org.apache.cloudstack.api.Identity;
-import org.apache.cloudstack.api.InternalIdentity;
+import com.cloud.agent.api.StartupCommand;
+import com.cloud.host.Host;
 
-public interface NetrisProvider extends InternalIdentity, Identity {
-    long getZoneId();
-    String getName();
-    String getHostname();
-    String getPort();
-    String getUsername();
+public class StartupNetrisCommand extends StartupCommand {
+    public StartupNetrisCommand() {
+        super(Host.Type.L2Networking);
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
new file mode 100644
index 00000000000..ed02a09b42c
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
@@ -0,0 +1,52 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class CreateNetrisVnetCommand extends NetrisCommand {
+    private String vpcName;
+    private Long vpcId;
+    private String cidr;
+    private Integer vxlanId;
+
+    public CreateNetrisVnetCommand(Long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, String cidr, boolean isVpc) {
+        super(zoneId, accountId, domainId, vNetName, networkId, isVpc);
+        this.vpcId = vpcId;
+        this.vpcName = vpcName;
+        this.cidr = cidr;
+    }
+
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public String getVpcName() {
+        return vpcName;
+    }
+
+    public String getCidr() {
+        return cidr;
+    }
+
+    public Integer getVxlanId() {
+        return vxlanId;
+    }
+
+    public void setVxlanId(Integer vxlanId) {
+        this.vxlanId = vxlanId;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVpcCommand.java
similarity index 65%
copy from plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVpcCommand.java
index 1085b780a18..cff25f78287 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVpcCommand.java
@@ -14,18 +14,18 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package org.apache.cloudstack.service;
+package org.apache.cloudstack.agent.api;
 
-import io.netris.ApiException;
-import io.netris.model.GetSiteBody;
-import io.netris.model.VPCListing;
-import io.netris.model.response.TenantResponse;
+public class CreateNetrisVpcCommand extends NetrisCommand {
 
-import java.util.List;
+    private final String cidr;
 
-public interface NetrisApiClient {
-    boolean isSessionAlive();
-    List<GetSiteBody> listSites();
-    List<VPCListing> listVPCs();
-    List<TenantResponse> listTenants() throws ApiException;
+    public CreateNetrisVpcCommand(long zoneId, long accountId, long domainId, String name, String cidr, long vpcId, boolean isVpc) {
+        super(zoneId, accountId, domainId, name, vpcId, isVpc);
+        this.cidr = cidr;
+    }
+
+    public String getCidr() {
+        return cidr;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisVpcCommand.java
similarity index 65%
copy from plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisVpcCommand.java
index 1085b780a18..d560f0b8cd9 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisVpcCommand.java
@@ -14,18 +14,17 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package org.apache.cloudstack.service;
+package org.apache.cloudstack.agent.api;
 
-import io.netris.ApiException;
-import io.netris.model.GetSiteBody;
-import io.netris.model.VPCListing;
-import io.netris.model.response.TenantResponse;
+public class DeleteNetrisVpcCommand extends NetrisCommand {
+    private final String cidr;
 
-import java.util.List;
+    public DeleteNetrisVpcCommand(long zoneId, long accountId, long domainId, String name, String cidr, long vpcId, boolean isVpc) {
+        super(zoneId, accountId, domainId, name, vpcId, isVpc);
+        this.cidr = cidr;
+    }
 
-public interface NetrisApiClient {
-    boolean isSessionAlive();
-    List<GetSiteBody> listSites();
-    List<VPCListing> listVPCs();
-    List<TenantResponse> listTenants() throws ApiException;
+    public String getCidr() {
+        return cidr;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderService.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisAnswer.java
similarity index 66%
copy from plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderService.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisAnswer.java
index a7c60859791..197500a3cce 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderService.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisAnswer.java
@@ -14,12 +14,17 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package org.apache.cloudstack.service;
+package org.apache.cloudstack.agent.api;
 
-import com.cloud.network.netris.NetrisProvider;
-import com.cloud.utils.component.PluggableService;
-import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
 
-public interface NetrisProviderService extends PluggableService {
-    NetrisProvider addProvider(AddNetrisProviderCmd cmd);
+public class NetrisAnswer extends Answer {
+    public NetrisAnswer(final Command command, final boolean success, final String details) {
+        super(command, success, details);
+    }
+
+    public NetrisAnswer(final Command command, final Exception e) {
+        super(command, e);
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
new file mode 100644
index 00000000000..440892e6e87
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+import com.cloud.agent.api.Command;
+
+public class NetrisCommand extends Command {
+    private final long zoneId;
+    private final long accountId;
+    private final long domainId;
+    private final String name;
+    private final long id;
+    private final boolean isVpc;
+
+    public NetrisCommand(long zoneId, long accountId, long domainId, String name, long id, boolean isVpc) {
+        this.zoneId = zoneId;
+        this.accountId = accountId;
+        this.domainId = domainId;
+        this.name = name;
+        this.id = id;
+        this.isVpc = isVpc;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public long getZoneId() {
+        return zoneId;
+    }
+
+    public long getAccountId() {
+        return accountId;
+    }
+
+    public long getDomainId() {
+        return domainId;
+    }
+
+    public boolean isVpc() {
+        return isVpc;
+    }
+
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/AddNetrisProviderCmd.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/AddNetrisProviderCmd.java
index f7068793b80..d8e49e776ae 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/AddNetrisProviderCmd.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/AddNetrisProviderCmd.java
@@ -17,25 +17,103 @@
 package org.apache.cloudstack.api.command;
 
 import com.cloud.exception.ConcurrentOperationException;
-import com.cloud.exception.InsufficientCapacityException;
-import com.cloud.exception.NetworkRuleConflictException;
-import com.cloud.exception.ResourceAllocationException;
-import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.netris.NetrisProvider;
 import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.response.NetrisProviderResponse;
+import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.service.NetrisProviderService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
 
 @APICommand(name = AddNetrisProviderCmd.APINAME, description = "Add Netris Provider to CloudStack",
         responseObject = NetrisProviderResponse.class, requestHasSensitiveInfo = false,
         responseHasSensitiveInfo = false, since = "4.20")
 public class AddNetrisProviderCmd extends BaseCmd {
     public static final String APINAME = "addNetrisProvider";
+    public static final Logger LOGGER = LoggerFactory.getLogger(AddNetrisProviderCmd.class.getName());
 
-    @Override
-    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+    @Inject
+    NetrisProviderService netrisProviderService;
+
+    @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true,
+            description = "the ID of zone")
+    private Long zoneId;
+
+    @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Netris provider name")
+    private String name;
+
+    @Parameter(name = ApiConstants.HOST_NAME, type = CommandType.STRING, required = true, description = "Netris provider hostname / IP address")
+    private String hostname;
+
+    @Parameter(name = ApiConstants.PORT, type = CommandType.STRING, description = "Netris provider port")
+    private String port;
+
+    @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Username to log into Netris")
+    private String username;
+
+    @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "Password to login into Netris")
+    private String password;
+
+    @Parameter(name = ApiConstants.SITE_NAME, type = CommandType.STRING, required = true, description = "Netris Site name")
+    private String siteName;
+
+    @Parameter(name = ApiConstants.TENANT_NAME, type = CommandType.STRING, required = true, description = "Password to login into Netris")
+    private String tenantName;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getZoneId() {
+        return zoneId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public String getPort() {
+        return port;
+    }
+
+    public String getUsername() {
+        return username;
+    }
 
+    public String getPassword() {
+        return password;
+    }
+
+    public String getSiteName() {
+        return siteName;
+    }
+
+    public String getTenantName() {
+        return tenantName;
+    }
+
+    @Override
+    public void execute() throws ServerApiException, ConcurrentOperationException {
+        NetrisProvider provider = netrisProviderService.addProvider(this);
+        NetrisProviderResponse response = netrisProviderService.createNetrisProviderResponse(provider);
+        if (response == null)
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add Netris provider");
+        else {
+            response.setResponseName(getCommandName());
+            setResponseObject(response);
+        }
     }
 
     @Override
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/DeleteNetrisProviderCmd.java
similarity index 70%
copy from plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/DeleteNetrisProviderCmd.java
index 5a3e5586400..27e29071c45 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/DeleteNetrisProviderCmd.java
@@ -16,62 +16,59 @@
 // under the License.
 package org.apache.cloudstack.api.command;
 
-import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.cloudstack.api.APICommand;
-import org.apache.cloudstack.api.Parameter;
-import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.ApiErrorCode;
-import org.apache.cloudstack.api.response.NsxControllerResponse;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.NetrisProviderResponse;
 import org.apache.cloudstack.api.response.SuccessResponse;
-import org.apache.cloudstack.service.NsxProviderService;
+import org.apache.cloudstack.service.NetrisProviderService;
 
 import javax.inject.Inject;
 
-import static org.apache.cloudstack.api.command.DeleteNsxControllerCmd.APINAME;
+import static org.apache.cloudstack.api.command.DeleteNetrisProviderCmd.APINAME;
 
-@APICommand(name = APINAME, description = "delete NSX Controller to CloudStack",
-        responseObject = NsxControllerResponse.class, requestHasSensitiveInfo = false,
-        responseHasSensitiveInfo = false, since = "4.19.0")
-public class DeleteNsxControllerCmd extends BaseCmd {
-    public static final String APINAME = "deleteNsxController";
+@APICommand(name = APINAME, description = "delete Netris Provider to CloudStack",
+        responseObject = NetrisProviderResponse.class, requestHasSensitiveInfo = false,
+        responseHasSensitiveInfo = false, since = "4.20.0")
+public class DeleteNetrisProviderCmd extends BaseCmd {
+    public static final String APINAME = "deleteNetrisProvider";
 
     @Inject
-    protected NsxProviderService nsxProviderService;
-/////////////////////////////////////////////////////
+    private NetrisProviderService netrisProviderService;
+
+    /////////////////////////////////////////////////////
     //////////////// API parameters /////////////////////
     /////////////////////////////////////////////////////
-
-    @Parameter(name = ApiConstants.NSX_CONTROLLER_ID, type = CommandType.UUID, entityType = NsxControllerResponse.class,
-            required = true, description = "NSX Controller ID")
-    private Long nsxControllerId;
+    @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetrisProviderResponse.class,
+            required = true, description = "Netris Provider ID")
+    private Long id;
 
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
 
-    public Long getNsxControllerId() {
-        return nsxControllerId;
-    }
+    public Long getId() { return id; }
 
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
 
-
     @Override
     public void execute() throws ServerApiException, ConcurrentOperationException {
         try {
-            boolean deleted = nsxProviderService.deleteNsxController(getNsxControllerId());
+            boolean deleted = netrisProviderService.deleteNetrisProvider(getId());
             if (deleted) {
                 SuccessResponse response = new SuccessResponse(getCommandName());
                 response.setResponseName(getCommandName());
                 setResponseObject(response);
             } else {
-                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove NSX Controller from Zone");
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to Netris provider from zone");
             }
         } catch (InvalidParameterValueException e) {
             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage());
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/ListNetrisProvidersCmd.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/ListNetrisProvidersCmd.java
new file mode 100644
index 00000000000..ed3d64e867a
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/ListNetrisProvidersCmd.java
@@ -0,0 +1,73 @@
+// 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.api.command;
+
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.utils.StringUtils;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.NetrisProviderResponse;
+import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.service.NetrisProviderService;
+
+import javax.inject.Inject;
+import java.util.List;
+
+@APICommand(name = ListNetrisProvidersCmd.APINAME, description = "list all Netris providers added to CloudStack",
+        responseObject = NetrisProviderResponse.class, requestHasSensitiveInfo = false,
+        responseHasSensitiveInfo = false, since = "4.20.0")
+public class ListNetrisProvidersCmd extends BaseListCmd {
+    public static final String APINAME = "listNetrisProviders";
+
+    @Inject
+    NetrisProviderService netrisProviderService;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+    ///
+    @Parameter(name = ApiConstants.ZONE_ID, description = "Netris provider added to the specific zone",
+            type = CommandType.UUID, entityType = ZoneResponse.class)
+    Long zoneId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getZoneId() {
+        return zoneId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ServerApiException, ConcurrentOperationException {
+        List<BaseResponse> baseResponseList = netrisProviderService.listNetrisProviders(zoneId);
+        List<BaseResponse> pagingList = StringUtils.applyPagination(baseResponseList, this.getStartIndex(), this.getPageSizeVal());
+        ListResponse<BaseResponse> listResponse = new ListResponse<>();
+        listResponse.setResponses(pagingList);
+        listResponse.setResponseName(getCommandName());
+        setResponseObject(listResponse);
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/response/NetrisProviderResponse.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/response/NetrisProviderResponse.java
index be8b72261bb..0a6030659f0 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/response/NetrisProviderResponse.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/response/NetrisProviderResponse.java
@@ -29,6 +29,10 @@ public class NetrisProviderResponse extends BaseResponse {
     @Param(description = "Netris Provider name")
     private String name;
 
+    @SerializedName(ApiConstants.UUID)
+    @Param(description = "Netris provider uuid")
+    private String uuid;
+
     @SerializedName(ApiConstants.ZONE_ID)
     @Param(description = "Zone ID to which the Netris Provider is associated with")
     private String zoneId;
@@ -45,9 +49,13 @@ public class NetrisProviderResponse extends BaseResponse {
     @Param(description = "Netris Provider port")
     private String port;
 
-    @SerializedName(ApiConstants.USERNAME)
-    @Param(description = "Netris Provider username")
-    private String username;
+    @SerializedName(ApiConstants.SITE_NAME)
+    @Param(description = "Netris Provider site")
+    private String siteName;
+
+    @SerializedName(ApiConstants.TENANT_NAME)
+    @Param(description = "Netris Admin tenant name")
+    private String tenantName;
 
     public String getName() {
         return name;
@@ -57,6 +65,14 @@ public class NetrisProviderResponse extends BaseResponse {
         this.name = name;
     }
 
+    public String getUuid() {
+        return uuid;
+    }
+
+    public void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+
     public String getZoneId() {
         return zoneId;
     }
@@ -89,11 +105,19 @@ public class NetrisProviderResponse extends BaseResponse {
         this.port = port;
     }
 
-    public String getUsername() {
-        return username;
+    public String getSiteName() {
+        return siteName;
+    }
+
+    public void setSiteName(String siteName) {
+        this.siteName = siteName;
+    }
+
+    public String getTenantName() {
+        return tenantName;
     }
 
-    public void setUsername(String username) {
-        this.username = username;
+    public void setTenantName(String tenantName) {
+        this.tenantName = tenantName;
     }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
new file mode 100644
index 00000000000..ac3f06e34f5
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -0,0 +1,244 @@
+// 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.resource;
+
+import com.cloud.agent.IAgentControl;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.CheckHealthAnswer;
+import com.cloud.agent.api.CheckHealthCommand;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.PingCommand;
+import com.cloud.agent.api.ReadyAnswer;
+import com.cloud.agent.api.ReadyCommand;
+import com.cloud.agent.api.StartupCommand;
+import com.cloud.host.Host;
+import com.cloud.resource.ServerResource;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.NetrisAnswer;
+import org.apache.cloudstack.StartupNetrisCommand;
+import org.apache.cloudstack.service.NetrisApiClient;
+import org.apache.cloudstack.service.NetrisApiClientImpl;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import javax.naming.ConfigurationException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class NetrisResource implements ServerResource {
+    protected Logger logger = LogManager.getLogger(getClass());
+
+    private String name;
+    protected String hostname;
+    protected String username;
+    protected String password;
+    protected String guid;
+    protected String zoneId;
+    protected String siteName;
+    protected String adminTenantName;
+
+    protected NetrisApiClient netrisApiClient;
+
+    @Override
+    public Host.Type getType() {
+        return Host.Type.Routing;
+    }
+
+    @Override
+    public StartupCommand[] initialize() {
+        StartupNetrisCommand sc = new StartupNetrisCommand();
+        sc.setGuid(guid);
+        sc.setName(name);
+        sc.setDataCenter(zoneId);
+        sc.setPod("");
+        sc.setPrivateIpAddress("");
+        sc.setStorageIpAddress("");
+        sc.setVersion("");
+        return new StartupCommand[] {sc};
+    }
+
+    @Override
+    public PingCommand getCurrentStatus(long id) {
+        return null;
+    }
+
+    @Override
+    public Answer executeRequest(Command cmd) {
+        if (cmd instanceof ReadyCommand) {
+            return executeRequest((ReadyCommand) cmd);
+        } else if (cmd instanceof CheckHealthCommand) {
+            return executeRequest((CheckHealthCommand) cmd);
+        } else if (cmd instanceof CreateNetrisVpcCommand) {
+            return executeRequest((CreateNetrisVpcCommand) cmd);
+        } else if (cmd instanceof DeleteNetrisVpcCommand) {
+            return executeRequest((DeleteNetrisVpcCommand) cmd);
+        } else if (cmd instanceof CreateNetrisVnetCommand) {
+            return executeRequest((CreateNetrisVnetCommand) cmd);
+        } else {
+            return Answer.createUnsupportedCommandAnswer(cmd);
+        }
+    }
+
+    @Override
+    public void disconnected() {
+        // Do nothing
+    }
+
+    @Override
+    public IAgentControl getAgentControl() {
+        return null;
+    }
+
+    @Override
+    public void setAgentControl(IAgentControl agentControl) {
+        // Do nothing
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public void setConfigParams(Map<String, Object> params) {
+        // Do nothing
+    }
+
+    @Override
+    public Map<String, Object> getConfigParams() {
+        return new HashMap<>();
+    }
+
+    @Override
+    public int getRunLevel() {
+        return 0;
+    }
+
+    @Override
+    public void setRunLevel(int level) {
+
+    }
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+        hostname = (String) params.get("hostname");
+        if (hostname == null) {
+            throw new ConfigurationException("Missing Netris hostname from params: " + params);
+        }
+
+        username = (String) params.get("username");
+        if (username == null) {
+            throw new ConfigurationException("Missing Netris username from params: " + params);
+        }
+
+        password = (String) params.get("password");
+        if (password == null) {
+            throw new ConfigurationException("Missing Netris password from params: " + params);
+        }
+
+        this.name = (String) params.get("name");
+        if (this.name == null) {
+            throw new ConfigurationException("Unable to find name");
+        }
+
+        guid = (String) params.get("guid");
+        if (guid == null) {
+            throw new ConfigurationException("Unable to find the guid");
+        }
+
+        zoneId = (String) params.get("zoneId");
+        if (zoneId == null) {
+            throw new ConfigurationException("Unable to find zone");
+        }
+
+        siteName = (String) params.get("siteName");
+        if (siteName == null) {
+            throw new ConfigurationException("Unable to find the Netris site name");
+        }
+
+        adminTenantName = (String) params.get("tenantName");
+        if (adminTenantName == null) {
+            throw new ConfigurationException("Unable to find the Netris admin tenant name");
+        }
+
+        netrisApiClient = new NetrisApiClientImpl(hostname, username, password, siteName, adminTenantName);
+        return netrisApiClient.isSessionAlive();
+    }
+
+    private Answer executeRequest(ReadyCommand cmd) {
+        return new ReadyAnswer(cmd);
+    }
+
+    private Answer executeRequest(CheckHealthCommand cmd) {
+        return new CheckHealthAnswer(cmd, netrisApiClient.isSessionAlive());
+    }
+
+    private Answer executeRequest(CreateNetrisVpcCommand cmd) {
+        try {
+            boolean result = netrisApiClient.createVpc(cmd);
+            if (!result) {
+                return new NetrisAnswer(cmd, false, String.format("Netris VPC %s creation failed", cmd.getName()));
+            }
+            return new NetrisAnswer(cmd, true, "OK");
+        } catch (CloudRuntimeException e) {
+            String msg = String.format("Error creating Netris VPC: %s", e.getMessage());
+            logger.error(msg, e);
+            return new NetrisAnswer(cmd, new CloudRuntimeException(msg));
+        }
+    }
+
+    private Answer executeRequest(CreateNetrisVnetCommand cmd) {
+        try {
+            String vpcName = cmd.getVpcName();
+            boolean result = netrisApiClient.createVnet(cmd);
+            if (!result) {
+                return new NetrisAnswer(cmd, false, String.format("Netris VPC %s creation failed", vpcName));
+            }
+            return new NetrisAnswer(cmd, true, "OK");
+        } catch (CloudRuntimeException e) {
+            String msg = String.format("Error creating Netris VPC: %s", e.getMessage());
+            logger.error(msg, e);
+            return new NetrisAnswer(cmd, new CloudRuntimeException(msg));
+        }
+    }
+
+    private Answer executeRequest(DeleteNetrisVpcCommand cmd) {
+        boolean result = netrisApiClient.deleteVpc(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Netris VPC %s deletion failed", cmd.getName()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
+    @Override
+    public boolean start() {
+        return true;
+    }
+
+    @Override
+    public boolean stop() {
+        return true;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
new file mode 100644
index 00000000000..8cfc5ea7918
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -0,0 +1,65 @@
+// 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.resource;
+
+import org.apache.cloudstack.agent.api.NetrisCommand;
+import org.apache.commons.lang3.ArrayUtils;
+
+public class NetrisResourceObjectUtils {
+
+    public enum NetrisObjectType {
+        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET
+    }
+
+    public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, NetrisObjectType netrisObjectType, String... suffixes) {
+        long zoneId = cmd.getZoneId();
+        long accountId = cmd.getAccountId();
+        long domainId = cmd.getDomainId();
+        long objectId = cmd.getId();
+        String objectName = cmd.getName();
+        boolean isVpc = cmd.isVpc();
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(String.format("D%s-A%s-Z%s", domainId, accountId, zoneId));
+        String prefix = isVpc ? "-V" : "-N";
+        switch (netrisObjectType) {
+            case VPC:
+                if (ArrayUtils.isEmpty(suffixes)) {
+                    stringBuilder.append(String.format("%s%s-%s", prefix, objectId, objectName));
+                } else {
+                    stringBuilder.append(String.format("%s%s", prefix, suffixes[0]));
+                    suffixes = new String[0];
+                }
+                break;
+            case IPAM_ALLOCATION:
+            case IPAM_SUBNET:
+                stringBuilder.append(String.format("%s%s", prefix, objectId));
+                break;
+            case VNET:
+               break;
+            default:
+                stringBuilder.append(String.format("-%s", objectName));
+                break;
+        }
+        if (ArrayUtils.isNotEmpty(suffixes)) {
+            for (String suffix : suffixes) {
+                stringBuilder.append(String.format("-%s", suffix));
+            }
+        }
+        return stringBuilder.toString();
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index 1085b780a18..f0317687b95 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -20,6 +20,9 @@ import io.netris.ApiException;
 import io.netris.model.GetSiteBody;
 import io.netris.model.VPCListing;
 import io.netris.model.response.TenantResponse;
+import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 
 import java.util.List;
 
@@ -28,4 +31,20 @@ public interface NetrisApiClient {
     List<GetSiteBody> listSites();
     List<VPCListing> listVPCs();
     List<TenantResponse> listTenants() throws ApiException;
+
+    /**
+     * Create a VPC on CloudStack creates the following Netris resources:
+     * - Create a Netris VPC with the VPC name
+     * - Create an IPAM Allocation for the created Netris VPC using the Prefix = VPC CIDR
+     */
+    boolean createVpc(CreateNetrisVpcCommand cmd);
+
+    /**
+     * Delete a VPC on CloudStack removes the following Netris resources:
+     * - Delete the IPAM Allocation for the VPC using the Prefix = VPC CIDR
+     * - Delete a Netris VPC with the VPC name
+     */
+    boolean deleteVpc(DeleteNetrisVpcCommand cmd);
+
+    boolean createVnet(CreateNetrisVnetCommand cmd);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index e2e4c9a90f1..5acaafefe17 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.service;
 
+import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
 import io.netris.ApiClient;
 import io.netris.ApiException;
@@ -23,18 +24,46 @@ import io.netris.ApiResponse;
 import io.netris.api.v1.AuthenticationApi;
 import io.netris.api.v1.SitesApi;
 import io.netris.api.v1.TenantsApi;
+import io.netris.api.v2.IpamApi;
+import io.netris.api.v2.VNetApi;
 import io.netris.api.v2.VpcApi;
+import io.netris.model.AllocationBody;
+import io.netris.model.AllocationBodyVpc;
 import io.netris.model.GetSiteBody;
+import io.netris.model.InlineResponse2004;
+import io.netris.model.IpTreeAllocationTenant;
+import io.netris.model.IpTreeSubnetSites;
 import io.netris.model.SitesResponseOK;
+import io.netris.model.SubnetBody;
+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.VnetResAddBody;
 import io.netris.model.response.AuthResponse;
 import io.netris.model.response.TenantResponse;
 import io.netris.model.response.TenantsResponse;
+import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class NetrisApiClientImpl implements NetrisApiClient {
 
@@ -42,17 +71,52 @@ public class NetrisApiClientImpl implements NetrisApiClient {
 
     private static ApiClient apiClient;
 
-    public NetrisApiClientImpl(String endpointBaseUrl, String username, String password) {
+    private final int siteId;
+    private final String siteName;
+    private 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);
+        logger.error(msg, e);
         throw new CloudRuntimeException(msg);
     }
 
@@ -103,4 +167,264 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
         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 InlineResponse2004 createVpcAllocationInternal(VPCResponseObjectOK createdVpc, String cidr, int adminTenantId,
+                                                           String adminTenantName, String netrisIpamAllocationName) {
+        logger.debug(String.format("Creating Netris VPC Allocation %s for VPC %s", cidr, createdVpc.getData().getName()));
+        try {
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            AllocationBody body = new AllocationBody();
+            AllocationBodyVpc allocationBodyVpc = new AllocationBodyVpc();
+            allocationBodyVpc.setId(createdVpc.getData().getId());
+            allocationBodyVpc.setName(createdVpc.getData().getName());
+            body.setVpc(allocationBodyVpc);
+            body.setName(netrisIpamAllocationName);
+            body.setPrefix(cidr);
+            IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
+            allocationTenant.setId(new BigDecimal(adminTenantId));
+            allocationTenant.setName(adminTenantName);
+            body.setTenant(allocationTenant);
+            return ipamApi.apiV2IpamAllocationPost(body);
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error creating Netris Allocation %s for VPC %s", cidr, createdVpc.getData().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();
+        InlineResponse2004 ipamResponse = createVpcAllocationInternal(createdVpc, vpcCidr, tenantId, tenantName, netrisIpamAllocationName);
+        if (ipamResponse == null || !ipamResponse.isIsSuccess()) {
+            String reason = ipamResponse == null ? "Empty response" : "Operation failed on Netris";
+            logger.debug("The Netris Allocation {} for VPC {} creation failed: {}", vpcCidr, cmd.getName(), reason);
+            return false;
+        }
+        logger.debug(String.format("Successfully created VPC %s and its IPAM Allocation %s on Netris", cmd.getName(), vpcCidr));
+        return true;
+    }
+
+    private void deleteVpcIpamAllocationInternal(VPCListing vpcResource, String vpcCidr) {
+        logger.debug(String.format("Deleting Netris VPC IPAM Allocation %s for VPC %s", vpcCidr, vpcResource.getName()));
+        try {
+            VpcApi vpcApi = apiClient.getApiStubForMethod(VpcApi.class);
+            VPCResponseResourceOK vpcResourcesResponse = vpcApi.apiV2VpcVpcIdResourcesGet(vpcResource.getId());
+            VPCResourceIpam vpcAllocationResource = getVpcAllocationResource(vpcResourcesResponse);
+            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", vpcCidr, vpcResource.getName()), e);
+        }
+    }
+
+    private VPCResourceIpam getVpcAllocationResource(VPCResponseResourceOK vpcResourcesResponse) {
+        VPCResource resource = vpcResourcesResponse.getData().get(0);
+        List<VPCResourceIpam> vpcAllocations = resource.getAllocation();
+        if (CollectionUtils.isNotEmpty(vpcAllocations)) {
+            if (vpcAllocations.size() > 1) {
+                logger.warn("Unexpected VPC allocations size {}, one expected", vpcAllocations.size());
+            }
+            return vpcAllocations.get(0);
+        }
+        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.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 vpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC);
+        VPCListing vpcResource = getVpcByNameAndTenant(vpcName);
+        if (vpcResource == null) {
+            logger.error(String.format("Could not find the Netris VPC resource with name %s and tenant ID %s", vpcName, tenantId));
+            return false;
+        }
+        String vpcCidr = cmd.getCidr();
+        deleteVpcIpamAllocationInternal(vpcResource, vpcCidr);
+        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();
+        boolean isVpc = cmd.isVpc();
+
+        String suffix = String.format("%s-%s", vpcId, vpcName);
+        String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
+        VPCListing associatedVpc = getVpcByNameAndTenant(netrisVpcName);
+        if (associatedVpc == null) {
+            logger.error(String.format("Failed to find Netris VPC with name: %s, to create the corresponding vNet for network %s", vpcName, networkName));
+            return false;
+        }
+
+        String vNetName;
+        if (isVpc) {
+            vNetName = String.format("V%s-N%s-%s", vpcId, networkId, networkName);
+        } else {
+            vNetName = String.format("N%s-%s", networkId, networkName);
+        }
+        String netrisVnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VNET, vNetName) ;
+        String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
+
+        InlineResponse2004 subnetResponse = createVpcSubnetInternal(associatedVpc, vNetName, vnetCidr, netrisSubnetName);
+        if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
+            String reason = subnetResponse == null ? "Empty response" : "Operation failed on Netris";
+            logger.debug("The Netris Subnet {} for VPC {} for network {} creation failed: {}", vnetCidr, vpcName, networkName, reason);
+            return false;
+        }
+        logger.debug("Successfully created VPC {} and its IPAM Subnet {} on Netris", vpcName, vnetCidr);
+
+        VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr);
+        if (vnetResponse == null || !vnetResponse.isIsSuccess()) {
+            String reason = vnetResponse == null ? "Empty response" : "Operation failed on Netris";
+            logger.debug("The Netris vNet creation {} for VPC {} failed: {}", vNetName, vpcName, reason);
+            return false;
+        }
+        return true;
+    }
+
+    private InlineResponse2004 createVpcSubnetInternal(VPCListing associatedVpc, String vNetName, String vNetCidr, String netrisSubnetName) {
+        logger.debug("Creating Netris VPC Subnet {} for VPC {} for vNet {}", vNetCidr, associatedVpc.getName(), vNetName);
+        try {
+
+            SubnetBody subnetBody = new SubnetBody();
+            subnetBody.setName(netrisSubnetName);
+
+            AllocationBodyVpc vpcAllocationBody = new AllocationBodyVpc();
+            vpcAllocationBody.setName(associatedVpc.getName());
+            vpcAllocationBody.setId(associatedVpc.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(SubnetBody.PurposeEnum.COMMON);
+            subnetBody.setPrefix(vNetCidr);
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            return ipamApi.apiV2IpamSubnetPost(subnetBody);
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error creating Netris Subnet %s for VPC %s", vNetCidr, associatedVpc.getName()), e);
+            return null;
+        }
+    }
+
+    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String vNetCidr) {
+        logger.debug("Creating Netris VPC vNet {} for CIDR {}", netrisVnetName, vNetCidr);
+        try {
+            VnetAddBody vnetBody = new VnetAddBody();
+
+            vnetBody.setCustomAnycastMac("");
+
+            VnetAddBodyGateways gateways = new VnetAddBodyGateways();
+            gateways.prefix(vNetCidr);
+            gateways.setDhcpEnabled(false);
+            VnetAddBodyDhcp dhcp = new VnetAddBodyDhcp();
+            dhcp.setEnd("");
+            dhcp.setStart("");
+            dhcp.setOptionSet(new VnetAddBodyDhcpOptionSet());
+            gateways.setDhcp(dhcp);
+            List<VnetAddBodyGateways> gatewaysList = new ArrayList<>();
+            gatewaysList.add(gateways);
+            vnetBody.setGateways(gatewaysList);
+
+            vnetBody.setGuestTenants(new ArrayList<>());
+            vnetBody.setL3vpn(false);
+            vnetBody.setName(netrisVnetName);
+            vnetBody.setNativeVlan(0);
+            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);
+
+            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;
+        }
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
new file mode 100644
index 00000000000..f15090dc3f4
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -0,0 +1,350 @@
+// 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.agent.AgentManager;
+import com.cloud.agent.Listener;
+import com.cloud.agent.api.AgentControlAnswer;
+import com.cloud.agent.api.AgentControlCommand;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.StartupCommand;
+import com.cloud.deploy.DeployDestination;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.ConnectionException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.host.Host;
+import com.cloud.host.HostVO;
+import com.cloud.host.Status;
+import com.cloud.network.IpAddress;
+import com.cloud.network.Network;
+import com.cloud.network.NetworkModel;
+import com.cloud.network.PhysicalNetworkServiceProvider;
+import com.cloud.network.element.DhcpServiceProvider;
+import com.cloud.network.element.DnsServiceProvider;
+import com.cloud.network.element.VirtualRouterElement;
+import com.cloud.network.element.VpcProvider;
+import com.cloud.network.netris.NetrisService;
+import com.cloud.network.rules.LoadBalancerContainer;
+import com.cloud.network.vpc.NetworkACLItem;
+import com.cloud.network.vpc.PrivateGateway;
+import com.cloud.network.vpc.StaticRouteProfile;
+import com.cloud.network.vpc.Vpc;
+import com.cloud.offering.NetworkOffering;
+import com.cloud.resource.ResourceManager;
+import com.cloud.resource.ResourceStateAdapter;
+import com.cloud.resource.ServerResource;
+import com.cloud.resource.UnableDeleteHostException;
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.vm.NicProfile;
+import com.cloud.vm.ReservationContext;
+import com.cloud.vm.VirtualMachineProfile;
+import org.apache.cloudstack.StartupNetrisCommand;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Component
+public class NetrisElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, ResourceStateAdapter,
+        Listener, VpcProvider {
+
+    @Inject
+    NetworkModel networkModel;
+    @Inject
+    AgentManager agentManager;
+    @Inject
+    ResourceManager resourceManager;
+    @Inject
+    private NetrisService netrisService;
+
+    protected Logger logger = LogManager.getLogger(getClass());
+
+    private final Map<Network.Service, Map<Network.Capability, String>> capabilities = initCapabilities();
+
+    private static Map<Network.Service, Map<Network.Capability, String>> initCapabilities() {
+        Map<Network.Service, Map<Network.Capability, String>> capabilities = new HashMap<>();
+
+        Map<Network.Capability, String> dhcpCapabilities = Map.of(Network.Capability.DhcpAccrossMultipleSubnets, "true");
+        capabilities.put(Network.Service.Dhcp, dhcpCapabilities);
+
+        Map<Network.Capability, String> dnsCapabilities = new HashMap<>();
+        dnsCapabilities.put(Network.Capability.AllowDnsSuffixModification, "true");
+        capabilities.put(Network.Service.Dns, dnsCapabilities);
+
+        capabilities.put(Network.Service.StaticNat, null);
+
+        // Set capabilities for LB service
+        Map<Network.Capability, String> lbCapabilities = new HashMap<Network.Capability, String>();
+        lbCapabilities.put(Network.Capability.SupportedLBAlgorithms, "roundrobin,leastconn");
+        lbCapabilities.put(Network.Capability.SupportedLBIsolation, "dedicated");
+        lbCapabilities.put(Network.Capability.SupportedProtocols, "tcp, udp");
+        lbCapabilities.put(Network.Capability.SupportedStickinessMethods, VirtualRouterElement.getHAProxyStickinessCapability());
+        lbCapabilities.put(Network.Capability.LbSchemes, String.join(",", LoadBalancerContainer.Scheme.Internal.name(), LoadBalancerContainer.Scheme.Public.name()));
+
+        capabilities.put(Network.Service.Lb, lbCapabilities);
+        capabilities.put(Network.Service.PortForwarding, null);
+        capabilities.put(Network.Service.NetworkACL, null);
+
+        Map<Network.Capability, String> firewallCapabilities = new HashMap<>();
+        firewallCapabilities.put(Network.Capability.SupportedProtocols, "tcp,udp,icmp");
+        firewallCapabilities.put(Network.Capability.SupportedEgressProtocols, "tcp,udp,icmp,all");
+        firewallCapabilities.put(Network.Capability.MultipleIps, "true");
+        firewallCapabilities.put(Network.Capability.TrafficStatistics, "per public ip");
+        firewallCapabilities.put(Network.Capability.SupportedTrafficDirection, "ingress, egress");
+        capabilities.put(Network.Service.Firewall, firewallCapabilities);
+
+        Map<Network.Capability, String> sourceNatCapabilities = new HashMap<>();
+        sourceNatCapabilities.put(Network.Capability.RedundantRouter, "true");
+        sourceNatCapabilities.put(Network.Capability.SupportedSourceNatTypes, "peraccount");
+        capabilities.put(Network.Service.SourceNat, sourceNatCapabilities);
+        return capabilities;
+    }
+
+    @Override
+    public boolean processAnswers(long agentId, long seq, Answer[] answers) {
+        return false;
+    }
+
+    @Override
+    public boolean processCommands(long agentId, long seq, Command[] commands) {
+        return false;
+    }
+
+    @Override
+    public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) {
+        return null;
+    }
+
+    @Override
+    public void processHostAdded(long hostId) {
+        // Do nothing
+    }
+
+    @Override
+    public void processConnect(Host host, StartupCommand cmd, boolean forRebalance) throws ConnectionException {
+        // Do nothing
+    }
+
+    @Override
+    public boolean processDisconnect(long agentId, Status state) {
+        return false;
+    }
+
+    @Override
+    public void processHostAboutToBeRemoved(long hostId) {
+        // Do nothing
+    }
+
+    @Override
+    public void processHostRemoved(long hostId, long clusterId) {
+        // Do nothing
+    }
+
+    @Override
+    public boolean isRecurring() {
+        return false;
+    }
+
+    @Override
+    public int getTimeout() {
+        return 0;
+    }
+
+    @Override
+    public boolean processTimeout(long agentId, long seq) {
+        return false;
+    }
+
+    protected boolean canHandle(Network network, Network.Service service) {
+        logger.debug("Checking if Netris Element can handle service " + service.getName() + " on network "
+                + network.getDisplayText());
+
+        if (!networkModel.isProviderForNetwork(getProvider(), network.getId())) {
+            logger.debug("Netris Element is not a provider for network " + network.getDisplayText());
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean addDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean configDhcpSupportForSubnet(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean removeDhcpSupportForSubnet(Network network) throws ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, String> dhcpOptions) {
+        return true;
+    }
+
+    @Override
+    public boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) throws ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean addDnsEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean configDnsSupportForSubnet(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean removeDnsSupportForSubnet(Network network) throws ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public Map<Network.Service, Map<Network.Capability, String>> getCapabilities() {
+        return capabilities;
+    }
+
+    @Override
+    public Network.Provider getProvider() {
+        return Network.Provider.Netris;
+    }
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+        agentManager.registerForHostEvents(this, true, true, true);
+        resourceManager.registerResourceStateAdapter(this.getClass().getSimpleName(), this);
+        return true;
+    }
+
+    @Override
+    public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
+        return true;
+    }
+
+    @Override
+    public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
+        return false;
+    }
+
+    @Override
+    public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
+        return false;
+    }
+
+    @Override
+    public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
+        return canHandle(network, Network.Service.Connectivity);
+    }
+
+    @Override
+    public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean isReady(PhysicalNetworkServiceProvider provider) {
+        return true;
+    }
+
+    @Override
+    public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
+        return false;
+    }
+
+    @Override
+    public boolean canEnableIndividualServices() {
+        return true;
+    }
+
+    @Override
+    public boolean verifyServicesCombination(Set<Network.Service> services) {
+        return true;
+    }
+
+    @Override
+    public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
+        return null;
+    }
+
+    @Override
+    public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map<String, String> details, List<String> hostTags) {
+        if (!(startup[0] instanceof StartupNetrisCommand)) {
+            return null;
+        }
+        host.setType(Host.Type.L2Networking);
+        return host;
+    }
+
+    @Override
+    public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException {
+        return null;
+    }
+
+    @Override
+    public boolean implementVpc(Vpc vpc, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
+        return true;
+    }
+
+    @Override
+    public boolean shutdownVpc(Vpc vpc, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException {
+        long zoneId = vpc.getZoneId();
+        long accountId = vpc.getAccountId();
+        long domainId = vpc.getDomainId();
+        return netrisService.deleteVpcResource(zoneId, accountId, domainId, vpc);
+    }
+
+    @Override
+    public boolean createPrivateGateway(PrivateGateway gateway) throws ConcurrentOperationException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean deletePrivateGateway(PrivateGateway privateGateway) throws ConcurrentOperationException, ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean applyStaticRoutes(Vpc vpc, List<StaticRouteProfile> routes) throws ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException {
+        return true;
+    }
+
+    @Override
+    public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
+        return true;
+    }
+}
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
similarity index 66%
copy from plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
index 032967d4061..95056c95460 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
@@ -16,71 +16,46 @@
 // under the License.
 package org.apache.cloudstack.service;
 
-import static java.util.Objects.isNull;
-import static java.util.Objects.nonNull;
-
 import com.cloud.dc.DataCenter;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.domain.DomainVO;
-import com.cloud.domain.dao.DomainDao;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.network.NetworkMigrationResponder;
-import com.cloud.network.NetworkModel;
-import com.cloud.network.NetworkProfile;
 import com.cloud.network.Network;
+import com.cloud.network.NetworkMigrationResponder;
 import com.cloud.network.Networks;
 import com.cloud.network.PhysicalNetwork;
-import com.cloud.network.PublicIpAddress;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.dao.PhysicalNetworkVO;
 import com.cloud.network.guru.GuestNetworkGuru;
+import com.cloud.network.netris.NetrisService;
 import com.cloud.network.vpc.VpcVO;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.offerings.NetworkOfferingVO;
-import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
 import com.cloud.user.Account;
-import com.cloud.user.dao.AccountDao;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.NicProfile;
 import com.cloud.vm.ReservationContext;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
-import org.apache.cloudstack.NsxAnswer;
-import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand;
-import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand;
-import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand;
-import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand;
-import org.apache.cloudstack.utils.NsxControllerUtils;
-
-import org.apache.cloudstack.utils.NsxHelper;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 
 import javax.inject.Inject;
-import java.util.List;
 import java.util.Objects;
 
-public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigrationResponder  {
-    protected Logger logger = LogManager.getLogger(getClass());
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+
+public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements NetworkMigrationResponder {
 
     @Inject
-    NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
-    @Inject
-    NsxControllerUtils nsxControllerUtils;
-    @Inject
-    AccountDao accountDao;
-    @Inject
-    DomainDao domainDao;
-    @Inject
-    NetworkModel networkModel;
+    private NetrisService netrisService;
 
-    public NsxGuestNetworkGuru() {
+    public NetrisGuestNetworkGuru() {
         super();
-        _isolationMethods = new PhysicalNetwork.IsolationMethod[] {new PhysicalNetwork.IsolationMethod("NSX")};
+        _isolationMethods = new PhysicalNetwork.IsolationMethod[] {new PhysicalNetwork.IsolationMethod("Netris")};
     }
 
     @Override
@@ -89,7 +64,7 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
         return networkType == DataCenter.NetworkType.Advanced && isMyTrafficType(offering.getTrafficType())
                 && isMyIsolationMethod(physicalNetwork) && (NetworkOffering.NetworkMode.ROUTED.equals(offering.getNetworkMode())
                 || (networkOfferingServiceMapDao.isProviderForNetworkOffering(
-                offering.getId(), Network.Provider.Nsx) && NetworkOffering.NetworkMode.NATTED.equals(offering.getNetworkMode())));
+                offering.getId(), Network.Provider.Netris) && NetworkOffering.NetworkMode.NATTED.equals(offering.getNetworkMode())));
     }
 
     @Override
@@ -106,7 +81,7 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
         if (network == null) {
             return null;
         }
-        network.setBroadcastDomainType(Networks.BroadcastDomainType.NSX);
+        network.setBroadcastDomainType(Networks.BroadcastDomainType.Netris);
 
         if (userSpecified != null) {
             if ((userSpecified.getIp6Cidr() == null && userSpecified.getIp6Gateway() != null) || (
@@ -120,7 +95,7 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
             }
         }
 
-        network.setBroadcastDomainType(Networks.BroadcastDomainType.NSX);
+        network.setBroadcastDomainType(Networks.BroadcastDomainType.Netris);
         network.setState(Network.State.Allocated);
 
         NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(),
@@ -143,7 +118,7 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
         if (name != null) {
             implemented.setName(name);
         }
-        implemented.setBroadcastUri(Networks.BroadcastDomainType.NSX.toUri("nsx"));
+        implemented.setBroadcastUri(Networks.BroadcastDomainType.Netris.toUri("netris"));
 
         return network;
     }
@@ -157,9 +132,9 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
             if (isNull(zone)) {
                 throw new CloudRuntimeException(String.format("Failed to find zone with id: %s", zoneId));
             }
-            createNsxSegment(designedNetwork, zone);
+            createNetrisVnet(designedNetwork, zone);
         } catch (Exception ex) {
-            throw new CloudRuntimeException("unable to create NSX network " + network.getUuid() + "due to: " + ex.getMessage());
+            throw new CloudRuntimeException("unable to create Netris network " + network.getUuid() + "due to: " + ex.getMessage());
         }
     }
 
@@ -192,7 +167,7 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
         if (network.getName() != null) {
             implemented.setName(network.getName());
         }
-        implemented.setBroadcastUri(Networks.BroadcastDomainType.NSX.toUri("nsx"));
+        implemented.setBroadcastUri(Networks.BroadcastDomainType.Netris.toUri("netris"));
         return implemented;
     }
 
@@ -232,59 +207,12 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
         NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(network.getNetworkOfferingId());
 
         if (isNull(network.getVpcId()) && networkOfferingVO.getNetworkMode().equals(NetworkOffering.NetworkMode.NATTED)) {
-            long domainId = domain.getId();
-            long accountId = account.getId();
-            long dataCenterId = zone.getId();
-            long resourceId = network.getId();
-            PublicIpAddress ipAddress = networkModel.getSourceNatIpAddressForGuestNetwork(account, network);
-            String translatedIp = ipAddress.getAddress().addr();
-            String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(domainId, accountId, dataCenterId, resourceId, false);
-            logger.debug(String.format("Creating NSX NAT Rule for Tier1 GW %s for translated IP %s for Isolated network %s", tier1GatewayName, translatedIp, network.getName()));
-            String natRuleId = NsxControllerUtils.getNsxNatRuleId(domainId, accountId, dataCenterId, resourceId, false);
-            CreateOrUpdateNsxTier1NatRuleCommand cmd = NsxHelper.createOrUpdateNsxNatRuleCommand(domainId, accountId, dataCenterId, tier1GatewayName, "SNAT", translatedIp, natRuleId);
-            NsxAnswer nsxAnswer = nsxControllerUtils.sendNsxCommand(cmd, dataCenterId);
-            if (!nsxAnswer.getResult()) {
-                String msg = String.format("Could not create NSX NAT Rule on Tier1 Gateway %s for IP %s  for Isolated network %s", tier1GatewayName, translatedIp, network.getName());
-                logger.error(msg);
-                throw new CloudRuntimeException(msg);
-            }
+            // Netris Natted mode
         }
 
-        // Create the DHCP relay config for the segment
-        String iPv4Address = nicProfile.getIPv4Address();
-        List<String> addresses = List.of(iPv4Address);
-        CreateNsxDhcpRelayConfigCommand command = NsxHelper.createNsxDhcpRelayConfigCommand(domain, account, zone, vpc, network, addresses);
-        NsxAnswer answer = nsxControllerUtils.sendNsxCommand(command, zone.getId());
-        if (!answer.getResult()) {
-            String msg = String.format("Error creating DHCP relay config for network %s and nic %s: %s", network.getName(), nic.getName(), answer.getDetails());
-            logger.error(msg);
-            throw new CloudRuntimeException(msg);
-        }
         return nicProfile;
     }
 
-    @Override
-    public void reserve(final NicProfile nic, final Network network, final VirtualMachineProfile vm,
-                        final DeployDestination dest, final ReservationContext context)
-            throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
-        // Do nothing
-    }
-
-    @Override
-    public boolean release(final NicProfile nic, final VirtualMachineProfile vm, final String reservationId) {
-        return true;
-    }
-
-    @Override
-    public void shutdown(final NetworkProfile profile, final NetworkOffering offering) {
-        // Do nothing
-    }
-
-    @Override
-    public boolean trash(Network network, NetworkOffering offering) {
-        return true;
-    }
-
     @Override
     public boolean prepareMigration(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) {
         return false;
@@ -300,7 +228,7 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
         // Do nothing
     }
 
-    public void createNsxSegment(NetworkVO networkVO, DataCenter zone) {
+    public void createNetrisVnet(NetworkVO networkVO, DataCenter zone) {
         Account account = accountDao.findById(networkVO.getAccountId());
         if (isNull(account)) {
             throw new CloudRuntimeException(String.format("Unable to find account with id: %s", networkVO.getAccountId()));
@@ -312,31 +240,31 @@ public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigr
             throw new CloudRuntimeException(msg);
         }
         String vpcName = null;
+        Long vpcId = null;
         if (nonNull(networkVO.getVpcId())) {
             VpcVO vpc = _vpcDao.findById(networkVO.getVpcId());
             if (isNull(vpc)) {
                 throw new CloudRuntimeException(String.format("Failed to find VPC network with id: %s", networkVO.getVpcId()));
             }
             vpcName = vpc.getName();
+            vpcId = vpc.getId();
         } else {
             logger.debug(String.format("Creating a Tier 1 Gateway for the network %s before creating the NSX segment", networkVO.getName()));
             long networkOfferingId = networkVO.getNetworkOfferingId();
             NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(networkOfferingId);
             boolean isSourceNatSupported = !NetworkOffering.NetworkMode.ROUTED.equals(networkOfferingVO.getNetworkMode()) &&
                     networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(networkVO.getNetworkOfferingId(), Network.Service.SourceNat);
-            CreateNsxTier1GatewayCommand nsxTier1GatewayCommand =  new CreateNsxTier1GatewayCommand(domain.getId(), account.getId(), zone.getId(), networkVO.getId(), networkVO.getName(), false, isSourceNatSupported);
-
-            NsxAnswer nsxAnswer = nsxControllerUtils.sendNsxCommand(nsxTier1GatewayCommand, zone.getId());
-            if (!nsxAnswer.getResult()) {
-                String msg = String.format("Could not create a Tier 1 Gateway for network %s: %s", networkVO.getName(), nsxAnswer.getDetails());
+            boolean result = netrisService.createVpcResource(zone.getId(), networkVO.getAccountId(), networkVO.getDomainId(),
+                    networkVO.getId(), networkVO.getName(), isSourceNatSupported, networkVO.getCidr(), false);
+            if (!result) {
+                String msg = String.format("Error creating Netris VPC for the network: %s", networkVO.getName());
                 logger.error(msg);
                 throw new CloudRuntimeException(msg);
             }
         }
-        CreateNsxSegmentCommand command = NsxHelper.createNsxSegmentCommand(domain, account, zone, vpcName, networkVO);
-        NsxAnswer answer = nsxControllerUtils.sendNsxCommand(command, zone.getId());
-        if (!answer.getResult()) {
-            throw new CloudRuntimeException("can not create NSX network");
+        boolean result = netrisService.createVnetResource(zone.getId(), account.getId(), domain.getId(), vpcName, vpcId, networkVO.getName(), networkVO.getId(), networkVO.getCidr());
+        if (!result) {
+            throw new CloudRuntimeException("Failed to create Netris vNet resource for network: " + networkVO.getName());
         }
     }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderService.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderService.java
index a7c60859791..7d5f4146ab1 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderService.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderService.java
@@ -18,8 +18,15 @@ package org.apache.cloudstack.service;
 
 import com.cloud.network.netris.NetrisProvider;
 import com.cloud.utils.component.PluggableService;
+import org.apache.cloudstack.api.BaseResponse;
 import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
+import org.apache.cloudstack.api.response.NetrisProviderResponse;
+
+import java.util.List;
 
 public interface NetrisProviderService extends PluggableService {
     NetrisProvider addProvider(AddNetrisProviderCmd cmd);
+    List<BaseResponse> listNetrisProviders(Long zoneId);
+    boolean deleteNetrisProvider(Long providerId);
+    NetrisProviderResponse createNetrisProviderResponse(NetrisProvider provider);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
index 5ebb0673a9c..ef4cd265d8c 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
@@ -16,17 +16,184 @@
 // under the License.
 package org.apache.cloudstack.service;
 
+import com.amazonaws.util.CollectionUtils;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.host.DetailVO;
+import com.cloud.host.Host;
+import com.cloud.host.dao.HostDetailsDao;
+import com.cloud.network.Network;
+import com.cloud.network.Networks;
+import com.cloud.network.dao.NetrisProviderDao;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
+import com.cloud.network.dao.PhysicalNetworkDao;
+import com.cloud.network.dao.PhysicalNetworkVO;
+import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.netris.NetrisProvider;
+import com.cloud.resource.ResourceManager;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.cloudstack.api.BaseResponse;
 import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
+import org.apache.cloudstack.api.command.DeleteNetrisProviderCmd;
+import org.apache.cloudstack.api.command.ListNetrisProvidersCmd;
+import org.apache.cloudstack.api.response.NetrisProviderResponse;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
+import org.apache.cloudstack.resource.NetrisResource;
 
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
 
 public class NetrisProviderServiceImpl implements NetrisProviderService {
+
+    @Inject
+    DataCenterDao dataCenterDao;
+    @Inject
+    ResourceManager resourceManager;
+    @Inject
+    NetrisProviderDao netrisProviderDao;
+    @Inject
+    HostDetailsDao hostDetailsDao;
+    @Inject
+    PhysicalNetworkDao physicalNetworkDao;
+    @Inject
+    NetworkDao networkDao;
+
     @Override
     public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
-        return null;
+        final Long zoneId = cmd.getZoneId();
+        final String name = cmd.getName();
+        final String hostname = cmd.getHostname();
+        final String port = cmd.getPort();
+        final String username = cmd.getUsername();
+        final String password = cmd.getPassword();
+        final String tenantName = cmd.getTenantName();
+        final String siteName = cmd.getSiteName();
+
+        Map<String, String> params = new HashMap<>();
+        params.put("guid", UUID.randomUUID().toString());
+        params.put("zoneId", zoneId.toString());
+        params.put("name", name);
+        params.put("hostname", hostname);
+        params.put("port", port);
+        params.put("username", username);
+        params.put("password", password);
+        params.put("siteName", siteName);
+        params.put("tenantName", tenantName);
+
+        Map<String, Object> hostdetails = new HashMap<>(params);
+        NetrisProvider netrisProvider;
+
+        NetrisResource netrisResource = new NetrisResource();
+        try {
+            netrisResource.configure(hostname, hostdetails);
+            final Host host = resourceManager.addHost(zoneId, netrisResource, netrisResource.getType(), params);
+            if (host != null) {
+                netrisProvider = Transaction.execute((TransactionCallback<NetrisProviderVO>) status -> {
+                    NetrisProviderVO netrisProviderVO = new NetrisProviderVO.Builder()
+                            .setZoneId(zoneId)
+                            .setHostId(host.getId())
+                            .setName(name)
+                            .setPort(port)
+                            .setHostname(hostname)
+                            .setUsername(username)
+                            .setPassword(password)
+                            .setSiteName(siteName)
+                            .setTenantName(tenantName)
+                            .build();
+
+                    netrisProviderDao.persist(netrisProviderVO);
+
+                    DetailVO detail = new DetailVO(host.getId(), "netriscontrollerid",
+                            String.valueOf(netrisProviderVO.getId()));
+                    hostDetailsDao.persist(detail);
+
+                    return netrisProviderVO;
+                });
+            } else {
+                throw new CloudRuntimeException("Failed to add Netris controller due to internal error.");
+            }
+        } catch (ConfigurationException e) {
+            throw new CloudRuntimeException(e.getMessage());
+        }
+        return  netrisProvider;
+    }
+
+    @Override
+    public List<BaseResponse> listNetrisProviders(Long zoneId) {
+        List<BaseResponse> netrisControllersResponseList = new ArrayList<>();
+        if (zoneId != null) {
+            NetrisProviderVO netrisProviderVO = netrisProviderDao.findByZoneId(zoneId);
+            if (Objects.nonNull(netrisProviderVO)) {
+                netrisControllersResponseList.add(createNetrisProviderResponse(netrisProviderVO));
+            }
+        } else {
+            List<NetrisProviderVO> netrisProviderVOList = netrisProviderDao.listAll();
+            for (NetrisProviderVO nsxProviderVO : netrisProviderVOList) {
+                netrisControllersResponseList.add(createNetrisProviderResponse(nsxProviderVO));
+            }
+        }
+
+        return netrisControllersResponseList;
+    }
+
+    @Override
+    public boolean deleteNetrisProvider(Long providerId) {
+        NetrisProviderVO netrisProvider = netrisProviderDao.findById(providerId);
+        if (Objects.isNull(netrisProvider)) {
+            throw new InvalidParameterValueException(String.format("Failed to find Netris provider with id: %s", providerId));
+        }
+        Long zoneId = netrisProvider.getZoneId();
+        // Find the physical network we work for
+        List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZone(zoneId);
+        for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
+            List<NetworkVO> networkList = networkDao.listByPhysicalNetwork(physicalNetwork.getId());
+            if (!CollectionUtils.isNullOrEmpty(networkList)) {
+                validateNetworkState(networkList);
+            }
+        }
+        netrisProviderDao.remove(providerId);
+        return true;
+    }
+
+    @Override
+    public NetrisProviderResponse createNetrisProviderResponse(NetrisProvider provider) {
+        DataCenterVO zone  = dataCenterDao.findById(provider.getZoneId());
+        if (Objects.isNull(zone)) {
+            throw new CloudRuntimeException(String.format("Failed to find zone with id %s", provider.getZoneId()));
+        }
+
+        NetrisProviderResponse response = new NetrisProviderResponse();
+        response.setName(provider.getName());
+        response.setUuid(provider.getUuid());
+        response.setHostname(provider.getHostname());
+        response.setPort(provider.getPort());
+        response.setZoneId(zone.getUuid());
+        response.setZoneName(zone.getName());
+        response.setSiteName(provider.getSiteName());
+        response.setTenantName(provider.getTenantName());
+        response.setObjectName("netrisProvider");
+        return response;
+    }
+
+    @VisibleForTesting
+    void validateNetworkState(List<NetworkVO> networkList) {
+        for (NetworkVO network : networkList) {
+            if (network.getBroadcastDomainType() == Networks.BroadcastDomainType.Netris &&
+                    ((network.getState() != Network.State.Shutdown) && (network.getState() != Network.State.Destroy))) {
+                throw new CloudRuntimeException("This Netris provider cannot be deleted as there are one or more logical networks provisioned by CloudStack on it.");
+            }
+        }
     }
 
     @Override
@@ -34,6 +201,8 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
         List<Class<?>> cmdList = new ArrayList<>();
         if (Boolean.TRUE.equals(NetworkOrchestrationService.NETRIS_ENABLED.value())) {
             cmdList.add(AddNetrisProviderCmd.class);
+            cmdList.add(ListNetrisProvidersCmd.class);
+            cmdList.add(DeleteNetrisProviderCmd.class);
         }
         return cmdList;
     }
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisPublicNetworkGuru.java
similarity index 64%
copy from plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java
copy to plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisPublicNetworkGuru.java
index 4df71056601..11db60a5aca 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisPublicNetworkGuru.java
@@ -17,68 +17,44 @@
 package org.apache.cloudstack.service;
 
 import com.cloud.dc.VlanDetailsVO;
-import com.cloud.dc.dao.VlanDetailsDao;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 import com.cloud.network.Network;
 import com.cloud.network.Networks;
-import com.cloud.network.nsx.NsxService;
 import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.guru.PublicNetworkGuru;
+import com.cloud.network.netris.NetrisService;
 import com.cloud.network.vpc.VpcOffering;
 import com.cloud.network.vpc.VpcOfferingVO;
 import com.cloud.network.vpc.VpcVO;
-import com.cloud.network.vpc.dao.VpcDao;
-import com.cloud.network.vpc.dao.VpcOfferingDao;
-import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
 import com.cloud.offering.NetworkOffering;
-import com.cloud.offerings.dao.NetworkOfferingDao;
 import com.cloud.user.Account;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.NicProfile;
 import com.cloud.vm.VirtualMachineProfile;
-import org.apache.cloudstack.NsxAnswer;
-import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand;
 import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.utils.NsxControllerUtils;
-import org.apache.cloudstack.utils.NsxHelper;
 import org.apache.commons.collections.CollectionUtils;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 
 import javax.inject.Inject;
 import java.util.List;
 import java.util.stream.Collectors;
 
-public class NsxPublicNetworkGuru extends PublicNetworkGuru {
+public class NetrisPublicNetworkGuru extends PublicNetworkGuru {
 
     @Inject
-    private VlanDetailsDao vlanDetailsDao;
-    @Inject
-    private VpcDao vpcDao;
-    @Inject
-    private VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
-    @Inject
-    private NsxControllerUtils nsxControllerUtils;
-    @Inject
-    private NsxService nsxService;
-    @Inject
-    private VpcOfferingDao vpcOfferingDao;
-    @Inject
-    private NetworkOfferingDao offeringDao;
+    private NetrisService netrisService;
 
-    protected Logger logger = LogManager.getLogger(getClass());
-
-    public NsxPublicNetworkGuru() {
+    public NetrisPublicNetworkGuru() {
         super();
     }
 
     @Override
     protected boolean canHandle(NetworkOffering offering) {
-        return isMyTrafficType(offering.getTrafficType()) && offering.isSystemOnly() && offering.isForNsx();
+        boolean isForNetris = networkModel.isProviderForNetworkOffering(Network.Provider.Netris, offering.getId());
+        return isMyTrafficType(offering.getTrafficType()) && offering.isSystemOnly() && isForNetris;
     }
 
     @Override
@@ -89,14 +65,14 @@ public class NsxPublicNetworkGuru extends PublicNetworkGuru {
 
         if (offering.getTrafficType() == Networks.TrafficType.Public) {
             return new NetworkVO(offering.getTrafficType(), Networks.Mode.Static, network.getBroadcastDomainType(), offering.getId(), Network.State.Setup, plan.getDataCenterId(),
-                            plan.getPhysicalNetworkId(), offering.isRedundantRouter());
+                    plan.getPhysicalNetworkId(), offering.isRedundantRouter());
         }
         return null;
     }
 
     @Override
     public NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException {
-        logger.debug("NSX Public network guru: allocate");
+        logger.debug("Netris Public network guru: allocate");
 
         IPAddressVO ipAddress = _ipAddressDao.findByIp(nic.getIPv4Address());
         if (ipAddress == null) {
@@ -120,11 +96,12 @@ public class NsxPublicNetworkGuru extends PublicNetworkGuru {
             logger.error(err);
             throw new CloudRuntimeException(err);
         }
+
         ips = ips.stream().filter(x -> !x.getAddress().addr().equals(nic.getIPv4Address())).collect(Collectors.toList());
-        // Use Source NAT IP address from the NSX Public Range. Do not Use the VR Public IP address
+        // Use Source NAT IP address from the Netris Public Range. Do not Use the VR Public IP address
         ipAddress = ips.get(0);
         if (ipAddress.isSourceNat() && !ipAddress.isForSystemVms()) {
-            VlanDetailsVO detail = vlanDetailsDao.findDetail(ipAddress.getVlanId(), ApiConstants.NSX_DETAIL_KEY);
+            VlanDetailsVO detail = vlanDetailsDao.findDetail(ipAddress.getVlanId(), ApiConstants.NETRIS_DETAIL_KEY);
             if (detail != null && detail.getValue().equalsIgnoreCase("true")) {
                 long accountId = vpc.getAccountId();
                 long domainId = vpc.getDomainId();
@@ -136,10 +113,10 @@ public class NsxPublicNetworkGuru extends PublicNetworkGuru {
                 boolean sourceNatEnabled = !NetworkOffering.NetworkMode.ROUTED.equals(vpcVO.getNetworkMode()) &&
                         vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), services);
 
-                logger.info(String.format("Creating Tier 1 Gateway for VPC %s", vpc.getName()));
-                boolean result = nsxService.createVpcNetwork(dataCenterId, accountId, domainId, resourceId, vpc.getName(), sourceNatEnabled);
+                logger.info(String.format("Creating Netris VPC %s", vpc.getName()));
+                boolean result = netrisService.createVpcResource(dataCenterId, accountId, domainId, resourceId, vpc.getName(), sourceNatEnabled, vpc.getCidr(), true);
                 if (!result) {
-                    String msg = String.format("Error creating Tier 1 Gateway for VPC %s", vpc.getName());
+                    String msg = String.format("Error creating Netris VPC %s", vpc.getName());
                     logger.error(msg);
                     throw new CloudRuntimeException(msg);
                 }
@@ -151,18 +128,6 @@ public class NsxPublicNetworkGuru extends PublicNetworkGuru {
                 if (!hasNatSupport) {
                     return nic;
                 }
-
-                String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(domainId, accountId, dataCenterId, resourceId, isForVpc);
-                String translatedIp = ipAddress.getAddress().addr();
-                logger.debug(String.format("Creating NSX Nat Rule for Tier1 GW %s for translated IP %s", tier1GatewayName, translatedIp));
-                String natRuleId = NsxControllerUtils.getNsxNatRuleId(domainId, accountId, dataCenterId, resourceId, isForVpc);
-                CreateOrUpdateNsxTier1NatRuleCommand cmd = NsxHelper.createOrUpdateNsxNatRuleCommand(domainId, accountId, dataCenterId, tier1GatewayName, "SNAT", translatedIp, natRuleId);
-                NsxAnswer nsxAnswer = nsxControllerUtils.sendNsxCommand(cmd, dataCenterId);
-                if (!nsxAnswer.getResult()) {
-                    String msg = String.format("Could not create NSX Nat Rule on Tier1 Gateway %s for IP %s", tier1GatewayName, translatedIp);
-                    logger.error(msg);
-                    throw new CloudRuntimeException(msg);
-                }
             }
         }
         return nic;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
new file mode 100644
index 00000000000..66d972b043e
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -0,0 +1,97 @@
+// 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.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.dao.NetrisProviderDao;
+import com.cloud.network.element.NetrisProviderVO;
+import com.cloud.network.netris.NetrisService;
+import com.cloud.network.vpc.Vpc;
+import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.NetrisAnswer;
+import org.apache.cloudstack.agent.api.NetrisCommand;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import javax.inject.Inject;
+import java.util.Objects;
+
+public class NetrisServiceImpl implements NetrisService, Configurable {
+
+    protected Logger logger = LogManager.getLogger(getClass());
+
+    @Inject
+    private NetrisProviderDao netrisProviderDao;
+    @Inject
+    private AgentManager agentMgr;
+
+    @Override
+    public String getConfigComponentName() {
+        return NetrisService.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey[0];
+    }
+
+    private NetrisProviderVO getZoneNetrisProvider(long zoneId) {
+        NetrisProviderVO netrisProviderVO = netrisProviderDao.findByZoneId(zoneId);
+        if (netrisProviderVO == null) {
+            logger.error("No Netris controller was found!");
+            throw new InvalidParameterValueException("Failed to find a Netris controller");
+        }
+        return netrisProviderVO;
+    }
+
+    private NetrisAnswer sendNetrisCommand(NetrisCommand cmd, long zoneId) {
+        NetrisProviderVO netrisProviderVO = getZoneNetrisProvider(zoneId);
+        Answer answer = agentMgr.easySend(netrisProviderVO.getHostId(), cmd);
+        if (answer == null || !answer.getResult()) {
+            logger.error("Netris API Command failed");
+            throw new InvalidParameterValueException("Failed API call to Netris controller");
+        }
+        return (NetrisAnswer) answer;
+    }
+
+    @Override
+    public boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpc) {
+        CreateNetrisVpcCommand cmd = new CreateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, cidr, vpcId, isVpc);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc) {
+        DeleteNetrisVpcCommand cmd = new DeleteNetrisVpcCommand(zoneId, accountId, domainId, vpc.getName(), vpc.getCidr(), vpc.getId(), true);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
+        CreateNetrisVnetCommand cmd = new CreateNetrisVnetCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, cidr, !Objects.isNull(vpcId));
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/netris/spring-netris-context.xml b/plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/core/spring-netris-core-managers-context.xml
similarity index 93%
copy from plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/netris/spring-netris-context.xml
copy to plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/core/spring-netris-core-managers-context.xml
index f0dfe703a2b..397460dc7f8 100644
--- a/plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/netris/spring-netris-context.xml
+++ b/plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/core/spring-netris-core-managers-context.xml
@@ -25,6 +25,7 @@
                       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                       http://www.springframework.org/schema/context
                       http://www.springframework.org/schema/context/spring-context.xsd">
-    <bean id="netrisProviderService" class="org.apache.cloudstack.service.NetrisProviderServiceImpl"/>
+
+    <bean id="netrisService" class="org.apache.cloudstack.service.NetrisServiceImpl"/>
 
 </beans>
diff --git a/plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/netris/spring-netris-context.xml b/plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/netris/spring-netris-context.xml
index f0dfe703a2b..a0551b7804a 100644
--- a/plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/netris/spring-netris-context.xml
+++ b/plugins/network-elements/netris/src/main/resources/META-INF/cloudstack/netris/spring-netris-context.xml
@@ -25,6 +25,17 @@
                       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                       http://www.springframework.org/schema/context
                       http://www.springframework.org/schema/context/spring-context.xsd">
+    <bean id="Netris" class="org.apache.cloudstack.service.NetrisElement">
+        <property name="name" value="Netris"/>
+    </bean>
+
+    <bean id="netrisGuestNetworkGuru" class="org.apache.cloudstack.service.NetrisGuestNetworkGuru">
+        <property name="name" value="NetrisGuestNetworkGuru" />
+    </bean>
+    <bean id="NetrisPublicNetworkGuru" class="org.apache.cloudstack.service.NetrisPublicNetworkGuru">
+        <property name="name" value="NetrisPublicNetworkGuru" />
+    </bean>
+
     <bean id="netrisProviderService" class="org.apache.cloudstack.service.NetrisProviderServiceImpl"/>
 
 </beans>
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java
new file mode 100644
index 00000000000..11b4af48fed
--- /dev/null
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java
@@ -0,0 +1,57 @@
+// 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.resource;
+
+import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NetrisResourceObjectUtilsTest {
+
+    private static final long zoneId = 1;
+    private static final long accountId = 2;
+    private static final long domainId = 2;
+
+    private static final long vpcId = 10;
+    private static final String vpcName = "testVpc";
+    private static final String vpcCidr = "10.10.0.0/16";
+
+    @Test
+    public void testCreateVpcName() {
+        CreateNetrisVpcCommand cmd = new CreateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, vpcCidr, vpcId, true);
+        String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC);
+        String expectedNetrisVpcName = String.format("D%s-A%s-Z%s-V%s-%s", domainId, accountId, zoneId, vpcId, vpcName);
+        Assert.assertEquals(expectedNetrisVpcName, netrisVpcName);
+    }
+
+    @Test
+    public void testCreateVpcIpamAllocationName() {
+        CreateNetrisVpcCommand cmd = new CreateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, vpcCidr, vpcId, true);
+        String ipamAllocationName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, vpcCidr);
+        String expectedNetrisVpcName = String.format("D%s-A%s-Z%s-V%s-%s", domainId, accountId, zoneId, vpcId, vpcCidr);
+        Assert.assertEquals(expectedNetrisVpcName, ipamAllocationName);
+    }
+
+    @Test
+    public void testDeleteVpcName() {
+        DeleteNetrisVpcCommand cmd = new DeleteNetrisVpcCommand(zoneId, accountId, domainId, vpcName, vpcCidr, vpcId, true);
+        String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC);
+        String expectedNetrisVpcName = String.format("D%s-A%s-Z%s-V%s-%s", domainId, accountId, zoneId, vpcId, vpcName);
+        Assert.assertEquals(expectedNetrisVpcName, netrisVpcName);
+    }
+}
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
index 760b25e53b7..d57fbae1c86 100644
--- a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
@@ -30,8 +30,10 @@ public class NetrisApiClientImplTest {
     private static final String endpointUrl = "https://shapeblue-ctl.netris.dev";
     private static final String username = "netris";
     private static final String password = "qHHa$CZ2oJv*@!7mwoSR";
+    private static final String siteName = "Datacenter-1";
+    private static final String adminTenantName = "Admin";
 
-    private static final NetrisApiClientImpl client = new NetrisApiClientImpl(endpointUrl, username, password);
+    private static final NetrisApiClientImpl client = new NetrisApiClientImpl(endpointUrl, username, password, siteName, adminTenantName);
 
     @Test
     public void testNetrisAuthStatus() {
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java
index 5a3e5586400..f7bf0a11bda 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java
@@ -41,7 +41,8 @@ public class DeleteNsxControllerCmd extends BaseCmd {
 
     @Inject
     protected NsxProviderService nsxProviderService;
-/////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////
     //////////////// API parameters /////////////////////
     /////////////////////////////////////////////////////
 
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java
index 032967d4061..047ee5837ae 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java
@@ -23,7 +23,6 @@ import com.cloud.dc.DataCenter;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.domain.DomainVO;
-import com.cloud.domain.dao.DomainDao;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 import com.cloud.exception.InvalidParameterValueException;
@@ -40,9 +39,7 @@ import com.cloud.network.guru.GuestNetworkGuru;
 import com.cloud.network.vpc.VpcVO;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.offerings.NetworkOfferingVO;
-import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
 import com.cloud.user.Account;
-import com.cloud.user.dao.AccountDao;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.NicProfile;
@@ -67,15 +64,9 @@ import java.util.Objects;
 public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigrationResponder  {
     protected Logger logger = LogManager.getLogger(getClass());
 
-    @Inject
-    NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
     @Inject
     NsxControllerUtils nsxControllerUtils;
     @Inject
-    AccountDao accountDao;
-    @Inject
-    DomainDao domainDao;
-    @Inject
     NetworkModel networkModel;
 
     public NsxGuestNetworkGuru() {
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java
index 4df71056601..d7605a1a858 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java
@@ -17,7 +17,6 @@
 package org.apache.cloudstack.service;
 
 import com.cloud.dc.VlanDetailsVO;
-import com.cloud.dc.dao.VlanDetailsDao;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientAddressCapacityException;
@@ -31,11 +30,7 @@ import com.cloud.network.guru.PublicNetworkGuru;
 import com.cloud.network.vpc.VpcOffering;
 import com.cloud.network.vpc.VpcOfferingVO;
 import com.cloud.network.vpc.VpcVO;
-import com.cloud.network.vpc.dao.VpcDao;
-import com.cloud.network.vpc.dao.VpcOfferingDao;
-import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
 import com.cloud.offering.NetworkOffering;
-import com.cloud.offerings.dao.NetworkOfferingDao;
 import com.cloud.user.Account;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.NicProfile;
@@ -55,20 +50,10 @@ import java.util.stream.Collectors;
 
 public class NsxPublicNetworkGuru extends PublicNetworkGuru {
 
-    @Inject
-    private VlanDetailsDao vlanDetailsDao;
-    @Inject
-    private VpcDao vpcDao;
-    @Inject
-    private VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
     @Inject
     private NsxControllerUtils nsxControllerUtils;
     @Inject
     private NsxService nsxService;
-    @Inject
-    private VpcOfferingDao vpcOfferingDao;
-    @Inject
-    private NetworkOfferingDao offeringDao;
 
     protected Logger logger = LogManager.getLogger(getClass());
 
@@ -78,7 +63,8 @@ public class NsxPublicNetworkGuru extends PublicNetworkGuru {
 
     @Override
     protected boolean canHandle(NetworkOffering offering) {
-        return isMyTrafficType(offering.getTrafficType()) && offering.isSystemOnly() && offering.isForNsx();
+        boolean isForNsx = networkModel.isProviderForNetworkOffering(Network.Provider.Nsx, offering.getId());
+        return isMyTrafficType(offering.getTrafficType()) && offering.isSystemOnly() && isForNsx;
     }
 
     @Override
diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java
index 1abef392345..ae3f3f3ecd5 100644
--- a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java
+++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java
@@ -22,10 +22,10 @@ import com.cloud.deploy.DeploymentPlan;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 import com.cloud.network.Network;
+import com.cloud.network.NetworkModel;
 import com.cloud.network.Networks;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
-import com.cloud.network.guru.PublicNetworkGuru;
 import com.cloud.network.vpc.VpcOfferingVO;
 import com.cloud.network.vpc.VpcVO;
 import com.cloud.network.vpc.dao.VpcDao;
@@ -56,6 +56,7 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.times;
@@ -80,23 +81,26 @@ public class NsxPublicNetworkGuruTest {
     VpcOfferingDao vpcOfferingDao;
     @Mock
     NsxControllerUtils nsxControllerUtils;
+    @Mock
+    NetworkModel networkModel;
 
     @Before
     public void setup() {
         guru = new NsxPublicNetworkGuru();
 
-        ReflectionTestUtils.setField((PublicNetworkGuru) guru, "_ipAddressDao", ipAddressDao);
+        ReflectionTestUtils.setField(guru, "_ipAddressDao", ipAddressDao);
         ReflectionTestUtils.setField(guru, "vpcDao", vpcDao);
         ReflectionTestUtils.setField(guru, "vlanDetailsDao", vlanDetailsDao);
         ReflectionTestUtils.setField(guru, "vpcOfferingServiceMapDao", vpcOfferingServiceMapDao);
         ReflectionTestUtils.setField(guru, "nsxService", nsxService);
         ReflectionTestUtils.setField(guru, "vpcOfferingDao", vpcOfferingDao);
         ReflectionTestUtils.setField(guru, "nsxControllerUtils", nsxControllerUtils);
+        ReflectionTestUtils.setField(guru, "networkModel", networkModel);
 
         offering = Mockito.mock(NetworkOffering.class);
         when(offering.getTrafficType()).thenReturn(Networks.TrafficType.Public);
-        when(offering.isForNsx()).thenReturn(true);
         when(offering.isSystemOnly()).thenReturn(true);
+        when(networkModel.isProviderForNetworkOffering(eq(Network.Provider.Nsx), anyLong())).thenReturn(true);
     }
 
     @Test
diff --git a/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/api/command/ConfigTungstenFabricServiceCmd.java b/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/api/command/ConfigTungstenFabricServiceCmd.java
index 19bf0a33909..83e1a1d182c 100644
--- a/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/api/command/ConfigTungstenFabricServiceCmd.java
+++ b/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/api/command/ConfigTungstenFabricServiceCmd.java
@@ -165,7 +165,6 @@ public class ConfigTungstenFabricServiceCmd extends BaseCmd {
                             "Default offering for Tungsten-Fabric Network", Networks.TrafficType.Guest, false, false,
                             null, null, true, NetworkOffering.Availability.Optional, null, Network.GuestType.Isolated,
                             false, false, false, false, true, false);
-                    networkOfferingVO.setForTungsten(true);
                     networkOfferingVO.setState(NetworkOffering.State.Enabled);
                     networkOfferingDao.persist(networkOfferingVO);
 
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 69cd7930a69..c667cbd6dae 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -234,6 +234,7 @@
                 <module>hypervisors/vmware</module>
                 <module>network-elements/cisco-vnmc</module>
                 <module>network-elements/nsx</module>
+                <module>network-elements/netris</module>
                 <module>network-elements/juniper-contrail</module>
                 <module>network-elements/tungsten</module>
             </modules>
diff --git a/pom.xml b/pom.xml
index 29fc939f553..735d68c03ef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,7 +142,7 @@
         <cs.gmavenplus.version>3.0.2</cs.gmavenplus.version>
         <cs.google-http-client>1.42.3</cs.google-http-client>
         <cs.groovy.version>2.4.17</cs.groovy.version>
-        <cs.gson.version>1.7.2</cs.gson.version>
+        <cs.gson.version>2.10.1</cs.gson.version>
         <cs.guava.version>31.1-jre</cs.guava.version>
         <cs.httpclient.version>4.5.14</cs.httpclient.version>
         <cs.httpcore.version>4.4.16</cs.httpcore.version>
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 810f0abd7e0..2f5c6d2832a 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -41,6 +41,7 @@ import javax.inject.Inject;
 
 import com.cloud.bgp.ASNumber;
 import com.cloud.bgp.ASNumberRange;
+import com.cloud.configuration.ConfigurationService;
 import com.cloud.dc.ASNumberRangeVO;
 import com.cloud.dc.ASNumberVO;
 import com.cloud.dc.VlanDetailsVO;
@@ -997,8 +998,7 @@ public class ApiResponseHelper implements ResponseGenerator {
                 }
             }
             vlanResponse.setForSystemVms(isForSystemVms(vlan.getId()));
-            VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlan.getId(), ApiConstants.NSX_DETAIL_KEY);
-            vlanResponse.setForNsx(Objects.nonNull(vlanDetail) && vlanDetail.getValue().equals("true"));
+            vlanResponse.setProvider(getProviderFromVlanDetailKey(vlan));
             vlanResponse.setObjectName("vlan");
             return vlanResponse;
         } catch (InstantiationException | IllegalAccessException e) {
@@ -1006,6 +1006,15 @@ public class ApiResponseHelper implements ResponseGenerator {
         }
     }
 
+    private String getProviderFromVlanDetailKey(Vlan vlan) {
+        for (Map.Entry<String, String> entry : ConfigurationService.ProviderDetailKeyMap.entrySet()) {
+            VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlan.getId(), entry.getValue());
+            if (Objects.nonNull(vlanDetail) && "true".equals(vlanDetail.getValue())) {
+                return entry.getKey();
+            }
+        }
+        return null;
+    }
     /**
      * Return true if vlan IP range is dedicated for system vms (SSVM and CPVM), false if not
      * @param vlanId vlan id
@@ -2414,8 +2423,8 @@ public class ApiResponseHelper implements ResponseGenerator {
             serviceResponses.add(svcRsp);
         }
         response.setForVpc(_configMgr.isOfferingForVpc(offering));
-        response.setForTungsten(offering.isForTungsten());
-        response.setForNsx(offering.isForNsx());
+        response.setForTungsten(_ntwkModel.isProviderForNetworkOffering(Provider.Tungsten, offering.getId()));
+        response.setForNsx(_ntwkModel.isProviderForNetworkOffering(Provider.Nsx, offering.getId()));
         if (offering.getNetworkMode() != null) {
             response.setNetworkMode(offering.getNetworkMode().name());
         }
diff --git a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
index 2b3be728bd3..079929d68ec 100644
--- a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
@@ -25,7 +25,10 @@ import javax.inject.Inject;
 import com.cloud.cpu.CPU;
 import com.cloud.dc.ASNumberRangeVO;
 import com.cloud.dc.dao.ASNumberRangeDao;
+import com.cloud.network.Network;
+import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NsxProviderDao;
+import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.element.NsxProviderVO;
 import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
@@ -62,6 +65,8 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase<DataCenterJoinVO, Long
     @Inject
     private NsxProviderDao nsxProviderDao;
     @Inject
+    private NetrisProviderDao netrisProviderDao;
+    @Inject
     private ASNumberRangeDao asNumberRangeDao;
 
     protected DataCenterJoinDaoImpl() {
@@ -129,10 +134,7 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase<DataCenterJoinVO, Long
             }
         }
 
-        NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(dataCenter.getId());
-        if (Objects.nonNull(nsxProviderVO)) {
-            zoneResponse.setNsxEnabled(true);
-        }
+        setExternalNetworkProviderUsedByZone(zoneResponse, dataCenter.getId());
 
         List<CPU.CPUArch> clusterArchs = ApiDBUtils.listZoneClustersArchs(dataCenter.getId());
         zoneResponse.setMultiArch(CollectionUtils.isNotEmpty(clusterArchs) && clusterArchs.size() > 1);
@@ -152,6 +154,19 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase<DataCenterJoinVO, Long
         return zoneResponse;
     }
 
+    private void setExternalNetworkProviderUsedByZone(ZoneResponse zoneResponse, Long zoneId) {
+        NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId);
+        if (Objects.nonNull(nsxProviderVO)) {
+            zoneResponse.setNsxEnabled(true);
+            zoneResponse.setProvider(Network.Provider.Nsx.getName());
+        }
+
+        NetrisProviderVO netrisProviderVO = netrisProviderDao.findByZoneId(zoneId);
+        if (Objects.nonNull(netrisProviderVO)) {
+            zoneResponse.setProvider(Network.Provider.Netris.getName());
+        }
+    }
+
     @Override
     public DataCenterJoinVO newDataCenterView(DataCenter dataCenter) {
         SearchCriteria<DataCenterJoinVO> sc = dofIdSearch.create();
diff --git a/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java
index afa5c206f0f..7ea4b7d5834 100644
--- a/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java
@@ -19,6 +19,8 @@ package com.cloud.api.query.dao;
 
 import java.util.List;
 
+import com.cloud.network.Network;
+import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
 import org.apache.cloudstack.api.response.VpcOfferingResponse;
 import org.apache.commons.lang3.StringUtils;
 
@@ -29,8 +31,13 @@ import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.net.NetUtils;
 
+import javax.inject.Inject;
+
 public class VpcOfferingJoinDaoImpl extends GenericDaoBase<VpcOfferingJoinVO, Long> implements VpcOfferingJoinDao {
 
+    @Inject
+    private VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
+
     private SearchBuilder<VpcOfferingJoinVO> sofIdSearch;
 
     protected VpcOfferingJoinDaoImpl() {
@@ -76,7 +83,7 @@ public class VpcOfferingJoinDaoImpl extends GenericDaoBase<VpcOfferingJoinVO, Lo
             offeringResponse.setDomain(offeringJoinVO.getDomainPath());
             offeringResponse.setZoneId(offeringJoinVO.getZoneUuid());
             offeringResponse.setZone(offeringJoinVO.getZoneName());
-            offeringResponse.setForNsx(offeringJoinVO.isForNsx());
+            offeringResponse.setForNsx(vpcOfferingServiceMapDao.isProviderForVpcOffering(Network.Provider.Nsx, offeringJoinVO.getId()));
             if (offeringJoinVO.getNetworkMode() != null) {
                 offeringResponse.setNetworkMode(offeringJoinVO.getNetworkMode().name());
             }
diff --git a/server/src/main/java/com/cloud/api/query/vo/NetworkOfferingJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/NetworkOfferingJoinVO.java
index 4ed54de8042..d2b51e4f178 100644
--- a/server/src/main/java/com/cloud/api/query/vo/NetworkOfferingJoinVO.java
+++ b/server/src/main/java/com/cloud/api/query/vo/NetworkOfferingJoinVO.java
@@ -154,12 +154,6 @@ public class NetworkOfferingJoinVO extends BaseViewVO implements NetworkOffering
     @Column(name = "for_vpc")
     private boolean forVpc;
 
-    @Column(name = "for_tungsten")
-    boolean forTungsten;
-
-    @Column(name = "for_nsx")
-    boolean forNsx;
-
     @Column(name = "network_mode")
     NetworkMode networkMode;
 
@@ -355,22 +349,8 @@ public class NetworkOfferingJoinVO extends BaseViewVO implements NetworkOffering
         return forVpc;
     }
 
-    @Override
-    public boolean isForTungsten() {
-        return forTungsten;
-    }
-
     public void setForVpc(boolean forVpc) { this.forVpc = forVpc; }
 
-    @Override
-    public boolean isForNsx() {
-        return forNsx;
-    }
-
-    public void setForNsx(boolean forNsx) {
-        this.forNsx = forNsx;
-    }
-
     @Override
     public NetworkMode getNetworkMode() {
         return networkMode;
diff --git a/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java
index d72f5b61907..4e0707edf88 100644
--- a/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java
+++ b/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java
@@ -78,9 +78,6 @@ public class VpcOfferingJoinVO implements VpcOffering {
     @Column(name = "sort_key")
     int sortKey;
 
-    @Column(name = "for_nsx")
-    boolean forNsx = false;
-
     @Column(name = "network_mode")
     NetworkOffering.NetworkMode networkMode;
 
@@ -152,11 +149,6 @@ public class VpcOfferingJoinVO implements VpcOffering {
         return isDefault;
     }
 
-    @Override
-    public boolean isForNsx() {
-        return forNsx;
-    }
-
     @Override
     public NetworkOffering.NetworkMode getNetworkMode() {
         return networkMode;
diff --git a/server/src/main/java/com/cloud/api/response/ApiResponseSerializer.java b/server/src/main/java/com/cloud/api/response/ApiResponseSerializer.java
index c72c275c212..09341f6fd97 100644
--- a/server/src/main/java/com/cloud/api/response/ApiResponseSerializer.java
+++ b/server/src/main/java/com/cloud/api/response/ApiResponseSerializer.java
@@ -86,8 +86,8 @@ public class ApiResponseSerializer {
 
     public static String toJSONSerializedString(ResponseObject result, StringBuilder log) {
         if (result != null && log != null) {
-            Gson responseBuilder = ApiResponseGsonHelper.getBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
-            Gson logBuilder = ApiResponseGsonHelper.getLogBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
+            Gson responseBuilder = ApiResponseGsonHelper.getBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC).create();
+            Gson logBuilder = ApiResponseGsonHelper.getLogBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC).create();
 
             StringBuilder sb = new StringBuilder();
 
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 25cbd10de0a..5881d8bec53 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -45,6 +45,8 @@ import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.network.dao.NetrisProviderDao;
+import com.cloud.network.element.NetrisProviderVO;
 import org.apache.cloudstack.acl.SecurityChecker;
 import org.apache.cloudstack.affinity.AffinityGroup;
 import org.apache.cloudstack.affinity.AffinityGroupService;
@@ -470,6 +472,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
     Ipv6Service ipv6Service;
     @Inject
     NsxProviderDao nsxProviderDao;
+    @Inject
+    NetrisProviderDao netrisProviderDao;
 
     // FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao?
     @Inject
@@ -2613,10 +2617,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                 final boolean success = _zoneDao.remove(zoneId);
 
                 if (success) {
-                    NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId);
-                    if (Objects.nonNull(nsxProvider)) {
-                        nsxProviderDao.remove(nsxProvider.getId());
-                    }
+                    deleteExternalProviderIfAny(zoneId);
 
                     // delete template refs for this zone
                     templateZoneDao.deleteByZoneId(zoneId);
@@ -2642,6 +2643,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         });
     }
 
+    private void deleteExternalProviderIfAny(Long zoneId) {
+        NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId);
+        if (Objects.nonNull(nsxProvider)) {
+            nsxProviderDao.remove(nsxProvider.getId());
+        }
+        NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId);
+        if (Objects.nonNull(netrisProvider)) {
+            netrisProviderDao.remove(netrisProvider.getId());
+        }
+    }
+
     @Override
     @DB
     @ActionEvent(eventType = EventTypes.EVENT_ZONE_EDIT, eventDescription = "editing zone", async = false)
@@ -4696,12 +4708,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         }
 
         return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, forVirtualNetwork, forSystemVms, networkId, physicalNetworkId, startIPv6, endIPv6, ip6Gateway,
-                ip6Cidr, domain, vlanOwner, network, sameSubnet, cmd.isForNsx());
+                ip6Cidr, domain, vlanOwner, network, sameSubnet, cmd.getProvider());
     }
 
     private Vlan commitVlan(final Long zoneId, final Long podId, final String startIP, final String endIP, final String newVlanGatewayFinal, final String newVlanNetmaskFinal,
             final String vlanId, final Boolean forVirtualNetwork, final Boolean forSystemVms, final Long networkId, final Long physicalNetworkId, final String startIPv6, final String endIPv6,
-            final String ip6Gateway, final String ip6Cidr, final Domain domain, final Account vlanOwner, final Network network, final Pair<Boolean, Pair<String, String>> sameSubnet, boolean forNsx) {
+            final String ip6Gateway, final String ip6Cidr, final Domain domain, final Account vlanOwner, final Network network, final Pair<Boolean, Pair<String, String>> sameSubnet, Provider provider) {
         final GlobalLock commitVlanLock = GlobalLock.getInternLock("CommitVlan");
         commitVlanLock.lock(5);
         logger.debug("Acquiring lock for committing vlan");
@@ -4729,7 +4741,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                         newVlanNetmask = sameSubnet.second().second();
                     }
                     final Vlan vlan = createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, forSystemVms, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId,
-                            false, domain, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr, forNsx);
+                            false, domain, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr, provider);
                     // create an entry in the nic_secondary table. This will be the new
                     // gateway that will be configured on the corresponding routervm.
                     return vlan;
@@ -4856,7 +4868,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
     @Override
     @DB
     public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final boolean forVirtualNetwork, final boolean forSystemVms, final Long podId, final String startIP, final String endIP,
-                                           final String vlanGateway, final String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr, boolean forNsx) {
+                                           final String vlanGateway, final String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr, Provider provider) {
         final Network network = _networkModel.getNetwork(networkId);
 
         boolean ipv4 = false, ipv6 = false;
@@ -4906,6 +4918,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         // 1) if vlan is specified for the guest network range, it should be the
         // same as network's vlan
         // 2) if vlan is missing, default it to the guest network's vlan
+        boolean forExternalProvider = ConfigurationService.IsIpRangeForProvider(provider);
         if (network.getTrafficType() == TrafficType.Guest) {
             String networkVlanId = null;
             boolean connectivityWithoutVlan = false;
@@ -4938,11 +4951,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             } else {
                 vlanId = networkVlanId;
             }
-        } else if (network.getTrafficType() == TrafficType.Public && vlanId == null && !forNsx) {
+        } else if (network.getTrafficType() == TrafficType.Public && vlanId == null && !forExternalProvider) {
             throw new InvalidParameterValueException("Unable to determine vlan id or untagged vlan for public network");
         }
 
-        if (vlanId == null && !forNsx) {
+        if (vlanId == null && !forExternalProvider) {
             vlanId = Vlan.UNTAGGED;
         }
 
@@ -5039,7 +5052,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         if (isSharedNetworkWithoutSpecifyVlan) {
             bypassVlanOverlapCheck = true;
         }
-        if (!bypassVlanOverlapCheck && !forNsx && !_zoneDao.findVnet(zoneId, physicalNetworkId, BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId))).isEmpty()) {
+        if (!bypassVlanOverlapCheck && !forExternalProvider && !_zoneDao.findVnet(zoneId, physicalNetworkId, BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId))).isEmpty()) {
             throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone "
                     + zone.getName());
         }
@@ -5055,7 +5068,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
         // Everything was fine, so persist the VLAN
         final VlanVO vlan = commitVlanAndIpRange(zoneId, networkId, physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, domain, vlanOwner, vlanIp6Gateway, vlanIp6Cidr,
-                ipv4, zone, vlanType, ipv6Range, ipRange, forSystemVms, forNsx);
+                ipv4, zone, vlanType, ipv6Range, ipRange, forSystemVms, provider);
 
         return vlan;
     }
@@ -5126,14 +5139,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
     private VlanVO commitVlanAndIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final Long podId, final String startIP, final String endIP,
             final String vlanGateway, final String vlanNetmask, final String vlanId, final Domain domain, final Account vlanOwner, final String vlanIp6Gateway, final String vlanIp6Cidr,
-            final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange, final boolean forSystemVms, final boolean forNsx) {
+            final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange, final boolean forSystemVms, final Provider provider) {
         return Transaction.execute(new TransactionCallback<VlanVO>() {
             @Override
             public VlanVO doInTransaction(final TransactionStatus status) {
                 VlanVO vlan = new VlanVO(vlanType, vlanId, vlanGateway, vlanNetmask, zone.getId(), ipRange, networkId, physicalNetworkId, vlanIp6Gateway, vlanIp6Cidr, ipv6Range);
                 logger.debug("Saving vlan range " + vlan);
                 vlan = _vlanDao.persist(vlan);
-                vlanDetailsDao.addDetail(vlan.getId(), ApiConstants.NSX_DETAIL_KEY, String.valueOf(forNsx), true);
+                if (Objects.nonNull(provider)) {
+                    addProviderVlanDetailKey(vlan, provider);
+                }
 
                 // IPv6 use a used ip map, is different from ipv4, no need to save
                 // public ip range
@@ -5174,6 +5189,15 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
     }
 
+    private void addProviderVlanDetailKey(Vlan vlan, Provider provider) {
+        vlanDetailsDao.addDetail(vlan.getId(), getProviderDetailKey(provider.getName()),
+                String.valueOf(ConfigurationService.IsIpRangeForProvider(provider)), true);
+    }
+
+    private String getProviderDetailKey(String providerName) {
+        return ConfigurationService.ProviderDetailKeyMap.get(providerName);
+    }
+
     @Override
     public Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOperationException,
             ResourceUnavailableException,ResourceAllocationException {
@@ -6829,9 +6853,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         if (serviceOfferingId != null) {
             offeringFinal.setServiceOfferingId(serviceOfferingId);
         }
-
-        offeringFinal.setForTungsten(Objects.requireNonNullElse(forTungsten, false));
-        offeringFinal.setForNsx(Objects.requireNonNullElse(forNsx, false));
         offeringFinal.setNetworkMode(networkMode);
 
         if (enableOffering) {
diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
index 19760e6d025..95006bd5388 100644
--- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
+++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
@@ -2046,7 +2046,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
     }
 
     public static String logDeploymentWithoutException(VirtualMachine vm, DeploymentPlan plan, ExcludeList avoids, DeploymentPlanner planner) {
-        return LogUtils.logGsonWithoutException("Trying to deploy VM [%s] and details: Plan [%s]; avoid list [%s] and planner: [%s].", vm, plan, avoids, planner);
+        return LogUtils.logGsonWithoutException("Trying to deploy VM [%s] and details: Plan [%s]; avoid list [%s] and planner: [%s].", vm, plan, avoids, planner != null ? planner.getName() : null);
     }
     @Override
     public ConfigKey<?>[] getConfigKeys() {
diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java
index 1276ec22067..b8533b472ec 100644
--- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java
@@ -207,7 +207,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
     DomainManager _domainMgr;
 
     @Inject
-    NetworkOfferingServiceMapDao _ntwkOfferingSrvcDao;
+    protected NetworkOfferingServiceMapDao _ntwkOfferingSrvcDao;
     @Inject
     PhysicalNetworkDao _physicalNetworkDao;
     @Inject
@@ -494,7 +494,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
     @Override
     public Map<Provider, ArrayList<PublicIpAddress>> getProviderToIpList(Network network, Map<PublicIpAddress, Set<Service>> ipToServices) {
         NetworkOffering offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
-        if (!offering.isConserveMode() && !offering.isForNsx()) {
+        boolean isForNsx = isProviderForNetworkOffering(Provider.Nsx, offering.getId());
+        if (!offering.isConserveMode() && !isForNsx) {
             for (PublicIpAddress ip : ipToServices.keySet()) {
                 Set<Service> services = new HashSet<Service>();
                 services.addAll(ipToServices.get(ip));
@@ -1623,7 +1624,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
         if (!canIpUsedForService(publicIp, service, networkId)) {
             return false;
         }
-        if (!offering.isConserveMode() && !offering.isForNsx()) {
+        boolean isForNsx = isProviderForNetworkOffering(Provider.Nsx, offering.getId());
+        if (!offering.isConserveMode() && !isForNsx) {
             return canIpUsedForNonConserveService(publicIp, service);
         }
         return true;
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 2ccd5fdd820..05be4ea6307 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -2305,9 +2305,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
                     }
 
                     if (createVlan && network != null) {
+                        Provider networkProvider = getNetworkOfferingProvider(ntwkOff);
                         // Create vlan ip range
                         _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, false, null, startIP, endIP, gateway, netmask, vlanId,
-                                bypassVlanOverlapCheck, null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr, ntwkOff.isForNsx());
+                                bypassVlanOverlapCheck, null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr, networkProvider);
                     }
                     if (associatedNetwork != null) {
                         _networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.AssociatedNetworkId, String.valueOf(associatedNetwork.getId()), true));
@@ -2333,6 +2334,15 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
         }
     }
 
+    private Provider getNetworkOfferingProvider(NetworkOffering networkOffering) {
+        if (_networkModel.isProviderForNetworkOffering(Provider.Nsx, networkOffering.getId())) {
+            return Provider.Nsx;
+        } else if (_networkModel.isProviderForNetworkOffering(Provider.Netris, networkOffering.getId())) {
+            return Provider.Netris;
+        }
+        return null;
+    }
+
     @Override
     public Pair<List<? extends Network>, Integer> searchForNetworks(ListNetworksCmd cmd) {
         Long id = cmd.getId();
@@ -4236,6 +4246,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
                         logger.warn("Failed to add NSX provider to physical network due to:", ex.getMessage());
                     }
 
+                    // Add Netris provider
+                    try {
+                        addNetrisProviderToPhysicalNetwork(pNetwork.getId());
+                    } catch (Exception ex) {
+                        logger.warn("Failed to add Netris provider to physical network due to:", ex.getMessage());
+                    }
+
                     CallContext.current().putContextParameter(PhysicalNetwork.class, pNetwork.getUuid());
 
                     return pNetwork;
@@ -5646,6 +5663,22 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
         return null;
     }
 
+    private PhysicalNetworkServiceProvider addNetrisProviderToPhysicalNetwork(long physicalNetworkId) {
+        PhysicalNetworkVO pvo = _physicalNetworkDao.findById(physicalNetworkId);
+        DataCenterVO dvo = _dcDao.findById(pvo.getDataCenterId());
+        if (dvo.getNetworkType() == NetworkType.Advanced) {
+
+            Provider provider = Network.Provider.getProvider(Provider.Netris.getName());
+            if (provider == null) {
+                return null;
+            }
+
+            addProviderToPhysicalNetwork(physicalNetworkId, Provider.Netris.getName(), null, null);
+            enableProvider(Provider.Netris.getName());
+        }
+        return null;
+    }
+
     protected boolean isNetworkSystem(Network network) {
         NetworkOffering no = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId());
         if (no.isSystemOnly()) {
diff --git a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java
index c46be9bf428..957c5b1287a 100644
--- a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java
+++ b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java
@@ -22,6 +22,9 @@ import java.util.Random;
 
 import javax.inject.Inject;
 
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
+import com.cloud.user.dao.AccountDao;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -121,6 +124,12 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur
     Ipv6AddressManager ipv6AddressManager;
     @Inject
     DomainRouterDao domainRouterDao;
+    @Inject
+    public NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
+    @Inject
+    public AccountDao accountDao;
+    @Inject
+    public DomainDao domainDao;
 
     Random _rand = new Random(System.currentTimeMillis());
 
diff --git a/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java
index f5434388f40..bac397f2809 100644
--- a/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java
+++ b/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java
@@ -18,6 +18,10 @@ package com.cloud.network.guru;
 
 import javax.inject.Inject;
 
+import com.cloud.dc.dao.VlanDetailsDao;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.network.vpc.dao.VpcOfferingDao;
+import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 
 import com.cloud.dc.DataCenter;
@@ -74,7 +78,15 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru {
     @Inject
     Ipv6Service ipv6Service;
     @Inject
-    NetworkModel networkModel;
+    protected NetworkModel networkModel;
+    @Inject
+    protected VpcDao vpcDao;
+    @Inject
+    protected VlanDetailsDao vlanDetailsDao;
+    @Inject
+    protected VpcOfferingDao vpcOfferingDao;
+    @Inject
+    protected VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
 
     private static final TrafficType[] TrafficTypes = {TrafficType.Public};
 
diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
index 5c962a1cbff..83a3dbcfaa7 100644
--- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
+++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java
@@ -866,7 +866,11 @@ public class CommandSetupHelper {
                 String vlanTag = ipAddr.getVlanTag();
                 String key = null;
                 if (Objects.isNull(vlanTag)) {
-                    key = "nsx-" + ipAddr.getAddress().addr();
+                    if (Hypervisor.HypervisorType.VMware == router.getHypervisorType()) {
+                        key = "nsx-" + ipAddr.getAddress().addr();
+                    } else if (Hypervisor.HypervisorType.KVM == router.getHypervisorType()) {
+                        key = "netris-" + ipAddr.getAddress().addr();
+                    }
                 } else {
                     key = BroadcastDomainType.getValue(BroadcastDomainType.fromString(ipAddr.getVlanTag()));
                 }
@@ -1215,7 +1219,8 @@ public class CommandSetupHelper {
         final SetupGuestNetworkCommand setupCmd = new SetupGuestNetworkCommand(dhcpRange, networkDomain, router.getIsRedundantRouter(), defaultDns1, defaultDns2, add, _itMgr.toNicTO(nicProfile,
                 router.getHypervisorType()));
 
-        setupCmd.setVrGuestGateway(networkOfferingVO.isForNsx());
+        boolean isForNsx = _networkModel.isProviderForNetworkOffering(Provider.Nsx, networkOfferingVO.getId());
+        setupCmd.setVrGuestGateway(isForNsx);
         NicVO publicNic = _nicDao.findDefaultNicForVM(router.getId());
         if (publicNic != null) {
             updateSetupGuestNetworkCommandIpv6(setupCmd, network, publicNic, defaultIp6Dns1, defaultIp6Dns2);
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index df4a2e76d52..de17c25a72d 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -48,7 +48,9 @@ import com.cloud.bgp.BGPService;
 import com.cloud.dc.ASNumberVO;
 import com.cloud.dc.dao.ASNumberDao;
 import com.cloud.dc.Vlan;
+import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NsxProviderDao;
+import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.element.NsxProviderVO;
 import com.cloud.resourcelimit.CheckedReservation;
 import com.google.common.collect.Sets;
@@ -291,13 +293,15 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
     @Inject
     private NsxProviderDao nsxProviderDao;
     @Inject
+    private NetrisProviderDao netrisProviderDao;
+    @Inject
     RoutedIpv4Manager routedIpv4Manager;
 
     private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker"));
     private List<VpcProvider> vpcElements = null;
     private final List<Service> nonSupportedServices = Arrays.asList(Service.SecurityGroup, Service.Firewall);
     private final List<Provider> supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler,
-            Provider.JuniperContrailVpcRouter, Provider.Ovs, Provider.BigSwitchBcf, Provider.ConfigDrive, Provider.Nsx);
+            Provider.JuniperContrailVpcRouter, Provider.Ovs, Provider.BigSwitchBcf, Provider.ConfigDrive, Provider.Nsx, Provider.Netris);
 
     int _cleanupInterval;
     int _maxNetworks;
@@ -353,7 +357,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                     }
                     createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap,
                             true, State.Enabled, null, false,
-                            false, false, false, null, null, false);
+                            false, false, null, null, false);
                 }
 
                 // configure default vpc offering with Netscaler as LB Provider
@@ -373,7 +377,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                         }
                     }
                     createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName,
-                            svcProviderMap, false, State.Enabled, null, false, false, false, false, null, null, false);
+                            svcProviderMap, false, State.Enabled, null, false, false, false, null, null, false);
 
                 }
 
@@ -394,7 +398,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                         }
                     }
                     createVpcOffering(VpcOffering.redundantVPCOfferingName, VpcOffering.redundantVPCOfferingName, svcProviderMap, true, State.Enabled,
-                            null, false, false, true, false, null, null, false);
+                            null, false, false, true, null, null, false);
                 }
 
                 // configure default vpc offering with NSX as network service provider in NAT mode
@@ -411,7 +415,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                         }
                     }
                     createVpcOffering(VpcOffering.DEFAULT_VPC_NAT_NSX_OFFERING_NAME, VpcOffering.DEFAULT_VPC_NAT_NSX_OFFERING_NAME, svcProviderMap, false,
-                            State.Enabled, null, false, false, false, true, NetworkOffering.NetworkMode.NATTED, null, false);
+                            State.Enabled, null, false, false, false, NetworkOffering.NetworkMode.NATTED, null, false);
 
                 }
 
@@ -429,7 +433,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                         }
                     }
                     createVpcOffering(VpcOffering.DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME, VpcOffering.DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME, svcProviderMap, false,
-                            State.Enabled, null, false, false, false, true, NetworkOffering.NetworkMode.ROUTED, null, false);
+                            State.Enabled, null, false, false, false, NetworkOffering.NetworkMode.ROUTED, null, false);
 
                 }
 
@@ -447,7 +451,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                         }
                     }
                     createVpcOffering(VpcOffering.DEFAULT_VPC_ROUTE_NETRIS_OFFERING_NAME, VpcOffering.DEFAULT_VPC_ROUTE_NETRIS_OFFERING_NAME, svcProviderMap, false,
-                            State.Enabled, null, false, false, false, true, NetworkOffering.NetworkMode.ROUTED, null, false);
+                            State.Enabled, null, false, false, false, NetworkOffering.NetworkMode.ROUTED, null, false);
 
                 }
             }
@@ -660,7 +664,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         final boolean offersRegionLevelVPC = isVpcOfferingForRegionLevelVpc(serviceCapabilityList);
         final boolean redundantRouter = isVpcOfferingRedundantRouter(serviceCapabilityList, redundantRouterService);
         final VpcOfferingVO offering = createVpcOffering(name, displayText, svcProviderMap, false, state, serviceOfferingId, supportsDistributedRouter, offersRegionLevelVPC,
-                redundantRouter, forNsx, networkMode, routingMode, specifyAsNumber);
+                redundantRouter, networkMode, routingMode, specifyAsNumber);
 
         if (offering != null) {
             List<VpcOfferingDetailsVO> detailsVO = new ArrayList<>();
@@ -686,9 +690,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
     }
 
     @DB
-    protected VpcOfferingVO createVpcOffering(final String name, final String displayText, final Map<Network.Service, Set<Network.Provider>> svcProviderMap,
+    protected VpcOfferingVO createVpcOffering(final String name, final String displayText, final Map<Service, Set<Provider>> svcProviderMap,
                                               final boolean isDefault, final State state, final Long serviceOfferingId, final boolean supportsDistributedRouter, final boolean offersRegionLevelVPC,
-                                              final boolean redundantRouter, Boolean forNsx, NetworkOffering.NetworkMode networkMode, NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber) {
+                                              final boolean redundantRouter, NetworkOffering.NetworkMode networkMode, NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber) {
 
         return Transaction.execute(new TransactionCallback<VpcOfferingVO>() {
             @Override
@@ -699,7 +703,6 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                 if (state != null) {
                     offering.setState(state);
                 }
-                offering.setForNsx(forNsx);
                 offering.setNetworkMode(networkMode);
                 offering.setSpecifyAsNumber(specifyAsNumber);
                 if (Objects.nonNull(routingMode)) {
@@ -1308,11 +1311,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
             cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu(), cmd.getCidrSize(), cmd.getAsNumber(), bgpPeerIds);
 
         String sourceNatIP = cmd.getSourceNatIP();
-        boolean forNsx = isVpcForNsx(vpc);
+        boolean forNsx = isVpcForProvider(Provider.Nsx, vpc);
+        boolean forNetris = isVpcForProvider(Provider.Netris, vpc);
         try {
-            if (sourceNatIP != null || forNsx) {
-                if (forNsx) {
-                    logger.info("Provided source NAT IP will be ignored in an NSX-enabled zone");
+            if (sourceNatIP != null || forNsx || forNetris) {
+                if (forNsx || forNetris) {
+                    logger.info("Provided source NAT IP will be ignored in an NSX-enabled or Netris-enabled zone");
                     sourceNatIP = null;
                 }
                 logger.info(String.format("Trying to allocate the specified IP [%s] as the source NAT of VPC [%s].", sourceNatIP, vpc));
@@ -1341,15 +1345,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         return NetworkOffering.RoutingMode.Dynamic == vpcOffering.getRoutingMode();
     }
 
-    private boolean isVpcForNsx(Vpc vpc) {
+    private boolean isVpcForProvider(Provider provider, Vpc vpc) {
         if (vpc == null) {
             return false;
         }
-        VpcOfferingServiceMapVO mapVO = _vpcOffSvcMapDao.findByServiceProviderAndOfferingId(Service.SourceNat.getName(), Provider.Nsx.getName(), vpc.getVpcOfferingId());
-        if (mapVO != null) {
-            logger.debug(String.format("The VPC %s is NSX-based and supports the %s service", vpc.getName(), Service.SourceNat.getName()));
-        }
-        return mapVO != null;
+        return _vpcOffSvcMapDao.isProviderForVpcOffering(provider, vpc.getVpcOfferingId());
     }
 
     private void allocateSourceNatIp(Vpc vpc, String sourceNatIP) {
@@ -1357,9 +1357,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         DataCenter zone = _dcDao.findById(vpc.getZoneId());
         // reserve this ip and then
         try {
-            if (isVpcForNsx(vpc) && org.apache.commons.lang3.StringUtils.isBlank(sourceNatIP)) {
+            if (isVpcForProvider(Provider.Nsx, vpc) && org.apache.commons.lang3.StringUtils.isBlank(sourceNatIP)) {
                 logger.debug(String.format("Reserving a source NAT IP for NSX VPC %s", vpc.getName()));
-                sourceNatIP = reserveSourceNatIpForNsxVpc(account, zone);
+                sourceNatIP = reserveSourceNatIpForProviderVpc(account, zone, Provider.Nsx);
+            } else if (isVpcForProvider(Provider.Netris, vpc) && org.apache.commons.lang3.StringUtils.isBlank(sourceNatIP)) {
+                logger.debug(String.format("Reserving a source NAT IP for Netris VPC %s", vpc.getName()));
+                sourceNatIP = reserveSourceNatIpForProviderVpc(account, zone, Provider.Netris);
             }
             IpAddress ip = _ipAddrMgr.allocateIp(account, false, CallContext.current().getCallingAccount(), CallContext.current().getCallingUserId(), zone, null, sourceNatIP);
             this.associateIPToVpc(ip.getId(), vpc.getId());
@@ -1368,8 +1371,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         }
     }
 
-    private String reserveSourceNatIpForNsxVpc(Account account, DataCenter zone) throws ResourceAllocationException {
-        IpAddress ipAddress = _ntwkSvc.reserveIpAddressWithVlanDetail(account, zone, true, ApiConstants.NSX_DETAIL_KEY);
+    private String reserveSourceNatIpForProviderVpc(Account account, DataCenter zone, Provider provider) throws ResourceAllocationException {
+        String detailKey = provider == Provider.Nsx ? ApiConstants.NSX_DETAIL_KEY : ApiConstants.NETRIS_DETAIL_KEY;
+        IpAddress ipAddress = _ntwkSvc.reserveIpAddressWithVlanDetail(account, zone, true, detailKey);
         return ipAddress.getAddress().addr();
     }
 
@@ -1571,7 +1575,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                 }
             }
             return vpcDao.findById(vpcId);
-        } else if (isVpcForNsx(vpcToUpdate)) {
+        } else if (isVpcForProvider(Provider.Nsx, vpcToUpdate)) {
             if (logger.isDebugEnabled()) {
                 logger.debug("no restart needed.");
             }
@@ -1590,7 +1594,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         if (! userIps.isEmpty()) {
             try {
                 _ipAddrMgr.updateSourceNatIpAddress(requestedIp, userIps);
-                if (isVpcForNsx(vpc)) {
+                if (isVpcForProvider(Provider.Nsx, vpc) || isVpcForProvider(Provider.Netris, vpc)) {
                     VpcProvider nsxElement = (VpcProvider) _ntwkModel.getElementImplementingProvider(Provider.Nsx.getName());
                     if (nsxElement == null) {
                         return true;
@@ -2053,7 +2057,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
 
         // 5) When aclId is provided, verify that ACLProvider is supported by
         // network offering
-        if (aclId != null && !_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.NetworkACL) && !guestNtwkOff.isForNsx()) {
+        boolean isForNsx = _ntwkModel.isProviderForNetworkOffering(Provider.Nsx, guestNtwkOff.getId());
+        if (aclId != null && !_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.NetworkACL) && !isForNsx) {
             throw new InvalidParameterValueException("Cannot apply NetworkACL. Network Offering does not support NetworkACL service");
         }
 
@@ -2071,7 +2076,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
 
         // 2) Only Isolated networks with Source nat service enabled can be
         // added to vpc
-        if (!guestNtwkOff.isForNsx()
+        boolean isForNsx = _ntwkModel.isProviderForNetworkOffering(Provider.Nsx, guestNtwkOff.getId());
+        boolean isForNNetris = _ntwkModel.isProviderForNetworkOffering(Provider.Netris, guestNtwkOff.getId());
+        if (!isForNsx && !isForNNetris
                 && !(guestNtwkOff.getGuestType() == GuestType.Isolated && (supportedSvcs.contains(Service.SourceNat) || supportedSvcs.contains(Service.Gateway)))) {
 
             throw new InvalidParameterValueException("Only network offerings of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName()
@@ -3196,7 +3203,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
 
         logger.debug("Associating ip " + ipToAssoc + " to vpc " + vpc);
 
-        final boolean isSourceNatFinal = isSrcNatIpRequired(vpc.getVpcOfferingId()) && getExistingSourceNatInVpc(vpc.getAccountId(), vpcId, false) == null;
+        final boolean isSourceNatFinal = isSrcNatIpRequired(vpc.getVpcOfferingId()) && getExistingSourceNatInVpc(vpc.getAccountId(), vpcId, false, false) == null;
         try (CheckedReservation publicIpReservation = new CheckedReservation(owner, ResourceType.public_ip, 1l, reservationDao, _resourceLimitMgr)) {
             Transaction.execute(new TransactionCallbackNoReturn() {
                 @Override
@@ -3298,7 +3305,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         return guestNetwork;
     }
 
-    protected IPAddressVO getExistingSourceNatInVpc(final long ownerId, final long vpcId, final boolean forNsx) {
+    protected IPAddressVO getExistingSourceNatInVpc(final long ownerId, final long vpcId, final boolean forNsx, final boolean forNetris) {
 
         final List<IPAddressVO> addrs = listPublicIpsAssignedToVpc(ownerId, true, vpcId);
 
@@ -3309,7 +3316,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
             // Account already has ip addresses
             for (final IPAddressVO addr : addrs) {
                 if (addr.isSourceNat()) {
-                    if (!forNsx) {
+                    if (!forNsx && !forNetris) {
                         sourceNatIp = addr;
                     } else {
                         if (addr.isForSystemVms()) {
@@ -3346,15 +3353,17 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         final long dcId = vpc.getZoneId();
         NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(dcId);
         boolean forNsx = nsxProvider != null;
+        NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(dcId);
+        boolean forNetris = netrisProvider != null;
 
-        final IPAddressVO sourceNatIp = getExistingSourceNatInVpc(owner.getId(), vpc.getId(), forNsx);
+        final IPAddressVO sourceNatIp = getExistingSourceNatInVpc(owner.getId(), vpc.getId(), forNsx, forNetris);
 
         PublicIp ipToReturn = null;
 
         if (sourceNatIp != null) {
             ipToReturn = PublicIp.createFromAddrAndVlan(sourceNatIp, _vlanDao.findById(sourceNatIp.getVlanId()));
         } else {
-            if (forNsx) {
+            if (forNsx || forNetris) {
                 ipToReturn = _ipAddrMgr.assignPublicIpAddress(dcId, podId, owner, Vlan.VlanType.VirtualNetwork, null, null, false, true);
             } else {
                 ipToReturn = _ipAddrMgr.assignDedicateIpAddress(owner, null, vpc.getId(), dcId, true);
@@ -3398,7 +3407,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         final Map<Network.Service, Set<Network.Provider>> vpcOffSvcProvidersMap = getVpcOffSvcProvidersMap(vpcOfferingId);
         return (Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.SourceNat))
                 && (vpcOffSvcProvidersMap.get(Network.Service.SourceNat).contains(Network.Provider.VPCVirtualRouter)
-                || vpcOffSvcProvidersMap.get(Service.SourceNat).contains(Provider.Nsx)))
+                || vpcOffSvcProvidersMap.get(Service.SourceNat).contains(Provider.Nsx)
+                || vpcOffSvcProvidersMap.get(Service.SourceNat).contains(Provider.Netris)))
                 || (Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.Gateway))
                 && vpcOffSvcProvidersMap.get(Service.Gateway).contains(Network.Provider.VPCVirtualRouter));
     }
diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
index b4715974d2d..f21b84cdc02 100644
--- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
@@ -1055,8 +1055,6 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
                 NetworkOfferingVO defaultTungstenSharedSGNetworkOffering =
                         new NetworkOfferingVO(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE, "Offering for Tungsten Shared Security group enabled networks",
                                 TrafficType.Guest, false, true, null, null, true, Availability.Optional, null, Network.GuestType.Shared, true, true, false, false, false, false);
-
-                defaultTungstenSharedSGNetworkOffering.setForTungsten(true);
                 defaultTungstenSharedSGNetworkOffering.setState(NetworkOffering.State.Enabled);
                 defaultTungstenSharedSGNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultTungstenSharedSGNetworkOffering);
 
@@ -1234,7 +1232,6 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
                         false, false, false, false, forVpc);
         defaultNatProviderNetworkOffering.setPublicLb(publicLB);
         defaultNatProviderNetworkOffering.setInternalLb(!publicLB);
-        defaultNatProviderNetworkOffering.setForNsx(Provider.Nsx.equals(provider));
         defaultNatProviderNetworkOffering.setNetworkMode(networkMode);
         defaultNatProviderNetworkOffering.setState(NetworkOffering.State.Enabled);
         defaultNatProviderNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultNatProviderNetworkOffering);
diff --git a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java
index 817cfe07e58..016f52b8ade 100644
--- a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java
+++ b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java
@@ -23,14 +23,17 @@ import java.util.Objects;
 
 import com.cloud.dc.DataCenter;
 import com.cloud.dc.Vlan;
+import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NetworkDetailVO;
 import com.cloud.network.dao.NetworkDetailsDao;
 import com.cloud.network.dao.NsxProviderDao;
+import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.element.NsxProviderVO;
 import com.cloud.network.router.VirtualRouter;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.dao.DiskOfferingDao;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.LogManager;
 
@@ -93,6 +96,7 @@ public class RouterDeploymentDefinition {
     protected NetworkDao networkDao;
     protected DomainRouterDao routerDao;
     protected NsxProviderDao nsxProviderDao;
+    protected NetrisProviderDao netrisProviderDao;
     protected PhysicalNetworkServiceProviderDao physicalProviderDao;
     protected NetworkModel networkModel;
     protected VirtualRouterProviderDao vrProviderDao;
@@ -395,10 +399,11 @@ public class RouterDeploymentDefinition {
         if (Objects.nonNull(zone)) {
             zoneId = zone.getId();
         }
-        NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId);
+
+        boolean isExternalProvider = isExternalProviderPresent(zoneId);
 
         if (isPublicNetwork) {
-            if (Objects.isNull(nsxProvider)) {
+            if (!isExternalProvider) {
                 sourceNatIp = ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, guestNetwork);
             } else {
                 sourceNatIp = ipAddrMgr.assignPublicIpAddress(zoneId, getPodId(), owner, Vlan.VlanType.VirtualNetwork, null, null, false, true);
@@ -406,6 +411,16 @@ public class RouterDeploymentDefinition {
         }
     }
 
+    protected boolean isExternalProviderPresent(Long zoneId) {
+        NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId);
+        NetrisProviderVO netrisProviderVO = netrisProviderDao.findByZoneId(zoneId);
+
+        if (ObjectUtils.anyNotNull(nsxProvider, netrisProviderVO)) {
+            return true;
+        }
+        return false;
+    }
+
     protected void findDefaultServiceOfferingId() {
         ServiceOfferingVO serviceOffering = serviceOfferingDao.findDefaultSystemOffering(ServiceOffering.routerDefaultOffUniqueName, ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dest.getDataCenter().getId()));
         serviceOfferingId = serviceOffering.getId();
diff --git a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionBuilder.java b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionBuilder.java
index 227ae8d641e..de95956a406 100644
--- a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionBuilder.java
+++ b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionBuilder.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import javax.inject.Inject;
 
+import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NetworkDetailsDao;
 import com.cloud.network.dao.NsxProviderDao;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -66,6 +67,8 @@ public class RouterDeploymentDefinitionBuilder {
     @Inject
     private NsxProviderDao nsxProviderDao;
     @Inject
+    private NetrisProviderDao netrisProviderDao;
+    @Inject
     private PhysicalNetworkServiceProviderDao physicalProviderDao;
     @Inject
     private NetworkModel networkModel;
@@ -129,6 +132,7 @@ public class RouterDeploymentDefinitionBuilder {
         routerDeploymentDefinition.networkDao = networkDao;
         routerDeploymentDefinition.routerDao = routerDao;
         routerDeploymentDefinition.nsxProviderDao = nsxProviderDao;
+        routerDeploymentDefinition.netrisProviderDao = netrisProviderDao;
         routerDeploymentDefinition.physicalProviderDao = physicalProviderDao;
         routerDeploymentDefinition.networkModel = networkModel;
         routerDeploymentDefinition.vrProviderDao = vrProviderDao;
diff --git a/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java b/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java
index 405575c65b1..5d5798f60e6 100644
--- a/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java
+++ b/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java
@@ -23,7 +23,6 @@ import java.util.Objects;
 
 import com.cloud.dc.DataCenter;
 import com.cloud.network.dao.IPAddressVO;
-import com.cloud.network.element.NsxProviderVO;
 
 import com.cloud.dc.dao.VlanDao;
 import com.cloud.deploy.DataCenterDeployment;
@@ -127,10 +126,11 @@ public class VpcRouterDeploymentDefinition extends RouterDeploymentDefinition {
         if (Objects.nonNull(zone)) {
             zoneId = zone.getId();
         }
-        NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId);
+
+        boolean isExternalProvider = isExternalProviderPresent(zoneId);
 
         if (isPublicNetwork) {
-            if (Objects.isNull(nsxProvider)) {
+            if (!isExternalProvider) {
                 sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc, null);
             } else {
                 sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc, getPodId());
diff --git a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java
index 889031f2b50..70bbe2d1b80 100644
--- a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java
+++ b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java
@@ -30,6 +30,7 @@ import com.cloud.offerings.NetworkOfferingVO;
 import com.cloud.offerings.dao.NetworkOfferingDao;
 import com.cloud.network.vpc.VpcVO;
 import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
 import com.cloud.utils.Pair;
 import com.cloud.utils.net.Ip;
 import org.junit.Assert;
@@ -44,6 +45,7 @@ import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.springframework.test.util.ReflectionTestUtils;
 
+import javax.inject.Inject;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -60,6 +62,8 @@ public class NetworkModelImplTest {
 
     @Mock
     private VpcDao vpcDao;
+    @Inject
+    private NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
 
     @InjectMocks
     private NetworkModelImpl networkModel = new NetworkModelImpl();
@@ -70,8 +74,10 @@ public class NetworkModelImplTest {
     public void setUp() {
         networkOfferingDao = Mockito.mock(NetworkOfferingDao.class);
         networkServiceMapDao = Mockito.mock(NetworkServiceMapDao.class);
+        networkOfferingServiceMapDao = Mockito.mock(NetworkOfferingServiceMapDao.class);
         networkModel._networkOfferingDao = networkOfferingDao;
         networkModel._ntwkSrvcDao = networkServiceMapDao;
+        networkModel._ntwkOfferingSrvcDao = networkOfferingServiceMapDao;
     }
 
     private void prepareMocks(boolean isIp6, Network network, DataCenter zone, VpcVO vpc,
@@ -216,7 +222,6 @@ public class NetworkModelImplTest {
         PublicIpAddress publicIpAddress2 = new PublicIp(ipAddressVO2, vlanVO, 0x0ac00000L);
         NetworkOfferingVO networkOfferingVO = new NetworkOfferingVO();
         networkOfferingVO.setForVpc(true);
-        networkOfferingVO.setForNsx(false);
         Network network = new NetworkVO();
         List<NetworkServiceMapVO> networkServiceMapVOs = new ArrayList<>();
         networkServiceMapVOs.add(new NetworkServiceMapVO(15L, Network.Service.Firewall, Network.Provider.VPCVirtualRouter));
@@ -229,6 +234,7 @@ public class NetworkModelImplTest {
         Map<PublicIpAddress, Set<Network.Service>> ipToServices = new HashMap<>();
         ipToServices.put(publicIpAddress1, services1);
         ipToServices.put(publicIpAddress2, services2);
+        Mockito.when(networkOfferingServiceMapDao.isProviderForNetworkOffering(networkOfferingVO.getId(), Network.Provider.Nsx)).thenReturn(false);
         Map<Network.Provider, ArrayList<PublicIpAddress>> result = networkModel.getProviderToIpList(network, ipToServices);
         Assert.assertNotNull(result);
     }
diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java
index 8f05b716725..9b0822cf0b1 100644
--- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java
+++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java
@@ -558,7 +558,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
      */
     @Override
     public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, boolean forSystemVms, Long podId, String startIP, String endIP,
-                                           String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6, boolean forNsx)
+                                           String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6, Provider provider)
         throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException {
         // TODO Auto-generated method stub
         return null;
diff --git a/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java
index 5685c504ab2..3d45d8e839f 100644
--- a/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java
+++ b/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java
@@ -16,6 +16,7 @@
 // under the License.
 package com.cloud.vpc.dao;
 
+import com.cloud.network.Network;
 import com.cloud.network.Network.Service;
 import com.cloud.network.vpc.VpcOfferingServiceMapVO;
 import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
@@ -62,6 +63,11 @@ public class MockVpcOfferingServiceMapDaoImpl extends GenericDaoBase<VpcOffering
         return new VpcOfferingServiceMapVO();
     }
 
+    @Override
+    public boolean isProviderForVpcOffering(Network.Provider provider, long vpcOffering) {
+        return false;
+    }
+
     @Override
     public VpcOfferingServiceMapVO persist(VpcOfferingServiceMapVO vo) {
         return vo;
diff --git a/server/src/test/resources/createNetworkOffering.xml b/server/src/test/resources/createNetworkOffering.xml
index 5ee4f176847..17afd500191 100644
--- a/server/src/test/resources/createNetworkOffering.xml
+++ b/server/src/test/resources/createNetworkOffering.xml
@@ -73,6 +73,7 @@
     <bean id="configurationGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationGroupDaoImpl" />
     <bean id="configurationSubGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl" />
     <bean id="nsxControllerDaoImpl" class="com.cloud.network.dao.NsxProviderDaoImpl" />
+    <bean id="netrisProviderDaoImpl" class="com.cloud.network.dao.NetrisProviderDaoImpl" />
     <bean id="vlanDetailsDao" class="com.cloud.dc.dao.VlanDetailsDaoImpl" />
     <bean id="publicIpQuarantineDaoImpl" class="com.cloud.network.dao.PublicIpQuarantineDaoImpl" />
     <bean id="reservationDao" class="org.apache.cloudstack.reservation.dao.ReservationDaoImpl" />
diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py
index 49ce94e86fa..6894caf3702 100644
--- a/tools/apidoc/gen_toc.py
+++ b/tools/apidoc/gen_toc.py
@@ -98,6 +98,9 @@ known_categories = {
     'listNsxControllers': 'NSX',
     'addNsxController': 'NSX',
     'deleteNsxController': 'NSX',
+    'addNetrisProvider': 'Netris',
+    'listNetrisProviders': 'Netris',
+    'deleteNetrisProvider': 'Netris',
     'Vpn': 'VPN',
     'Limit': 'Resource Limit',
     'Netscaler': 'Netscaler',
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 43e8407fe9c..9ac5d54f98e 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1491,6 +1491,8 @@
 "label.netris.provider.port": "Netris provider port",
 "label.netris.provider.username": "Netris provider username",
 "label.netris.provider.password": "Netris provider password",
+"label.netris.provider.site": "Netris provider Site name",
+"label.netris.provider.tenant.name": "Netris provider Admin Tenant name",
 "label.netscaler": "NetScaler",
 "label.netscaler.mpx": "NetScaler MPX LoadBalancer",
 "label.netscaler.sdx": "NetScaler SDX LoadBalancer",
@@ -2077,6 +2079,7 @@
 "label.shutdown": "Shutdown",
 "label.shutdown.provider": "Shutdown provider",
 "label.simplified.chinese.keyboard": "Simplified Chinese keyboard",
+"label.site": "Netris Site",
 "label.site.to.site.vpn": "Site-to-site VPN",
 "label.site.to.site.vpn.connections": "Site-to-site VPN Connections",
 "label.size": "Size",
@@ -2265,6 +2268,7 @@
 "label.templatesubject": "Subject",
 "label.templatetype": "Template type",
 "label.templateversion": "Template version",
+"label.tenantname": "Netris Tenant",
 "label.term.type": "Term type",
 "label.test": "Test",
 "label.test.webhook.delivery": "Test Webhook Delivery",
@@ -2745,6 +2749,7 @@
 "message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule",
 "message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...",
 "message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule",
+"message.add.netris.controller": "Add Netris Provider",
 "message.add.nsx.controller": "Add NSX Provider",
 "message.add.network": "Add a new network for zone: <b><span id=\"zone_name\"></span></b>",
 "message.add.network.acl.failed": "Adding network ACL list failed.",
@@ -2832,6 +2837,7 @@
 "message.configuring.guest.traffic": "Configuring guest traffic",
 "message.configuring.physical.networks": "Configuring physical Networks",
 "message.configuring.public.traffic": "Configuring public traffic",
+"message.configuring.netris.public.traffic": "Configuring Netris public traffic",
 "message.configuring.nsx.public.traffic": "Configuring NSX public traffic",
 "message.configuring.storage.traffic": "Configuring storage traffic",
 "message.confirm.action.force.reconnect": "Please confirm that you want to force reconnect this host.",
@@ -3221,6 +3227,8 @@
 "message.installwizard.tooltip.netris.provider.hostname": "Netris Provider hostname / IP address not provided",
 "message.installwizard.tooltip.netris.provider.username": "Netris Provider username not provided",
 "message.installwizard.tooltip.netris.provider.password": "Netris Provider password not provided",
+"message.installwizard.tooltip.netris.provider.site": "Netris Provider Site name not provided",
+"message.installwizard.tooltip.netris.provider.tenant.name": "Netris Provider Admin Tenant name not provided",
 "message.installwizard.tooltip.nsx.provider.hostname": "NSX Provider hostname / IP address not provided",
 "message.installwizard.tooltip.nsx.provider.username": "NSX Provider username not provided",
 "message.installwizard.tooltip.nsx.provider.password": "NSX Provider password not provided",
diff --git a/ui/src/config/section/infra/phynetworks.js b/ui/src/config/section/infra/phynetworks.js
index 578a12516fa..977fc984d2c 100644
--- a/ui/src/config/section/infra/phynetworks.js
+++ b/ui/src/config/section/infra/phynetworks.js
@@ -57,7 +57,7 @@ export default {
       args: ['name', 'zoneid', 'isolationmethods', 'vlan', 'tags', 'networkspeed', 'broadcastdomainrange'],
       mapping: {
         isolationmethods: {
-          options: ['VLAN', 'VXLAN', 'GRE', 'STT', 'BCF_SEGMENT', 'SSP', 'ODL', 'L3VPN', 'VCS']
+          options: ['VLAN', 'VXLAN', 'GRE', 'STT', 'BCF_SEGMENT', 'SSP', 'ODL', 'L3VPN', 'VCS', 'NSX', 'NETRIS']
         }
       }
     },
diff --git a/ui/src/views/infra/network/ServiceProvidersTab.vue b/ui/src/views/infra/network/ServiceProvidersTab.vue
index 5eb8e3dbcce..16c965763c3 100644
--- a/ui/src/views/infra/network/ServiceProvidersTab.vue
+++ b/ui/src/views/infra/network/ServiceProvidersTab.vue
@@ -1128,6 +1128,50 @@ export default {
               columns: ['name', 'hostname', 'port', 'tier0gateway', 'edgecluster', 'transportzone']
             }
           ]
+        },
+        {
+          title: 'Netris',
+          details: ['name', 'state', 'id', 'physicalnetworkid', 'servicelist'],
+          actions: [
+            {
+              api: 'updateNetworkServiceProvider',
+              icon: 'stop-outlined',
+              listView: true,
+              label: 'label.disable.provider',
+              confirm: 'message.confirm.disable.provider',
+              show: (record) => { return (record && record.id && record.state === 'Enabled') },
+              mapping: {
+                state: {
+                  value: (record) => { return 'Disabled' }
+                }
+              }
+            },
+            {
+              api: 'updateNetworkServiceProvider',
+              icon: 'play-circle-outlined',
+              listView: true,
+              label: 'label.enable.provider',
+              confirm: 'message.confirm.enable.provider',
+              show: (record) => { return (record && record.id && record.state === 'Disabled') },
+              mapping: {
+                state: {
+                  value: (record) => { return 'Enabled' }
+                }
+              }
+            }
+          ],
+          lists: [
+            {
+              title: 'label.netris.provider',
+              api: 'listNetrisProviders',
+              mapping: {
+                zoneid: {
+                  value: (record) => { return record.zoneid }
+                }
+              },
+              columns: ['name', 'hostname', 'port', 'site', 'tenantname']
+            }
+          ]
         }
       ]
     }
@@ -1168,7 +1212,6 @@ export default {
       this.fetchLoading = true
       api('listNetworkServiceProviders', { physicalnetworkid: this.resource.id, name: name }).then(json => {
         const sps = json.listnetworkserviceprovidersresponse.networkserviceprovider || []
-        console.log(sps)
         if (sps.length > 0) {
           for (const sp of sps) {
             this.nsps[sp.name] = sp
diff --git a/ui/src/views/infra/network/TrafficTypesTab.vue b/ui/src/views/infra/network/TrafficTypesTab.vue
index 41da48d993c..a4bc20e4dbe 100644
--- a/ui/src/views/infra/network/TrafficTypesTab.vue
+++ b/ui/src/views/infra/network/TrafficTypesTab.vue
@@ -21,7 +21,7 @@
       <a-tab-pane v-for="(item, index) in traffictypes" :tab="item.traffictype" :key="index">
         <a-popconfirm
           :title="$t('message.confirm.delete.traffic.type')"
-          @confirm="deleteTrafficType(itemd)"
+          @confirm="deleteTrafficType(item)"
           :okText="$t('label.yes')"
           :cancelText="$t('label.no')" >
           <a-button
diff --git a/ui/src/views/infra/zone/PhysicalNetworksTab.vue b/ui/src/views/infra/zone/PhysicalNetworksTab.vue
index 932fdf09f06..6880c30802c 100644
--- a/ui/src/views/infra/zone/PhysicalNetworksTab.vue
+++ b/ui/src/views/infra/zone/PhysicalNetworksTab.vue
@@ -200,7 +200,7 @@ export default {
   },
   computed: {
     isolationMethods () {
-      return ['VLAN', 'VXLAN', 'GRE', 'STT', 'BCF_SEGMENT', 'SSP', 'ODL', 'L3VPN', 'VCS']
+      return ['VLAN', 'VXLAN', 'GRE', 'STT', 'BCF_SEGMENT', 'SSP', 'ODL', 'L3VPN', 'VCS', 'NSX', 'NETRIS']
     }
   },
   methods: {
diff --git a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
index 91b884072a7..8e773e196c7 100644
--- a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
+++ b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
@@ -472,7 +472,6 @@ export default {
           if (physicalNetwork.tags) {
             params.tags = physicalNetwork.tags
           }
-
           try {
             if (!this.stepData.stepMove.includes('createPhysicalNetwork' + index)) {
               const physicalNetworkResult = await this.createPhysicalNetwork(params)
@@ -490,7 +489,7 @@ export default {
                 physicalNetwork.traffics.findIndex(traffic => traffic.type === 'public' || traffic.type === 'guest') > -1) {
                 this.stepData.isNsxZone = true
               }
-              if (physicalNetwork.isolationMethod === 'NETRIS' &&
+              if (physicalNetwork.isolationMethod.toLowerCase() === 'netris' &&
                 physicalNetwork.traffics.findIndex(traffic => traffic.type === 'public' || traffic.type === 'guest') > -1) {
                 this.stepData.isNetrisZone = true
               }
@@ -921,7 +920,7 @@ export default {
         let stopNow = false
         this.stepData.returnedPublicTraffic = this.stepData?.returnedPublicTraffic || []
         let publicIpRanges = this.prefillContent['public-ipranges']
-        publicIpRanges = publicIpRanges.filter(item => item.fornsx === (idx === 1))
+        publicIpRanges = publicIpRanges.filter(item => (item.fornsx || item.fornetris) === (idx === 1))
         for (let index = 0; index < publicIpRanges.length; index++) {
           const publicVlanIpRange = publicIpRanges[index]
           let isExisting = false
@@ -944,16 +943,22 @@ export default {
           params.zoneId = this.stepData.zoneReturned.id
           if (publicVlanIpRange.vlan && publicVlanIpRange.vlan.length > 0) {
             params.vlan = publicVlanIpRange.vlan
-          } else if (publicVlanIpRange.fornsx) { // TODO: should this be the same for Netris?
+          } else if (publicVlanIpRange.fornsx || publicVlanIpRange.fornetris) { // TODO: should this be the same for Netris?
             params.vlan = null
           } else {
             params.vlan = 'untagged'
           }
+          let provider = null
+          if (publicVlanIpRange.fornsx) {
+            provider = 'Nsx'
+          } else if (publicVlanIpRange.fornetris) {
+            provider = 'Netris'
+          }
           params.gateway = publicVlanIpRange.gateway
           params.netmask = publicVlanIpRange.netmask
           params.startip = publicVlanIpRange.startIp
           params.endip = publicVlanIpRange.endIp
-          params.fornsx = publicVlanIpRange.fornsx
+          params.provider = provider
           params.forsystemvms = publicVlanIpRange.forsystemvms
 
           if (this.isBasicZone) {
@@ -986,16 +991,20 @@ export default {
         if (stopNow) {
           return
         }
-
         if (idx === 0) {
-          await this.stepConfigurePublicTraffic('message.configuring.nsx.public.traffic', 'nsxPublicTraffic', 1)
+          const isolationMethods = Object.values(this.stepData.physicalNetworkItem).map(network => network.isolationmethods.toLowerCase())
+          if (isolationMethods.includes('nsx')) {
+            await this.stepConfigurePublicTraffic('message.configuring.nsx.public.traffic', 'nsxPublicTraffic', 1)
+          } else if (isolationMethods.includes('netris')) {
+            await this.stepConfigurePublicTraffic('message.configuring.netris.public.traffic', 'netrisPublicTraffic', 1)
+          }
         } else {
           if (this.stepData.isTungstenZone) {
             await this.stepCreateTungstenFabricPublicNetwork()
           } else if (this.stepData.isNsxZone) {
             await this.stepAddNsxController()
           } else if (this.stepData.isNetrisZone) {
-            await this.stepAddNetrisController()
+            await this.stepAddNetrisProvider()
           } else {
             await this.stepConfigureStorageTraffic()
           }
@@ -1099,7 +1108,7 @@ export default {
         this.setStepStatus(STATUS_FAILED)
       }
     },
-    async stepAddNetrisController () {
+    async stepAddNetrisProvider () {
       this.setStepStatus(STATUS_FINISH)
       this.currentStep++
       this.addStep('message.add.netris.controller', 'netris')
@@ -1111,14 +1120,16 @@ export default {
         if (!this.stepData.stepMove.includes('addNetrisProvider')) {
           const providerParams = {}
           providerParams.name = this.prefillContent?.netrisName || ''
-          providerParams.netrisproviderhostname = this.prefillContent?.netrisHostname || ''
-          providerParams.netrisproviderport = this.prefillContent?.netrisPort || ''
+          providerParams.hostname = this.prefillContent?.hostname || ''
+          providerParams.port = this.prefillContent?.netrisPort || ''
           providerParams.username = this.prefillContent?.username || ''
           providerParams.password = this.prefillContent?.password || ''
           providerParams.zoneid = this.stepData.zoneReturned.id
+          providerParams.sitename = this.prefillContent?.siteName || ''
+          providerParams.tenantname = this.prefillContent?.tenantName || ''
 
           await this.addNetrisProvider(providerParams)
-          this.stepData.stepMove.push('addNetrisController')
+          this.stepData.stepMove.push('addNetrisProvider')
         }
         this.stepData.stepMove.push('netris')
         await this.stepConfigureStorageTraffic()
@@ -2285,7 +2296,7 @@ export default {
         })
       })
     },
-    addNetrisPovider (args) {
+    addNetrisProvider (args) {
       return new Promise((resolve, reject) => {
         api('addNetrisProvider', {}, 'POST', args).then(json => {
           resolve()
diff --git a/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue b/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue
index 3bc84bab37f..863bb6a375c 100644
--- a/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue
+++ b/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue
@@ -129,7 +129,7 @@
     </div>
     <div v-else>
       <advanced-guest-traffic-form
-        v-if="steps && steps[currentStep].formKey === 'guestTraffic' && !isNsxZone && !isNetrisZone"
+        v-if="steps && steps[currentStep].formKey === 'guestTraffic' && !isNsxZone"
         @nextPressed="nextPressed"
         @backPressed="handleBack"
         @fieldsChanged="fieldsChanged"
@@ -290,7 +290,7 @@ export default {
         title: 'label.pod',
         formKey: 'pod'
       })
-      if (!this.isTungstenZone && !this.isNsxZone && !this.isNetrisZone) {
+      if (!this.isTungstenZone && !this.isNsxZone) {
         steps.push({
           title: 'label.guest.traffic',
           formKey: 'guestTraffic',
@@ -485,7 +485,7 @@ export default {
         },
         {
           title: 'label.netris.provider.hostname',
-          key: 'netrisHostname',
+          key: 'hostname',
           placeHolder: 'message.installwizard.tooltip.netris.provider.hostname',
           required: true
         },
@@ -507,6 +507,18 @@ export default {
           placeHolder: 'message.installwizard.tooltip.netris.provider.password',
           required: true,
           password: true
+        },
+        {
+          title: 'label.netris.provider.site',
+          key: 'siteName',
+          placeHolder: 'message.installwizard.tooltip.netris.provider.site',
+          required: true
+        },
+        {
+          title: 'label.netris.provider.tenant.name',
+          key: 'tenantName',
+          placeHolder: 'message.installwizard.tooltip.netris.provider.tenant.name',
+          required: true
         }
       ]
       return fields
@@ -631,7 +643,7 @@ export default {
     }
     this.scrollToStepActive()
     if (this.zoneType === 'Basic' ||
-      (this.zoneType === 'Advanced' && (this.sgEnabled || this.isNsxZone || this.isNetrisZone))) {
+      (this.zoneType === 'Advanced' && (this.sgEnabled || this.isNsxZone))) {
       this.skipGuestTrafficStep = false
     } else {
       this.fetchConfiguration()
diff --git a/ui/src/views/network/VpcTiersTab.vue b/ui/src/views/network/VpcTiersTab.vue
index e9eaddf149e..2f194e60784 100644
--- a/ui/src/views/network/VpcTiersTab.vue
+++ b/ui/src/views/network/VpcTiersTab.vue
@@ -457,6 +457,7 @@ export default {
       publicLBExists: false,
       setMTU: false,
       isNsxEnabled: false,
+      zoneExtNetProvider: null,
       isOfferingNatMode: false,
       isOfferingRoutedMode: false,
       displayCollapsible: [],
@@ -533,6 +534,7 @@ export default {
         this.setMTU = json?.listzonesresponse?.zone?.[0]?.allowuserspecifyvrmtu || false
         this.privateMtuMax = json?.listzonesresponse?.zone?.[0]?.routerprivateinterfacemaxmtu || 1500
         this.isNsxEnabled = json?.listzonesresponse?.zone?.[0]?.isnsxenabled || false
+        this.zoneExtNetProvider = json?.listzonesresponse?.zone?.[0]?.provider || null
       })
     },
     fetchNetworkAclList () {
@@ -612,7 +614,7 @@ export default {
         guestiptype: 'Isolated',
         state: 'Enabled'
       }
-      if (!this.isNsxEnabled && !this.isOfferingRoutedMode) {
+      if ((!this.isNsxEnabled || !['netris', 'nsx'].includes(this.zoneExtNetProvider.toLowerCase())) && !this.isOfferingRoutedMode) {
         params.supportedServices = 'SourceNat'
       }
       api('listNetworkOfferings', params).then(json => {
@@ -635,7 +637,7 @@ export default {
           }
         }
         this.networkOfferings = filteredOfferings
-        if (this.isNsxEnabled) {
+        if (this.isNsxEnabled || ['netris', 'nsx'].includes(this.zoneExtNetProvider.toLowerCase())) {
           this.networkOfferings = this.networkOfferings.filter(offering => offering.networkmode === (this.isOfferingNatMode ? 'NATTED' : 'ROUTED'))
         }
         if (this.resource.asnumberid) {
diff --git a/utils/src/test/java/com/cloud/utils/LogUtilsTest.java b/utils/src/test/java/com/cloud/utils/LogUtilsTest.java
index 350c9e946cd..e369c0fcac6 100644
--- a/utils/src/test/java/com/cloud/utils/LogUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/LogUtilsTest.java
@@ -24,7 +24,7 @@ public class LogUtilsTest {
 
     @Test
     public void logGsonWithoutExceptionTestLogCorrectlyPrimitives() {
-        String expected = "test primitives: int [1], double [1.11], float [1.2222], boolean [true], null [], char [\"c\"].";
+        String expected = "test primitives: int [1], double [1.11], float [1.2222], boolean [true], null [null], char [\"c\"].";
         String log = LogUtils.logGsonWithoutException("test primitives: int [%s], double [%s], float [%s], boolean [%s], null [%s], char [%s].",
                 1, 1.11d, 1.2222f, true, null, 'c');
         assertEquals(expected, log);


(cloudstack) 21/44: Revert UI filtration for public IPs (#29)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 6157c8f70c52bf4f741121d5dfa08ef8c7ffd659
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Fri Dec 6 13:46:26 2024 -0500

     Revert UI filtration for public IPs (#29)
    
    * Fix issue with pagination of public addresses listed after filtering for external providers
    
    * Revert UI filteration for public IPs for external network provider enabled zones
---
 ui/src/views/AutogenView.vue | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue
index 54524ccc6b8..7083787a908 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -1001,19 +1001,6 @@ export default {
         }
 
         this.items = json[responseName][objectName]
-        var filteredItems = []
-        if (this.apiName === 'listPublicIpAddresses') {
-          for (var zone of this.$store.getters.zones) {
-            const zoneIps = this.items.filter(item => item.zoneid === zone.id)
-            const providerIps = zoneIps.filter(item => item.forprovider === true)
-            if (providerIps.length === 0) {
-              filteredItems.push(...zoneIps)
-            } else {
-              filteredItems.push(...providerIps)
-            }
-          }
-          this.items = filteredItems
-        }
         if (!this.items || this.items.length === 0) {
           this.items = []
         }


(cloudstack) 07/44: Add missing suffix return (#14)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 6e41063808f122d98a42156e8785dffee9496d5d
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Thu Oct 24 18:44:10 2024 -0400

    Add missing suffix return (#14)
---
 .../src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 16f5f7fc0d8..b2cc9d47bdf 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -530,5 +530,6 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         } else {
             suffix = String.format("%s-%s", networkId, networkName);
         }
+        return suffix;
     }
 }


(cloudstack) 39/44: Enable Autoscaling on Netris for CPU and memory (#51)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit bd4e652128c4a149fd8d59e3296d8a10ab95db7f
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Thu Feb 13 12:14:30 2025 -0300

    Enable Autoscaling on Netris for CPU and memory (#51)
    
    * Enable Autoscaling on Netris for CPU and memory
    
    * Fix monitor autoscale group and cleanup
    
    * Rename autoscaling group method
    
    * Integrate Autoscaling by allowing to update LB rules
    
    * Refactor according to the SDK changes
---
 .../com/cloud/network/netris/NetrisService.java    |  2 +-
 .../resources/META-INF/db/schema-41910to42000.sql  |  4 +++
 .../apache/cloudstack/resource/NetrisResource.java |  2 +-
 .../apache/cloudstack/service/NetrisApiClient.java |  2 +-
 .../cloudstack/service/NetrisApiClientImpl.java    | 41 +++++++++++++---------
 .../apache/cloudstack/service/NetrisElement.java   | 18 +++++++++-
 .../cloudstack/service/NetrisServiceImpl.java      |  2 +-
 .../com/cloud/network/as/AutoScaleManagerImpl.java | 14 ++++----
 .../network/lb/LoadBalancingRulesManagerImpl.java  |  1 +
 .../com/cloud/server/ConfigurationServerImpl.java  | 31 ++++++++--------
 .../cloud/network/as/AutoScaleManagerImplTest.java | 16 ++++-----
 .../cloudstack/service/NetrisServiceMockTest.java  |  2 +-
 12 files changed, 84 insertions(+), 51 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index d386d4f8d58..43f94e0c6b7 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -55,6 +55,6 @@ public interface NetrisService {
 
     boolean releaseNatIp(long zoneId, String publicIp);
 
-    boolean createLbRule(NetrisNetworkRule rule);
+    boolean createOrUpdateLbRule(NetrisNetworkRule rule);
     boolean deleteLbRule(NetrisNetworkRule rule);
 }
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
index 739c5644859..be98ec33d33 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
@@ -459,3 +459,7 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.static_routes', 'next_hop', 'varchar
 
 -- Add `for_router` to `user_ip_address` table
 CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_ip_address', 'for_router', 'tinyint(1) DEFAULT 0 COMMENT "True if the ip address is used by Domain Router to expose services"');
+
+-- Add Netris Autoscaling rules
+INSERT IGNORE INTO `cloud`.`counter` (uuid, provider, source, name, value, created) VALUES (UUID(), 'Netris', 'cpu', 'VM CPU - average percentage', 'vm.cpu.average.percentage', NOW());
+INSERT IGNORE INTO `cloud`.`counter` (uuid, provider, source, name, value, created) VALUES (UUID(), 'Netris', 'memory', 'VM Memory - average percentage', 'vm.memory.average.percentage', NOW());
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 5d4777d610c..2a02b5de1b4 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -359,7 +359,7 @@ public class NetrisResource implements ServerResource {
     }
 
     private Answer executeRequest(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd) {
-        boolean result = netrisApiClient.createLbRule(cmd);
+        boolean result = netrisApiClient.createOrUpdateLbRule(cmd);
         if (!result) {
             return new NetrisAnswer(cmd, false, String.format("Failed to create Netris LB rule for %s: %s, " +
                     "for private port: %s and public port: %s", getNetworkType(cmd.isVpc()), cmd.getName(), cmd.getPrivatePort(), cmd.getPublicPort(), cmd.getPublicPort()));
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index 91351bf21f7..5957805d22d 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -86,6 +86,6 @@ public interface NetrisApiClient {
     boolean addOrUpdateStaticRoute(AddOrUpdateNetrisStaticRouteCommand cmd);
     boolean deleteStaticRoute(DeleteNetrisStaticRouteCommand cmd);
     boolean releaseNatIp(ReleaseNatIpCommand cmd);
-    boolean createLbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd);
+    boolean createOrUpdateLbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd);
     boolean deleteLbRule(DeleteNetrisLoadBalancerRuleCommand cmd);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index e49c7c5882a..0136a96cb79 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -56,11 +56,13 @@ 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.L4lbAddItem;
+import io.netris.model.L4lbAddOrUpdateItem;
 import io.netris.model.L4lbresBody;
 import io.netris.model.NatBodySiteSite;
 import io.netris.model.NatBodyVpcVpc;
@@ -97,6 +99,7 @@ import io.netris.model.VnetResDeleteBody;
 import io.netris.model.VnetResListBody;
 import io.netris.model.VnetsBody;
 import io.netris.model.response.AuthResponse;
+import io.netris.model.response.L4LbEditResponse;
 import io.netris.model.response.TenantResponse;
 import io.netris.model.response.TenantsResponse;
 import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
@@ -116,6 +119,7 @@ import org.apache.cloudstack.agent.api.ReleaseNatIpCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 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;
@@ -612,7 +616,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
     }
 
     @Override
-    public boolean createLbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd) {
+    public boolean createOrUpdateLbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd) {
         boolean isVpc = cmd.isVpc();
         Long networkResourceId = cmd.getId();
         String networkResourceName = cmd.getName();
@@ -641,16 +645,21 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             if (Boolean.FALSE.equals(result)) {
                 logger.warn("Could not find the Netris LB rule with name {}", lbName);
             }
-            if (!matchingLbId.isEmpty()) {
-                logger.warn("LB rule by name: {} already exists", lbName);
-                return true;
-            }
-
-            L4lbAddItem l4lbAddItem = getL4LbRule(cmd, vpcResource, lbName, publicIp, lbBackends);
+            boolean updateRule = !matchingLbId.isEmpty();
+            L4lbAddOrUpdateItem l4lbAddItem = getL4LbRule(cmd, vpcResource, lbName, publicIp, lbBackends, updateRule);
             L4LoadBalancerApi loadBalancerApi = apiClient.getApiStubForMethod(L4LoadBalancerApi.class);
-            ResAddEditBody response = loadBalancerApi.apiV2L4lbPost(l4lbAddItem);
-            if (Objects.isNull(response) || Boolean.FALSE.equals(response.isIsSuccess())) {
-                throw new CloudRuntimeException("Failed to create Netris LB rule");
+            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"));
             }
         } catch (ApiException e) {
             logAndThrowException("Failed to create Netris load balancer rule", e);
@@ -658,9 +667,9 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return true;
     }
 
-    private L4lbAddItem getL4LbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd, VPCListing vpcResource, String lbName,
-                             String publicIp, List<NetrisLbBackend> lbBackends) {
-        L4lbAddItem l4lbAddItem = new L4lbAddItem();
+    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);
 
@@ -685,7 +694,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             l4lbAddItem.setVpc(vpc);
 
             l4lbAddItem.setAutomatic(false);
-            l4lbAddItem.setIpFamily(NetUtils.isIpv4(publicIp) ? L4lbAddItem.IpFamilyEnum.IPv4 : L4lbAddItem.IpFamilyEnum.IPv6);
+            l4lbAddItem.setIpFamily(NetUtils.isIpv4(publicIp) ? L4lbAddOrUpdateItem.IpFamilyEnum.IPv4 : L4lbAddOrUpdateItem.IpFamilyEnum.IPv6);
             l4lbAddItem.setIp(publicIp);
             l4lbAddItem.setStatus("enable");
 
@@ -698,7 +707,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             }
             l4lbAddItem.setBackend(backends);
             l4lbAddItem.setPort(Integer.valueOf(cmd.getPublicPort()));
-            l4lbAddItem.setHealthCheck(L4lbAddItem.HealthCheckEnum.NONE);
+            l4lbAddItem.setHealthCheck(L4lbAddOrUpdateItem.HealthCheckEnum.NONE);
         } catch (Exception e) {
             throw new CloudRuntimeException("Failed to create Netris load balancer rule", e);
         }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 12e7239a8ee..27f8c99fcd0 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -44,6 +44,7 @@ import com.cloud.network.PhysicalNetworkServiceProvider;
 import com.cloud.network.PublicIpAddress;
 import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.SDNProviderOpObject;
+import com.cloud.network.as.AutoScaleCounter;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.LoadBalancerVMMapDao;
@@ -91,6 +92,7 @@ import com.cloud.vm.ReservationContext;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachineProfile;
 import com.cloud.vm.dao.VMInstanceDao;
+import com.google.gson.Gson;
 import org.apache.cloudstack.StartupNetrisCommand;
 import org.apache.cloudstack.api.ApiConstants;
 import com.cloud.network.netris.NetrisNetworkRule;
@@ -147,6 +149,16 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
 
     private final Map<Network.Service, Map<Network.Capability, String>> capabilities = initCapabilities();
 
+    protected static List<AutoScaleCounter> getNetrisAutoScaleCounters() {
+        AutoScaleCounter counter;
+        final List<AutoScaleCounter> counterList = new ArrayList<>();
+        counter = new AutoScaleCounter(AutoScaleCounter.AutoScaleCounterType.Cpu);
+        counterList.add(counter);
+        counter = new AutoScaleCounter(AutoScaleCounter.AutoScaleCounterType.Memory);
+        counterList.add(counter);
+        return counterList;
+    }
+
     private static Map<Network.Service, Map<Network.Capability, String>> initCapabilities() {
         Map<Network.Service, Map<Network.Capability, String>> capabilities = new HashMap<>();
 
@@ -166,6 +178,10 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
         lbCapabilities.put(Network.Capability.SupportedProtocols, "tcp, udp");
         lbCapabilities.put(Network.Capability.SupportedStickinessMethods, VirtualRouterElement.getHAProxyStickinessCapability());
         lbCapabilities.put(Network.Capability.LbSchemes, String.join(",", LoadBalancerContainer.Scheme.Internal.name(), LoadBalancerContainer.Scheme.Public.name()));
+        final Gson gson = new Gson();
+        final String autoScaleCounterList = gson.toJson(getNetrisAutoScaleCounters());
+        lbCapabilities.put(Network.Capability.AutoScaleCounters, autoScaleCounterList);
+        lbCapabilities.put(Network.Capability.VmAutoScaling, "true");
 
         capabilities.put(Network.Service.Lb, lbCapabilities);
         capabilities.put(Network.Service.PortForwarding, null);
@@ -750,7 +766,7 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
                     .lbBackends(lbBackends)
                     .build();
             if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(loadBalancingRule.getState())) {
-                result &= netrisService.createLbRule(networkRule);
+                result &= netrisService.createOrUpdateLbRule(networkRule);
             } else if (loadBalancingRule.getState() == FirewallRule.State.Revoke) {
                 result &= netrisService.deleteLbRule(networkRule);
             }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 62064c1ac07..45e9147d632 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -458,7 +458,7 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
     }
 
     @Override
-    public boolean createLbRule(NetrisNetworkRule rule) {
+    public boolean createOrUpdateLbRule(NetrisNetworkRule rule) {
         SDNProviderNetworkRule baseRule = rule.getBaseRule();
         CreateOrUpdateNetrisLoadBalancerRuleCommand cmd = new CreateOrUpdateNetrisLoadBalancerRuleCommand(baseRule.getZoneId(), baseRule.getAccountId(),
                 baseRule.getDomainId(), baseRule.getNetworkResourceName(), baseRule.getNetworkResourceId(), baseRule.isVpcResource(),
diff --git a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
index 5e7a4a0c4ef..83b747cf830 100644
--- a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
@@ -2257,8 +2257,9 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
             Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
             if (Network.Provider.Netscaler.equals(provider)) {
                 checkNetScalerAsGroup(asGroup);
-            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
-                checkVirtualRouterAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider) ||
+                       Network.Provider.Netris.equals(provider)) {
+                checkAutoscalingGroup(asGroup);
             }
         }
     }
@@ -2633,7 +2634,7 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
         countersNumberMap.put(key, countersNumberMap.get(key) + 1);
     }
 
-    protected void monitorVirtualRouterAsGroup(AutoScaleVmGroupVO asGroup) {
+    protected void monitorAutoscalingGroup(AutoScaleVmGroupVO asGroup) {
         if (!checkAsGroupMaxAndMinMembers(asGroup)) {
             return;
         }
@@ -2663,7 +2664,7 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
         }
     }
 
-    protected void checkVirtualRouterAsGroup(AutoScaleVmGroupVO asGroup) {
+    protected void checkAutoscalingGroup(AutoScaleVmGroupVO asGroup) {
         AutoScaleVmGroupTO groupTO = lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
 
         Map<String, Double> countersMap = new HashMap<>();
@@ -3005,8 +3006,9 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
                     Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
                     if (Network.Provider.Netscaler.equals(provider)) {
                         logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
-                    } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
-                        monitorVirtualRouterAsGroup(asGroup);
+                    } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider) ||
+                               Network.Provider.Netris.equals(provider)) {
+                        monitorAutoscalingGroup(asGroup);
                     }
                 }
             } catch (final Exception e) {
diff --git a/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
index bb1f4ec1b9b..c1ec91e69b9 100644
--- a/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
@@ -1005,6 +1005,7 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
             return false;
         }
         if (provider.get(0) == Provider.Netscaler || provider.get(0) == Provider.F5BigIp ||
+            provider.get(0) == Provider.Netris ||
             provider.get(0) == Provider.VirtualRouter || provider.get(0) == Provider.VPCVirtualRouter) {
             return true;
         }
diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
index 33b8db99bb3..dfc0e23f645 100644
--- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
@@ -1199,51 +1199,52 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
 
                 // Offering #9 - network offering for NSX provider - NATTED mode
                 createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING, "Offering for NSX enabled networks - NAT mode",
-                        NetworkOffering.NetworkMode.NATTED, false, true, Provider.Nsx);
+                        NetworkOffering.NetworkMode.NATTED, false, true, false, Provider.Nsx);
 
                 // Offering #10 - network offering for NSX provider - ROUTED mode
                 createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_ROUTED_NSX_OFFERING, "Offering for NSX enabled networks - ROUTED mode",
-                        NetworkOffering.NetworkMode.ROUTED, false, true, Provider.Nsx);
+                        NetworkOffering.NetworkMode.ROUTED, false, true, false, Provider.Nsx);
 
                 // Offering #11 - network offering for NSX provider for VPCs - NATTED mode
                 createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING_FOR_VPC, "Offering for NSX enabled networks on VPCs - NAT mode",
-                        NetworkOffering.NetworkMode.NATTED, true, true, Provider.Nsx);
+                        NetworkOffering.NetworkMode.NATTED, true, true, false, Provider.Nsx);
 
                 // Offering #12 - network offering for NSX provider for VPCs - ROUTED mode
                 createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC, "Offering for NSX enabled networks on VPCs - ROUTED mode",
-                        NetworkOffering.NetworkMode.ROUTED, true, true, Provider.Nsx);
+                        NetworkOffering.NetworkMode.ROUTED, true, true, false, Provider.Nsx);
 
                 // Offering #13 - network offering for NSX provider for VPCs with Internal LB - NATTED mode
                 createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB, "Offering for NSX enabled networks on VPCs with internal LB - NAT mode",
-                        NetworkOffering.NetworkMode.NATTED, true, false, Provider.Nsx);
+                        NetworkOffering.NetworkMode.NATTED, true, false, false, Provider.Nsx);
 
                 // Offering #14 - network offering for Netris provider for VPCs - ROUTED mode
                 createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_ROUTED_NETRIS_OFFERING_FOR_VPC, "Offering for Netris enabled networks on VPCs - ROUTED mode",
-                        NetworkOffering.NetworkMode.ROUTED, true, true, Provider.Netris);
+                        NetworkOffering.NetworkMode.ROUTED, true, true, false, Provider.Netris);
 
                 // Offering #15 - network offering for Netris provider for VPCs - NATTED mode
                 createAndPersistDefaultProviderOffering(NetworkOffering.DEFAULT_NAT_NETRIS_OFFERING_FOR_VPC, "Offering for Netris enabled networks on VPCs - NAT mode",
-                        NetworkOffering.NetworkMode.NATTED, true, true, Provider.Netris);
+                        NetworkOffering.NetworkMode.NATTED, true, true, true, Provider.Netris);
             }
         });
     }
 
     private void createAndPersistDefaultProviderOffering(String name, String displayText, NetworkOffering.NetworkMode networkMode,
-                                                    boolean forVpc, boolean publicLB, Provider provider) {
-        NetworkOfferingVO defaultNatProviderNetworkOffering =
+                                                    boolean forVpc, boolean publicLB, boolean supportVmAutoscaling, Provider provider) {
+        NetworkOfferingVO providerNetworkOffering =
                 new NetworkOfferingVO(name, displayText, TrafficType.Guest, false, false, null,
                         null, true, Availability.Optional, null, GuestType.Isolated, false,
                         false, false, false, false, forVpc);
-        defaultNatProviderNetworkOffering.setPublicLb(publicLB);
-        defaultNatProviderNetworkOffering.setInternalLb(!publicLB);
-        defaultNatProviderNetworkOffering.setNetworkMode(networkMode);
-        defaultNatProviderNetworkOffering.setState(NetworkOffering.State.Enabled);
-        defaultNatProviderNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultNatProviderNetworkOffering);
+        providerNetworkOffering.setPublicLb(publicLB);
+        providerNetworkOffering.setInternalLb(!publicLB);
+        providerNetworkOffering.setNetworkMode(networkMode);
+        providerNetworkOffering.setState(NetworkOffering.State.Enabled);
+        providerNetworkOffering.setSupportsVmAutoScaling(supportVmAutoscaling);
+        providerNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(providerNetworkOffering);
 
         Map<Service, Provider> serviceProviderMap = getServicesAndProvidersForProviderNetwork(networkMode, forVpc, provider);
         for (Map.Entry<Network.Service, Network.Provider> service : serviceProviderMap.entrySet()) {
             NetworkOfferingServiceMapVO offService =
-                    new NetworkOfferingServiceMapVO(defaultNatProviderNetworkOffering.getId(), service.getKey(), service.getValue());
+                    new NetworkOfferingServiceMapVO(providerNetworkOffering.getId(), service.getKey(), service.getValue());
             _ntwkOfferingServiceMapDao.persist(offService);
             logger.trace("Added service for the network offering: " + offService);
         }
diff --git a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
index b391aeb9f07..23f5a2a05e9 100644
--- a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
+++ b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
@@ -1572,11 +1572,11 @@ public class AutoScaleManagerImplTest {
         when(asVmGroupMock.getState()).thenReturn(AutoScaleVmGroup.State.ENABLED);
         when(asVmGroupMock.getLoadBalancerId()).thenReturn(loadBalancerId);
         Mockito.doReturn(Network.Provider.VirtualRouter).when(autoScaleManagerImplSpy).getLoadBalancerServiceProvider(loadBalancerId);
-        Mockito.doNothing().when(autoScaleManagerImplSpy).checkVirtualRouterAsGroup(asVmGroupMock);
+        Mockito.doNothing().when(autoScaleManagerImplSpy).checkAutoscalingGroup(asVmGroupMock);
 
         autoScaleManagerImplSpy.checkAutoScaleVmGroup(asVmGroupMock);
 
-        Mockito.verify(autoScaleManagerImplSpy).checkVirtualRouterAsGroup(asVmGroupMock);
+        Mockito.verify(autoScaleManagerImplSpy).checkAutoscalingGroup(asVmGroupMock);
         Mockito.verify(autoScaleManagerImplSpy, never()).checkNetScalerAsGroup(asVmGroupMock);
     }
 
@@ -1585,11 +1585,11 @@ public class AutoScaleManagerImplTest {
         when(asVmGroupMock.getState()).thenReturn(AutoScaleVmGroup.State.ENABLED);
         when(asVmGroupMock.getLoadBalancerId()).thenReturn(loadBalancerId);
         Mockito.doReturn(Network.Provider.VPCVirtualRouter).when(autoScaleManagerImplSpy).getLoadBalancerServiceProvider(loadBalancerId);
-        Mockito.doNothing().when(autoScaleManagerImplSpy).checkVirtualRouterAsGroup(asVmGroupMock);
+        Mockito.doNothing().when(autoScaleManagerImplSpy).checkAutoscalingGroup(asVmGroupMock);
 
         autoScaleManagerImplSpy.checkAutoScaleVmGroup(asVmGroupMock);
 
-        Mockito.verify(autoScaleManagerImplSpy).checkVirtualRouterAsGroup(asVmGroupMock);
+        Mockito.verify(autoScaleManagerImplSpy).checkAutoscalingGroup(asVmGroupMock);
         Mockito.verify(autoScaleManagerImplSpy, never()).checkNetScalerAsGroup(asVmGroupMock);
     }
 
@@ -1602,7 +1602,7 @@ public class AutoScaleManagerImplTest {
 
         autoScaleManagerImplSpy.checkAutoScaleVmGroup(asVmGroupMock);
 
-        Mockito.verify(autoScaleManagerImplSpy, never()).checkVirtualRouterAsGroup(asVmGroupMock);
+        Mockito.verify(autoScaleManagerImplSpy, never()).checkAutoscalingGroup(asVmGroupMock);
         Mockito.verify(autoScaleManagerImplSpy).checkNetScalerAsGroup(asVmGroupMock);
     }
 
@@ -1984,7 +1984,7 @@ public class AutoScaleManagerImplTest {
         Mockito.doNothing().when(autoScaleManagerImplSpy).doScaleUp(vmGroupId, 1);
         Mockito.doNothing().when(autoScaleManagerImplSpy).cleanupAsVmGroupStatistics(groupTO);
 
-        autoScaleManagerImplSpy.checkVirtualRouterAsGroup(asVmGroupMock);
+        autoScaleManagerImplSpy.checkAutoscalingGroup(asVmGroupMock);
 
         Mockito.verify(autoScaleManagerImplSpy).doScaleUp(vmGroupId, 1);
     }
@@ -2000,7 +2000,7 @@ public class AutoScaleManagerImplTest {
         Mockito.doNothing().when(autoScaleManagerImplSpy).doScaleDown(vmGroupId);
         Mockito.doNothing().when(autoScaleManagerImplSpy).cleanupAsVmGroupStatistics(groupTO);
 
-        autoScaleManagerImplSpy.checkVirtualRouterAsGroup(asVmGroupMock);
+        autoScaleManagerImplSpy.checkAutoscalingGroup(asVmGroupMock);
 
         Mockito.verify(autoScaleManagerImplSpy).doScaleDown(vmGroupId);
     }
@@ -2211,7 +2211,7 @@ public class AutoScaleManagerImplTest {
         Mockito.doNothing().when(autoScaleManagerImplSpy).getVmStatsFromHosts(groupTO);
         Mockito.doNothing().when(autoScaleManagerImplSpy).getNetworkStatsFromVirtualRouter(groupTO);
 
-        autoScaleManagerImplSpy.monitorVirtualRouterAsGroup(asVmGroupMock);
+        autoScaleManagerImplSpy.monitorAutoscalingGroup(asVmGroupMock);
 
         Mockito.verify(autoScaleManagerImplSpy).getVmStatsFromHosts(groupTO);
         Mockito.verify(autoScaleManagerImplSpy).getNetworkStatsFromVirtualRouter(groupTO);
diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index 99f319d6de7..7dbfc0b9cbb 100644
--- a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -107,7 +107,7 @@ public class NetrisServiceMockTest implements NetrisService {
     }
 
     @Override
-    public boolean createLbRule(NetrisNetworkRule rule) {
+    public boolean createOrUpdateLbRule(NetrisNetworkRule rule) {
         return true;
     }
 


(cloudstack) 28/44: Add support for Gateway service for Netris VPC and network offerings (#39)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 610fd911d621fe66f1db4dca634f20ec0e2f4205
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Thu Dec 26 08:33:41 2024 -0500

    Add support for Gateway service for Netris VPC and network offerings (#39)
    
    * Add support for Gateway service for Netris VPC and network offerings
    
    * Restore UserData service
    
    * add gateway only to vpc service
    
    * Add support for gateway service for external network providers for networks in routed mode
    
    * add support for gateway svc
    
    * Revert "add support for gateway svc"
    
    This reverts commit 06645cd1c6d08a81ede5d1431497ea3f2efdc5dc.
    
    * Fix VPC offering creation
    
    * Fix VR public NIC after Gateway service is set to Netris
    
    ---------
    
    Co-authored-by: nvazquez <ni...@gmail.com>
---
 .../java/com/cloud/network/vpc/VpcProvisioningService.java   |  2 +-
 .../api/command/admin/network/CreateNetworkOfferingCmd.java  |  3 +--
 .../api/command/admin/vpc/CreateVPCOfferingCmd.java          | 12 +++++++++---
 .../network/contrail/management/ContrailManagerImpl.java     |  2 +-
 .../java/org/apache/cloudstack/service/NetrisElement.java    |  1 +
 .../apache/cloudstack/service/NetrisGuestNetworkGuru.java    |  3 ++-
 .../com/cloud/configuration/ConfigurationManagerImpl.java    |  3 +++
 .../src/main/java/com/cloud/network/NetworkServiceImpl.java  |  6 +++++-
 .../src/main/java/com/cloud/network/vpc/VpcManagerImpl.java  | 11 +++++++----
 ui/src/views/offering/AddVpcOffering.vue                     |  7 +++++++
 10 files changed, 37 insertions(+), 13 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java
index 10f1ddcc12d..97b95339ecf 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java
@@ -37,7 +37,7 @@ public interface VpcProvisioningService {
     VpcOffering createVpcOffering(String name, String displayText, List<String> supportedServices,
                                   Map<String, List<String>> serviceProviders,
                                   Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol,
-                                  Long serviceOfferingId, Boolean forNsx, NetworkOffering.NetworkMode networkMode,
+                                  Long serviceOfferingId, String externalProvider, NetworkOffering.NetworkMode networkMode,
                                   List<Long> domainIds, List<Long> zoneIds, VpcOffering.State state,
                                   NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber);
 
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
index e2509b7c532..603e34d600b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
@@ -394,8 +394,7 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
     private void getServiceProviderMapForExternalProvider(Map<String, List<String>> serviceProviderMap, String provider) {
         String routerProvider = Boolean.TRUE.equals(getForVpc()) ? VirtualRouterProvider.Type.VPCVirtualRouter.name() :
                 VirtualRouterProvider.Type.VirtualRouter.name();
-        List<String> unsupportedServices = new ArrayList<>(List.of("Vpn", "SecurityGroup", "Connectivity",
-                "Gateway", "BaremetalPxeService"));
+        List<String> unsupportedServices = new ArrayList<>(List.of("Vpn", "Gateway", "SecurityGroup", "Connectivity", "BaremetalPxeService"));
         List<String> routerSupported = List.of("Dhcp", "Dns", "UserData");
         List<String> allServices = Service.listAllServices().stream().map(Service::getName).collect(Collectors.toList());
         if (routerProvider.equals(VirtualRouterProvider.Type.VPCVirtualRouter.name())) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
index dcceb6ada14..1dce0cd6a49 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
@@ -60,6 +60,7 @@ import static com.cloud.network.Network.Service.SourceNat;
 import static com.cloud.network.Network.Service.PortForwarding;
 import static com.cloud.network.Network.Service.NetworkACL;
 import static com.cloud.network.Network.Service.UserData;
+import static com.cloud.network.Network.Service.Gateway;
 
 @APICommand(name = "createVPCOffering", description = "Creates VPC offering", responseObject = VpcOfferingResponse.class,
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -190,6 +191,9 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
                         SourceNat.getName(),
                         PortForwarding.getName()));
             }
+            if (NetworkOffering.NetworkMode.ROUTED.name().equalsIgnoreCase(getNetworkMode())) {
+                supportedServices.add(Gateway.getName());
+            }
             if (getNsxSupportsLbService()) {
                 supportedServices.add(Lb.getName());
             }
@@ -239,8 +243,10 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
     }
 
     private void getServiceProviderMapForExternalProvider(Map<String, List<String>> serviceProviderMap, String provider) {
-        List<String> unsupportedServices = List.of("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity",
-                "Gateway", "Firewall");
+        List<String> unsupportedServices = Arrays.asList("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity", "Firewall");
+        if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) {
+            unsupportedServices.add("Gateway");
+        }
         List<String> routerSupported = List.of("Dhcp", "Dns", "UserData");
         List<String> allServices = Network.Service.listAllServices().stream().map(Network.Service::getName).collect(Collectors.toList());
         for (String service : allServices) {
@@ -249,7 +255,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
             if (routerSupported.contains(service))
                 serviceProviderMap.put(service, List.of(VirtualRouterProvider.Type.VPCVirtualRouter.name()));
             else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) ||
-                    Stream.of(NetworkACL.getName()).anyMatch(s -> s.equalsIgnoreCase(service))) {
+                    Stream.of(NetworkACL.getName(), Gateway.getName()).anyMatch(s -> s.equalsIgnoreCase(service))) {
                 serviceProviderMap.put(service, List.of(provider));
             }
         }
diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java
index 622886e6d83..da08ba2a6c2 100644
--- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java
+++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java
@@ -293,7 +293,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager
             }
             serviceProviderMap.put(svc, providerSet);
         }
-        vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, false, null, null, null, VpcOffering.State.Enabled, null, false);
+        vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, null, null, null, null, VpcOffering.State.Enabled, null, false);
         long id = vpcOffer.getId();
         _vpcOffDao.update(id, (VpcOfferingVO)vpcOffer);
         return _vpcOffDao.findById(id);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index b74ec53f9f5..44339e38628 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -171,6 +171,7 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
         Map<Network.Capability, String> sourceNatCapabilities = new HashMap<>();
         sourceNatCapabilities.put(Network.Capability.RedundantRouter, "true");
         sourceNatCapabilities.put(Network.Capability.SupportedSourceNatTypes, "peraccount");
+        capabilities.put(Network.Service.Gateway, null);
         capabilities.put(Network.Service.SourceNat, sourceNatCapabilities);
         return capabilities;
     }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
index 8fa07f17d40..3bd508b52e3 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
@@ -196,8 +196,9 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
                         dcId);
             }
             implemented.setBroadcastUri(Networks.BroadcastDomainType.Netris.toUri(vnet));
+            Long networkId = implemented.getId() > 0 ? implemented.getId() : network.getId();
             ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), network.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VXLAN_ASSIGN,
-                    "Assigned Zone vNet: " + vnet + " Network Id: " + implemented.getId(), implemented.getId(), ApiCommandResourceType.Network.toString(), 0);
+                    "Assigned Zone vNet: " + vnet + " Network Id: " + networkId, networkId, ApiCommandResourceType.Network.toString(), 0);
         } else {
             implemented.setBroadcastUri(network.getBroadcastUri());
         }
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 7f72d4fa279..8ff3593e4ed 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -6741,6 +6741,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                     } else {
                         serviceProviderMap.put(Service.Gateway, Sets.newHashSet(Provider.VirtualRouter));
                     }
+                } else {
+                    Set<Provider> providers = serviceProviderMap.get(Service.NetworkACL);
+                    serviceProviderMap.put(Service.Gateway, Sets.newHashSet(providers.iterator().next()));
                 }
             }
         }
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 00b21ec385a..15ff36b6c9e 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -32,6 +32,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -5084,11 +5085,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
 
             // validate Services
             boolean addGatewayService = false;
+            boolean isRoutedMode = enabledServices.stream().noneMatch(svc -> svc.equalsIgnoreCase(Service.SourceNat.getName()));
             for (String serviceName : enabledServices) {
                 Network.Service service = Network.Service.getService(serviceName);
                 if (service == null || service == Service.Gateway) {
                     throw new InvalidParameterValueException("Invalid Network Service specified=" + serviceName);
-                } else if (service == Service.SourceNat) {
+                } else if (service == Service.SourceNat ||
+                        (isRoutedMode && Arrays.asList(Provider.Nsx.getName().toLowerCase(Locale.ROOT),
+                        Provider.Netris.getName().toLowerCase(Locale.ROOT)).contains(providerName.toLowerCase(Locale.ROOT)))) {
                     addGatewayService = true;
                 }
 
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index db90aef30c8..d34cba5aeb6 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -605,7 +605,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         }
 
         return createVpcOffering(vpcOfferingName, displayText, supportedServices,
-                serviceProviderList, serviceCapabilityList, internetProtocol, serviceOfferingId, forNsx, networkMode,
+                serviceProviderList, serviceCapabilityList, internetProtocol, serviceOfferingId, provider, networkMode,
                 domainIds, zoneIds, (enable ? State.Enabled : State.Disabled), routingMode, specifyAsNumber);
     }
 
@@ -613,7 +613,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
     @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create = true)
     public VpcOffering createVpcOffering(final String name, final String displayText, final List<String> supportedServices, final Map<String, List<String>> serviceProviders,
                                          final Map serviceCapabilityList, final NetUtils.InternetProtocol internetProtocol, final Long serviceOfferingId,
-                                         final Boolean forNsx, final NetworkOffering.NetworkMode networkMode, List<Long> domainIds, List<Long> zoneIds, State state,
+                                         final String externalProvider, final NetworkOffering.NetworkMode networkMode, List<Long> domainIds, List<Long> zoneIds, State state,
                                          NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber) {
 
         if (!Ipv6Service.Ipv6OfferingCreationEnabled.value() && !(internetProtocol == null || NetUtils.InternetProtocol.IPv4.equals(internetProtocol))) {
@@ -697,7 +697,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         Service redundantRouterService = Service.SourceNat;
         if (CollectionUtils.isNotEmpty(sourceNatServiceProviders)) {
             svcProviderMap.put(Service.Gateway, sourceNatServiceProviders);
-        } else if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode)) {
+        } else if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode) && org.apache.commons.lang3.StringUtils.isBlank(externalProvider)) {
+            // For Routed mode, add the Gateway service except for external providers such as NSX, Netris to not override the svcProviderMap mapping
             svcProviderMap.put(Service.Gateway, Sets.newHashSet(Provider.VPCVirtualRouter));
             redundantRouterService = Service.Gateway;
         }
@@ -3540,7 +3541,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
                 || vpcOffSvcProvidersMap.get(Service.SourceNat).contains(Provider.Nsx)
                 || vpcOffSvcProvidersMap.get(Service.SourceNat).contains(Provider.Netris)))
                 || (Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.Gateway))
-                && vpcOffSvcProvidersMap.get(Service.Gateway).contains(Network.Provider.VPCVirtualRouter));
+                    && (vpcOffSvcProvidersMap.get(Service.Gateway).contains(Network.Provider.VPCVirtualRouter)
+                    || vpcOffSvcProvidersMap.get(Service.Gateway).contains(Provider.Nsx)
+                    || vpcOffSvcProvidersMap.get(Service.Gateway).contains(Network.Provider.Netris)));
     }
 
      @Override
diff --git a/ui/src/views/offering/AddVpcOffering.vue b/ui/src/views/offering/AddVpcOffering.vue
index 1480705aa29..1e6888e2c39 100644
--- a/ui/src/views/offering/AddVpcOffering.vue
+++ b/ui/src/views/offering/AddVpcOffering.vue
@@ -572,6 +572,13 @@ export default {
         services = services.filter(service => {
           return !['SourceNat', 'StaticNat', 'Lb', 'PortForwarding', 'Vpn'].includes(service.name)
         })
+        if (['NSX', 'Netris'].includes(this.provider)) {
+          services.push({
+            name: 'Gateway',
+            enabled: true,
+            provider: [{ name: this.provider }]
+          })
+        }
       }
       for (var i in services) {
         services[i].description = services[i].name


(cloudstack) 30/44: Release NAT IP subnet when VPC is removed or IP is released (#44)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit f54cb50d17854be0cc74583eb510d14ba2663109
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Thu Jan 9 10:24:45 2025 -0500

    Release NAT IP subnet when VPC is removed or IP is released (#44)
    
    * Release NAT IP subnet when VPC is removed or IP is released
    
    * add license
---
 .../com/cloud/network/element/NetworkElement.java  |  7 +++++
 .../com/cloud/network/netris/NetrisService.java    |  2 ++
 .../network/vpc/dao/VpcOfferingServiceMapDao.java  |  2 ++
 .../vpc/dao/VpcOfferingServiceMapDaoImpl.java      | 10 ++++++++
 .../networkservice/BaremetalDhcpElement.java       |  6 +++++
 .../networkservice/BaremetalPxeElement.java        |  6 +++++
 .../networkservice/BaremetalUserdataElement.java   |  6 +++++
 .../network/element/CiscoNexusVSMElement.java      |  7 ++++-
 .../cloud/network/element/BigSwitchBcfElement.java |  6 +++++
 .../cloud/network/element/BrocadeVcsElement.java   |  6 +++++
 .../cloud/network/element/CiscoVnmcElement.java    |  5 ++++
 .../cloudstack/network/element/DnsNotifier.java    |  6 +++++
 .../element/ElasticLoadBalancerElement.java        |  6 +++++
 .../cloudstack/element/GloboDnsElement.java        |  6 +++++
 .../element/InternalLoadBalancerElement.java       |  6 +++++
 .../contrail/management/ContrailElementImpl.java   |  5 ++++
 .../cloudstack/agent/api/ReleaseNatIpCommand.java  | 30 ++++++++++++++++++++++
 .../apache/cloudstack/resource/NetrisResource.java | 11 ++++++++
 .../apache/cloudstack/service/NetrisApiClient.java |  2 ++
 .../cloudstack/service/NetrisApiClientImpl.java    | 26 +++++++++++++++++++
 .../apache/cloudstack/service/NetrisElement.java   |  5 ++++
 .../cloudstack/service/NetrisServiceImpl.java      |  8 ++++++
 .../cloud/network/element/NetscalerElement.java    |  5 ++++
 .../cloud/network/element/NiciraNvpElement.java    |  5 ++++
 .../org/apache/cloudstack/service/NsxElement.java  |  5 ++++
 .../network/opendaylight/OpendaylightElement.java  |  6 +++++
 .../java/com/cloud/network/element/OvsElement.java |  6 +++++
 .../element/PaloAltoExternalFirewallElement.java   |  6 +++++
 .../cloudstack/network/element/SspElement.java     |  6 +++++
 .../network/tungsten/service/TungstenElement.java  |  6 +++++
 .../com/cloud/network/IpAddressManagerImpl.java    | 30 ++++++++++++++++++++++
 .../network/element/ConfigDriveNetworkElement.java |  6 +++++
 .../network/element/SecurityGroupElement.java      |  6 +++++
 .../network/element/VirtualRouterElement.java      |  6 +++++
 .../vpc/dao/MockVpcOfferingServiceMapDaoImpl.java  |  5 ++++
 .../cloudstack/service/NetrisServiceMockTest.java  |  5 ++++
 36 files changed, 276 insertions(+), 1 deletion(-)

diff --git a/api/src/main/java/com/cloud/network/element/NetworkElement.java b/api/src/main/java/com/cloud/network/element/NetworkElement.java
index fa67575edd3..00c530ce114 100644
--- a/api/src/main/java/com/cloud/network/element/NetworkElement.java
+++ b/api/src/main/java/com/cloud/network/element/NetworkElement.java
@@ -23,6 +23,7 @@ import com.cloud.deploy.DeployDestination;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.Provider;
@@ -87,6 +88,12 @@ public interface NetworkElement extends Adapter {
     boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException,
         ResourceUnavailableException;
 
+    /**
+     * Release IP from the network provider if reserved
+     * @param ipAddress
+     */
+    boolean releaseIp(IpAddress ipAddress);
+
     /**
      * The network is being shutdown.
      * @param network
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index 355586f7d91..6fd860f26be 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -46,4 +46,6 @@ public interface NetrisService {
     boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute);
 
     boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId);
+
+    boolean releaseNatIp(long zoneId, String publicIp);
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java
index 9519cd08a0b..020536e97ec 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java
@@ -40,4 +40,6 @@ public interface VpcOfferingServiceMapDao extends GenericDao<VpcOfferingServiceM
 
     boolean isProviderForVpcOffering(Network.Provider provider, long vpcOfferingId);
 
+    List<VpcOfferingServiceMapVO> listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service);
+
 }
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java
index 672955a3ecc..dcb1becf9e8 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java
@@ -119,4 +119,14 @@ public class VpcOfferingServiceMapDaoImpl extends GenericDaoBase<VpcOfferingServ
         sc.setParameters("provider", provider.getName());
         return findOneBy(sc) != null;
     }
+
+    @Override
+    public List<VpcOfferingServiceMapVO> listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service) {
+        SearchCriteria<VpcOfferingServiceMapVO> sc = AllFieldsSearch.create();
+
+        sc.setParameters("vpcOffId", vpcOfferingId);
+        sc.setParameters("service", service.getName());
+
+        return customSearch(sc, null);
+    }
 }
diff --git a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
index e39b40cfc68..2e9d6989550 100644
--- a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
+++ b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
@@ -34,6 +34,7 @@ import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.host.Host;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.GuestType;
@@ -125,6 +126,11 @@ public class BaremetalDhcpElement extends AdapterBase implements DhcpServiceProv
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return true;
diff --git a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalPxeElement.java b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalPxeElement.java
index fa708e7be4c..adcb96f6116 100644
--- a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalPxeElement.java
+++ b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalPxeElement.java
@@ -29,6 +29,7 @@ import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.GuestType;
@@ -161,6 +162,11 @@ public class BaremetalPxeElement extends AdapterBase implements NetworkElement {
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     private void releaseVlan(Network network, VirtualMachineProfile vm) {
         vlanMgr.releaseVlan(network, vm);
     }
diff --git a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java
index d2c9731ddd1..2ec05d443aa 100644
--- a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java
+++ b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetalUserdataElement.java
@@ -30,6 +30,7 @@ import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.Provider;
@@ -124,6 +125,11 @@ public class BaremetalUserdataElement extends AdapterBase implements NetworkElem
         return false;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         // TODO Auto-generated method stub
diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/network/element/CiscoNexusVSMElement.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/network/element/CiscoNexusVSMElement.java
index 2503e0ac7fd..441bdb83854 100644
--- a/plugins/hypervisors/vmware/src/main/java/com/cloud/network/element/CiscoNexusVSMElement.java
+++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/network/element/CiscoNexusVSMElement.java
@@ -24,7 +24,7 @@ import java.util.Set;
 
 import javax.inject.Inject;
 
-
+import com.cloud.network.IpAddress;
 import com.cloud.api.commands.DeleteCiscoNexusVSMCmd;
 import com.cloud.api.commands.DisableCiscoNexusVSMCmd;
 import com.cloud.api.commands.EnableCiscoNexusVSMCmd;
@@ -106,6 +106,11 @@ public class CiscoNexusVSMElement extends CiscoNexusVSMDeviceManagerImpl impleme
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return true;
diff --git a/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java b/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java
index 5fc9480c610..f3624ebeb40 100644
--- a/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java
+++ b/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java
@@ -29,6 +29,7 @@ import java.util.UUID;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.network.IpAddress;
 import org.springframework.stereotype.Component;
 import org.apache.cloudstack.network.ExternalNetworkDeviceManager.NetworkDevice;
 import org.apache.commons.net.util.SubnetUtils;
@@ -318,6 +319,11 @@ NetworkACLServiceProvider, FirewallServiceProvider, ResourceStateAdapter {
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         if (!canHandle(network, Service.Connectivity)) {
diff --git a/plugins/network-elements/brocade-vcs/src/main/java/com/cloud/network/element/BrocadeVcsElement.java b/plugins/network-elements/brocade-vcs/src/main/java/com/cloud/network/element/BrocadeVcsElement.java
index daf9c1c4e08..510900cc105 100644
--- a/plugins/network-elements/brocade-vcs/src/main/java/com/cloud/network/element/BrocadeVcsElement.java
+++ b/plugins/network-elements/brocade-vcs/src/main/java/com/cloud/network/element/BrocadeVcsElement.java
@@ -27,6 +27,7 @@ import java.util.UUID;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.network.IpAddress;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.network.ExternalNetworkDeviceManager.NetworkDevice;
 import org.springframework.stereotype.Component;
@@ -194,6 +195,11 @@ public class BrocadeVcsElement extends AdapterBase implements NetworkElement, Re
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         if (!canHandle(network, Service.Connectivity)) {
diff --git a/plugins/network-elements/cisco-vnmc/src/main/java/com/cloud/network/element/CiscoVnmcElement.java b/plugins/network-elements/cisco-vnmc/src/main/java/com/cloud/network/element/CiscoVnmcElement.java
index bea5a2c3f25..352eba4674d 100644
--- a/plugins/network-elements/cisco-vnmc/src/main/java/com/cloud/network/element/CiscoVnmcElement.java
+++ b/plugins/network-elements/cisco-vnmc/src/main/java/com/cloud/network/element/CiscoVnmcElement.java
@@ -434,6 +434,11 @@ public class CiscoVnmcElement extends AdapterBase implements SourceNatServicePro
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     private boolean cleanupLogicalEdgeFirewall(long vlanId, long hostId) {
         CleanupLogicalEdgeFirewallCommand cmd = new CleanupLogicalEdgeFirewallCommand(vlanId);
         Answer answer = _agentMgr.easySend(hostId, cmd);
diff --git a/plugins/network-elements/dns-notifier/src/main/java/org/apache/cloudstack/network/element/DnsNotifier.java b/plugins/network-elements/dns-notifier/src/main/java/org/apache/cloudstack/network/element/DnsNotifier.java
index 0b96aa35150..04f092648c2 100644
--- a/plugins/network-elements/dns-notifier/src/main/java/org/apache/cloudstack/network/element/DnsNotifier.java
+++ b/plugins/network-elements/dns-notifier/src/main/java/org/apache/cloudstack/network/element/DnsNotifier.java
@@ -26,6 +26,7 @@ import com.cloud.deploy.DeployDestination;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.Provider;
@@ -86,6 +87,11 @@ public class DnsNotifier extends AdapterBase implements NetworkElement {
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return true;
diff --git a/plugins/network-elements/elastic-loadbalancer/src/main/java/com/cloud/network/element/ElasticLoadBalancerElement.java b/plugins/network-elements/elastic-loadbalancer/src/main/java/com/cloud/network/element/ElasticLoadBalancerElement.java
index c1ea7823811..f512a08dffd 100644
--- a/plugins/network-elements/elastic-loadbalancer/src/main/java/com/cloud/network/element/ElasticLoadBalancerElement.java
+++ b/plugins/network-elements/elastic-loadbalancer/src/main/java/com/cloud/network/element/ElasticLoadBalancerElement.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.network.IpAddress;
 import org.springframework.stereotype.Component;
 
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@@ -136,6 +137,11 @@ public class ElasticLoadBalancerElement extends AdapterBase implements LoadBalan
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         // TODO kill all loadbalancer vms by calling the ElasticLoadBalancerManager
diff --git a/plugins/network-elements/globodns/src/main/java/com/globo/globodns/cloudstack/element/GloboDnsElement.java b/plugins/network-elements/globodns/src/main/java/com/globo/globodns/cloudstack/element/GloboDnsElement.java
index 09830d9a810..c83a913a61c 100644
--- a/plugins/network-elements/globodns/src/main/java/com/globo/globodns/cloudstack/element/GloboDnsElement.java
+++ b/plugins/network-elements/globodns/src/main/java/com/globo/globodns/cloudstack/element/GloboDnsElement.java
@@ -26,6 +26,7 @@ import java.util.Set;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.network.IpAddress;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
 import org.springframework.stereotype.Component;
@@ -175,6 +176,11 @@ public class GloboDnsElement extends AdapterBase implements ResourceStateAdapter
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return true;
diff --git a/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java b/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java
index 0a9b4a7131a..dc48d70f8a7 100644
--- a/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java
+++ b/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java
@@ -26,6 +26,7 @@ import java.util.Set;
 
 import javax.inject.Inject;
 
+import com.cloud.network.IpAddress;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 
@@ -221,6 +222,11 @@ public class InternalLoadBalancerElement extends AdapterBase implements LoadBala
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         List<? extends VirtualRouter> internalLbVms = _routerDao.listByNetworkAndRole(network.getId(), Role.INTERNAL_LB_VM);
diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
index 44cbc6c305f..fb832bf1d82 100644
--- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
+++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
@@ -244,6 +244,11 @@ public class ContrailElementImpl extends AdapterBase
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return false;
+    }
+
     /**
      * Network disable
      */
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/ReleaseNatIpCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/ReleaseNatIpCommand.java
new file mode 100644
index 00000000000..ac3a4faba4d
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/ReleaseNatIpCommand.java
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class ReleaseNatIpCommand extends NetrisCommand {
+    private String natIp;
+
+    public ReleaseNatIpCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc, String natIp) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.natIp = natIp;
+    }
+
+    public String getNatIp() {
+        return natIp;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 4507eb7d82b..349b83132c7 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -38,6 +38,7 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.NetrisAnswer;
 import org.apache.cloudstack.StartupNetrisCommand;
+import org.apache.cloudstack.agent.api.ReleaseNatIpCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.service.NetrisApiClient;
 import org.apache.cloudstack.service.NetrisApiClientImpl;
@@ -109,6 +110,8 @@ public class NetrisResource implements ServerResource {
             return executeRequest((DeleteNetrisStaticRouteCommand) cmd);
         } else if (cmd instanceof AddOrUpdateNetrisStaticRouteCommand) {
             return executeRequest((AddOrUpdateNetrisStaticRouteCommand) cmd);
+        } else if (cmd instanceof ReleaseNatIpCommand) {
+          return executeRequest((ReleaseNatIpCommand) cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
@@ -319,6 +322,14 @@ public class NetrisResource implements ServerResource {
         return new NetrisAnswer(cmd, true, "OK");
     }
 
+    private Answer executeRequest(ReleaseNatIpCommand cmd) {
+        boolean result = netrisApiClient.releaseNatIp(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Failed to release NAT IP: %s", cmd.getNatIp()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
     @Override
     public boolean start() {
         return true;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index 59c2c9342a9..83e8b5fe0a3 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -28,6 +28,7 @@ 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.ReleaseNatIpCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 
 import java.util.List;
@@ -78,4 +79,5 @@ public interface NetrisApiClient {
     boolean deleteNatRule(DeleteNetrisNatRuleCommand cmd);
     boolean addOrUpdateStaticRoute(AddOrUpdateNetrisStaticRouteCommand cmd);
     boolean deleteStaticRoute(DeleteNetrisStaticRouteCommand cmd);
+    boolean releaseNatIp(ReleaseNatIpCommand cmd);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 2c409482a19..8d6d088407e 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -88,6 +88,7 @@ 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.ReleaseNatIpCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
 import org.apache.commons.collections.CollectionUtils;
@@ -441,6 +442,31 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return false;
     }
 
+    @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;
+    }
+
     private Pair<Boolean, RoutesGetBody> staticRouteExists(Integer netrisVpcId, String prefix, String nextHop, String description) {
         try {
             FilterByVpc vpcFilter = new FilterByVpc();
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 44339e38628..96b7379d285 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -320,6 +320,11 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
         return false;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return netrisService.releaseNatIp(ipAddress.getDataCenterId(), ipAddress.getAddress().addr());
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return canHandle(network, Network.Service.Connectivity);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 6d97f850243..cb2047e32e8 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -52,6 +52,7 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.NetrisAnswer;
 import org.apache.cloudstack.agent.api.NetrisCommand;
+import org.apache.cloudstack.agent.api.ReleaseNatIpCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.framework.config.ConfigKey;
@@ -354,6 +355,13 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         return answer.getResult();
     }
 
+    @Override
+    public boolean releaseNatIp(long zoneId, String publicIp) {
+        ReleaseNatIpCommand cmd = new ReleaseNatIpCommand(zoneId, null, null, null, null, false, publicIp);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
     private String getResourceSuffix(Long vpcId, Long networkId, boolean isForVpc) {
         String suffix;
         if (isForVpc) {
diff --git a/plugins/network-elements/netscaler/src/main/java/com/cloud/network/element/NetscalerElement.java b/plugins/network-elements/netscaler/src/main/java/com/cloud/network/element/NetscalerElement.java
index 48b9006f34c..34df6e95482 100644
--- a/plugins/network-elements/netscaler/src/main/java/com/cloud/network/element/NetscalerElement.java
+++ b/plugins/network-elements/netscaler/src/main/java/com/cloud/network/element/NetscalerElement.java
@@ -420,6 +420,11 @@ IpDeployer, StaticNatServiceProvider, GslbServiceProvider {
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network guestConfig, ReservationContext context, boolean cleanup)
             throws ResourceUnavailableException, ConcurrentOperationException {
diff --git a/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/element/NiciraNvpElement.java b/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/element/NiciraNvpElement.java
index 356b452a9e4..5ab85910dc9 100644
--- a/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/element/NiciraNvpElement.java
+++ b/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/element/NiciraNvpElement.java
@@ -448,6 +448,11 @@ NiciraNvpElementService, ResourceStateAdapter, IpDeployer {
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         if (!canHandle(network, Service.Connectivity)) {
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
index 753d0cf822e..a57868f864e 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
@@ -284,6 +284,11 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
         return false;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return canHandle(network, Network.Service.Connectivity);
diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightElement.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightElement.java
index b35190217d3..00d9ca8360b 100644
--- a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightElement.java
+++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightElement.java
@@ -27,6 +27,7 @@ import java.util.Set;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.network.IpAddress;
 import org.springframework.stereotype.Component;
 
 import org.apache.cloudstack.network.opendaylight.agent.commands.StartupOpenDaylightControllerCommand;
@@ -102,6 +103,11 @@ public class OpendaylightElement extends AdapterBase implements ConnectivityProv
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return true;
diff --git a/plugins/network-elements/ovs/src/main/java/com/cloud/network/element/OvsElement.java b/plugins/network-elements/ovs/src/main/java/com/cloud/network/element/OvsElement.java
index 69891954264..85877292c95 100644
--- a/plugins/network-elements/ovs/src/main/java/com/cloud/network/element/OvsElement.java
+++ b/plugins/network-elements/ovs/src/main/java/com/cloud/network/element/OvsElement.java
@@ -25,6 +25,7 @@ import java.util.Set;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.network.IpAddress;
 import org.apache.cloudstack.network.topology.NetworkTopology;
 import org.apache.cloudstack.network.topology.NetworkTopologyContext;
 
@@ -208,6 +209,11 @@ StaticNatServiceProvider, IpDeployer {
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(final Network network, final ReservationContext context,
             final boolean cleanup) throws ConcurrentOperationException,
diff --git a/plugins/network-elements/palo-alto/src/main/java/com/cloud/network/element/PaloAltoExternalFirewallElement.java b/plugins/network-elements/palo-alto/src/main/java/com/cloud/network/element/PaloAltoExternalFirewallElement.java
index c81ac5f5f0c..e27fd88b5a8 100644
--- a/plugins/network-elements/palo-alto/src/main/java/com/cloud/network/element/PaloAltoExternalFirewallElement.java
+++ b/plugins/network-elements/palo-alto/src/main/java/com/cloud/network/element/PaloAltoExternalFirewallElement.java
@@ -25,6 +25,7 @@ import java.util.Set;
 import javax.inject.Inject;
 
 
+import com.cloud.network.IpAddress;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.network.ExternalNetworkDeviceManager.NetworkDevice;
 
@@ -174,6 +175,11 @@ public class PaloAltoExternalFirewallElement extends ExternalFirewallDeviceManag
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ResourceUnavailableException, ConcurrentOperationException {
         DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
diff --git a/plugins/network-elements/stratosphere-ssp/src/main/java/org/apache/cloudstack/network/element/SspElement.java b/plugins/network-elements/stratosphere-ssp/src/main/java/org/apache/cloudstack/network/element/SspElement.java
index bfe9de2c837..978eb114588 100644
--- a/plugins/network-elements/stratosphere-ssp/src/main/java/org/apache/cloudstack/network/element/SspElement.java
+++ b/plugins/network-elements/stratosphere-ssp/src/main/java/org/apache/cloudstack/network/element/SspElement.java
@@ -30,6 +30,7 @@ import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
 
+import com.cloud.network.IpAddress;
 import org.apache.cloudstack.api.commands.AddSspCmd;
 import org.apache.cloudstack.api.commands.DeleteSspCmd;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -510,6 +511,11 @@ public class SspElement extends AdapterBase implements ConnectivityProvider, Ssp
         return deleteNicEnv(network, nic, context);
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     /* (non-Javadoc)
      * Destroy a network implementation.
      *
diff --git a/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/service/TungstenElement.java b/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/service/TungstenElement.java
index 106cf5180c3..18275f2e9d4 100644
--- a/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/service/TungstenElement.java
+++ b/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/service/TungstenElement.java
@@ -41,6 +41,7 @@ import com.cloud.host.HostVO;
 import com.cloud.host.Status;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor;
+import com.cloud.network.IpAddress;
 import com.cloud.network.IpAddressManager;
 import com.cloud.network.Network;
 import com.cloud.network.NetworkMigrationResponder;
@@ -689,6 +690,11 @@ public class TungstenElement extends AdapterBase
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup)
         throws ConcurrentOperationException {
diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
index 2c54a5d890c..e52eeb84fbd 100644
--- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
@@ -43,6 +43,10 @@ import com.cloud.network.dao.Site2SiteVpnGatewayDao;
 import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.element.NsxProviderVO;
 import com.cloud.network.vo.PublicIpQuarantineVO;
+import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.VpcOffering;
+import com.cloud.network.vpc.VpcOfferingServiceMapVO;
+import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
 import com.cloud.resourcelimit.CheckedReservation;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@@ -275,6 +279,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
     @Inject
     NetworkOfferingServiceMapDao _ntwkOfferingSrvcDao;
     @Inject
+    VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
+    @Inject
     PhysicalNetworkDao _physicalNetworkDao;
     @Inject
     PhysicalNetworkServiceProviderDao _pNSPDao;
@@ -806,6 +812,30 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
             } else if (publicIpQuarantine != null) {
                 removePublicIpAddressFromQuarantine(publicIpQuarantine.getId(), "Public IP address removed from quarantine as there was an error while disassociating it.");
             }
+            Network network = _networksDao.findById(ipToBeDisassociated.getAssociatedWithNetworkId());
+            Vpc vpc = _vpcDao.findById(ip.getVpcId());
+            if (ObjectUtils.allNull(network, vpc)) {
+                return success;
+            }
+            List<String> providers;
+            if (Objects.nonNull(network)) {
+                NetworkOffering offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
+                providers = _ntwkOfferingSrvcDao.listProvidersForServiceForNetworkOffering(offering.getId(), Service.NetworkACL);
+            } else {
+                VpcOffering offering = vpcOfferingDao.findById(vpc.getVpcOfferingId());
+                List<VpcOfferingServiceMapVO> servicesMap = vpcOfferingServiceMapDao.listProvidersForServiceForVpcOffering(offering.getId(), Service.NetworkACL);
+                providers = servicesMap.stream().map(VpcOfferingServiceMapVO::getProvider).collect(Collectors.toList());
+            }
+
+            if (providers.isEmpty()) {
+                throw new InvalidParameterValueException("Unable to find the provider for this network");
+            }
+
+            String provider = providers.get(0);
+            NetworkElement element = _networkModel.getElementImplementingProvider(provider);
+            if (element != null) {
+                element.releaseIp(ipToBeDisassociated);
+            }
         } finally {
             _ipAddressDao.releaseFromLockTable(addrId);
         }
diff --git a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
index 3449f1f5d00..07a40c95b4a 100644
--- a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
+++ b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java
@@ -25,6 +25,7 @@ import java.util.Set;
 
 import javax.inject.Inject;
 
+import com.cloud.network.IpAddress;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
@@ -179,6 +180,11 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
         }
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return true; // assume that the agent will remove userdata etc
diff --git a/server/src/main/java/com/cloud/network/element/SecurityGroupElement.java b/server/src/main/java/com/cloud/network/element/SecurityGroupElement.java
index 86c862bf072..c2e3317487c 100644
--- a/server/src/main/java/com/cloud/network/element/SecurityGroupElement.java
+++ b/server/src/main/java/com/cloud/network/element/SecurityGroupElement.java
@@ -25,6 +25,7 @@ import com.cloud.deploy.DeployDestination;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.Network.Capability;
 import com.cloud.network.Network.Provider;
@@ -77,6 +78,11 @@ public class SecurityGroupElement extends AdapterBase implements NetworkElement
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
     @Override
     public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
         return true;
diff --git a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
index 53cf838ca87..92ee5f76c16 100644
--- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
@@ -24,6 +24,7 @@ import java.util.Set;
 
 import javax.inject.Inject;
 
+import com.cloud.network.IpAddress;
 import org.apache.cloudstack.network.BgpPeer;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.ObjectUtils;
@@ -943,6 +944,11 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
         return true;
     }
 
+    @Override
+    public boolean releaseIp(IpAddress ipAddress) {
+        return true;
+    }
+
 
     @Override
     public boolean configDhcpSupportForSubnet(final Network network, final NicProfile nic, final VirtualMachineProfile vm, final DeployDestination dest,
diff --git a/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java
index 3d45d8e839f..27aba5c013c 100644
--- a/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java
+++ b/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java
@@ -68,6 +68,11 @@ public class MockVpcOfferingServiceMapDaoImpl extends GenericDaoBase<VpcOffering
         return false;
     }
 
+    @Override
+    public List<VpcOfferingServiceMapVO> listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service) {
+        return List.of();
+    }
+
     @Override
     public VpcOfferingServiceMapVO persist(VpcOfferingServiceMapVO vo) {
         return vo;
diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index b4e4509dc3a..05a6cd20213 100644
--- a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -86,4 +86,9 @@ public class NetrisServiceMockTest implements NetrisService {
     public boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId) {
         return true;
     }
+
+    @Override
+    public boolean releaseNatIp(long zoneId, String publicIp) {
+        return true;
+    }
 }


(cloudstack) 08/44: Set up Netris Public range on new zone addition (#15)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 9b4d95780a02df12f5bed8e72eac1b3f2857875b
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Thu Nov 7 11:05:20 2024 -0300

    Set up Netris Public range on new zone addition (#15)
    
    * Set up Netris Public range on new zone addition
    
    * Add dependency to calculate subnet containing a start and end IP
    
    * Remove unused import
    
    * Move dependency to the netris module
    
    * Rename Netris IP range
    
    * Refactor logic
    
    * Revert "Refactor logic"
    
    This reverts commit 7ec36a81320444c37e7bb914dd895060b663411b.
    
    * Fix setup range after adding Netris Provider
    
    * Fix VXLAN range adding on zone creation
---
 plugins/network-elements/netris/pom.xml            |   5 +
 .../apache/cloudstack/agent/api/NetrisCommand.java |  10 +-
 .../agent/api/SetupNetrisPublicRangeCommand.java   |  37 ++++++
 .../apache/cloudstack/resource/NetrisResource.java |  11 ++
 .../resource/NetrisResourceObjectUtils.java        |  19 ++-
 .../apache/cloudstack/service/NetrisApiClient.java |   8 ++
 .../cloudstack/service/NetrisApiClientImpl.java    | 138 ++++++++++++++++-----
 .../service/NetrisProviderServiceImpl.java         |  88 ++++++++++++-
 .../service/NetrisProviderServiceImplTest.java     |  33 +++++
 .../java/com/cloud/network/NetworkServiceImpl.java |   2 +-
 ui/public/locales/en.json                          |   2 +-
 11 files changed, 305 insertions(+), 48 deletions(-)

diff --git a/plugins/network-elements/netris/pom.xml b/plugins/network-elements/netris/pom.xml
index 881d8195a93..21aeadec935 100644
--- a/plugins/network-elements/netris/pom.xml
+++ b/plugins/network-elements/netris/pom.xml
@@ -35,5 +35,10 @@
             <artifactId>netris-java-sdk</artifactId>
             <version>1.0.0</version>
         </dependency>
+        <dependency>
+            <groupId>com.github.seancfoley</groupId>
+            <artifactId>ipaddress</artifactId>
+            <version>5.5.1</version>
+        </dependency>
     </dependencies>
 </project>
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
index 440892e6e87..463001452c1 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
@@ -20,13 +20,13 @@ import com.cloud.agent.api.Command;
 
 public class NetrisCommand extends Command {
     private final long zoneId;
-    private final long accountId;
-    private final long domainId;
+    private final Long accountId;
+    private final Long domainId;
     private final String name;
     private final long id;
     private final boolean isVpc;
 
-    public NetrisCommand(long zoneId, long accountId, long domainId, String name, long id, boolean isVpc) {
+    public NetrisCommand(long zoneId, Long accountId, Long domainId, String name, long id, boolean isVpc) {
         this.zoneId = zoneId;
         this.accountId = accountId;
         this.domainId = domainId;
@@ -48,11 +48,11 @@ public class NetrisCommand extends Command {
         return zoneId;
     }
 
-    public long getAccountId() {
+    public Long getAccountId() {
         return accountId;
     }
 
-    public long getDomainId() {
+    public Long getDomainId() {
         return domainId;
     }
 
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/SetupNetrisPublicRangeCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/SetupNetrisPublicRangeCommand.java
new file mode 100644
index 00000000000..5cbdd96e76a
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/SetupNetrisPublicRangeCommand.java
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class SetupNetrisPublicRangeCommand extends NetrisCommand {
+
+    private final String superCidr;
+    private final String exactCidr;
+
+    public SetupNetrisPublicRangeCommand(long zoneId, String superCidr, String exactCidr) {
+        super(zoneId, null, null, null, zoneId, false);
+        this.superCidr = superCidr;
+        this.exactCidr = exactCidr;
+    }
+
+    public String getSuperCidr() {
+        return superCidr;
+    }
+
+    public String getExactCidr() {
+        return exactCidr;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 0fdb0913053..dc922aef963 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -34,6 +34,7 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.NetrisAnswer;
 import org.apache.cloudstack.StartupNetrisCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.service.NetrisApiClient;
 import org.apache.cloudstack.service.NetrisApiClientImpl;
 import org.apache.logging.log4j.LogManager;
@@ -94,6 +95,8 @@ public class NetrisResource implements ServerResource {
             return executeRequest((CreateNetrisVnetCommand) cmd);
         } else if (cmd instanceof DeleteNetrisVnetCommand) {
           return executeRequest((DeleteNetrisVnetCommand) cmd);
+        } else if (cmd instanceof SetupNetrisPublicRangeCommand) {
+            return executeRequest((SetupNetrisPublicRangeCommand) cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
@@ -250,6 +253,14 @@ public class NetrisResource implements ServerResource {
         return new NetrisAnswer(cmd, true, "OK");
     }
 
+    private Answer executeRequest(SetupNetrisPublicRangeCommand cmd) {
+        boolean result = netrisApiClient.setupZoneLevelPublicRange(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, "Netris Setup for Public Range failed");
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
     @Override
     public boolean start() {
         return true;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
index 127636dd7d5..5b19aa43199 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -27,14 +27,19 @@ public class NetrisResourceObjectUtils {
 
     public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, NetrisObjectType netrisObjectType, String... suffixes) {
         long zoneId = cmd.getZoneId();
-        long accountId = cmd.getAccountId();
-        long domainId = cmd.getDomainId();
+        Long accountId = cmd.getAccountId();
+        Long domainId = cmd.getDomainId();
         long objectId = cmd.getId();
         String objectName = cmd.getName();
         boolean isVpc = cmd.isVpc();
+        boolean isZoneLevel = accountId == null && domainId == null;
 
         StringBuilder stringBuilder = new StringBuilder();
-        stringBuilder.append(String.format("D%s-A%s-Z%s", domainId, accountId, zoneId));
+        if (isZoneLevel) {
+            stringBuilder.append(String.format("Z%s", zoneId));
+        } else {
+            stringBuilder.append(String.format("D%s-A%s-Z%s", domainId, accountId, zoneId));
+        }
         String prefix = isVpc ? "-V" : "-N";
         switch (netrisObjectType) {
             case VPC:
@@ -46,10 +51,14 @@ public class NetrisResourceObjectUtils {
                 }
                 break;
             case IPAM_ALLOCATION:
-                stringBuilder.append(String.format("%s%s", prefix, objectId));
+                if (!isZoneLevel) {
+                    stringBuilder.append(String.format("%s%s", prefix, objectId));
+                }
                 break;
             case IPAM_SUBNET:
-                stringBuilder.append(String.format("-N%s", objectId));
+                if (!isZoneLevel) {
+                    stringBuilder.append(String.format("-N%s", objectId));
+                }
                 break;
             case VNET:
                break;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index 42cb9a772fc..0a00583df52 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -24,6 +24,7 @@ import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 
 import java.util.List;
 
@@ -50,4 +51,11 @@ public interface NetrisApiClient {
     boolean createVnet(CreateNetrisVnetCommand cmd);
 
     boolean deleteVnet(DeleteNetrisVnetCommand cmd);
+
+    /**
+     * Check and create zone level Netris Public range in the following manner:
+     * - Check the IPAM allocation for the zone super CIDR. In case it doesn't exist, create it
+     * - Check the IPAM subnet for NAT purpose for the range start-end. In case it doesn't exist, create it
+     */
+    boolean setupZoneLevelPublicRange(SetupNetrisPublicRangeCommand cmd);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index b2cc9d47bdf..b61c84e0110 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -33,6 +33,9 @@ import io.netris.model.FilterBySites;
 import io.netris.model.FilterByVpc;
 import io.netris.model.GetSiteBody;
 import io.netris.model.InlineResponse2004;
+import io.netris.model.InlineResponse2004Data;
+import io.netris.model.IpTree;
+import io.netris.model.IpTreeAllocation;
 import io.netris.model.IpTreeAllocationTenant;
 import io.netris.model.IpTreeSubnet;
 import io.netris.model.IpTreeSubnetSites;
@@ -63,6 +66,7 @@ import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.logging.log4j.LogManager;
@@ -196,25 +200,31 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return response;
     }
 
-    private InlineResponse2004 createVpcAllocationInternal(VPCResponseObjectOK createdVpc, String cidr, int adminTenantId,
-                                                           String adminTenantName, String netrisIpamAllocationName) {
-        logger.debug(String.format("Creating Netris VPC Allocation %s for VPC %s", cidr, createdVpc.getData().getName()));
+    private InlineResponse2004Data createIpamAllocationInternal(String ipamName, String ipamPrefix, VPCListing vpc) {
+        logger.debug(String.format("Creating Netris IPAM Allocation %s for VPC %s", ipamPrefix, vpc.getName()));
         try {
             IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
             AllocationBody body = new AllocationBody();
             AllocationBodyVpc allocationBodyVpc = new AllocationBodyVpc();
-            allocationBodyVpc.setId(createdVpc.getData().getId());
-            allocationBodyVpc.setName(createdVpc.getData().getName());
+            allocationBodyVpc.setId(vpc.getId());
+            allocationBodyVpc.setName(vpc.getName());
             body.setVpc(allocationBodyVpc);
-            body.setName(netrisIpamAllocationName);
-            body.setPrefix(cidr);
+            body.setName(ipamName);
+            body.setPrefix(ipamPrefix);
             IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
-            allocationTenant.setId(new BigDecimal(adminTenantId));
-            allocationTenant.setName(adminTenantName);
+            allocationTenant.setId(new BigDecimal(tenantId));
+            allocationTenant.setName(tenantName);
             body.setTenant(allocationTenant);
-            return ipamApi.apiV2IpamAllocationPost(body);
+            InlineResponse2004 ipamResponse = ipamApi.apiV2IpamAllocationPost(body);
+            if (ipamResponse == null || !ipamResponse.isIsSuccess()) {
+                String reason = ipamResponse == null ? "Empty response" : "Operation failed on Netris";
+                logger.debug("The Netris Allocation {} for VPC {} creation failed: {}", ipamPrefix, vpc.getName(), reason);
+                return null;
+            }
+            logger.debug(String.format("Successfully created VPC %s and its IPAM Allocation %s on Netris", vpc.getName(), ipamPrefix));
+            return ipamResponse.getData();
         } catch (ApiException e) {
-            logAndThrowException(String.format("Error creating Netris Allocation %s for VPC %s", cidr, createdVpc.getData().getName()), e);
+            logAndThrowException(String.format("Error creating Netris IPAM Allocation %s for VPC %s", ipamPrefix, vpc.getName()), e);
             return null;
         }
     }
@@ -231,14 +241,8 @@ public class NetrisApiClientImpl implements NetrisApiClient {
 
         String netrisIpamAllocationName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, cmd.getCidr());
         String vpcCidr = cmd.getCidr();
-        InlineResponse2004 ipamResponse = createVpcAllocationInternal(createdVpc, vpcCidr, tenantId, tenantName, netrisIpamAllocationName);
-        if (ipamResponse == null || !ipamResponse.isIsSuccess()) {
-            String reason = ipamResponse == null ? "Empty response" : "Operation failed on Netris";
-            logger.debug("The Netris Allocation {} for VPC {} creation failed: {}", vpcCidr, cmd.getName(), reason);
-            return false;
-        }
-        logger.debug(String.format("Successfully created VPC %s and its IPAM Allocation %s on Netris", cmd.getName(), vpcCidr));
-        return true;
+        InlineResponse2004Data createdIpamAllocation = createIpamAllocationInternal(netrisIpamAllocationName, vpcCidr, createdVpc.getData());
+        return createdIpamAllocation != null;
     }
 
     private void deleteVpcIpamAllocationInternal(VPCListing vpcResource, String vpcCidr) {
@@ -334,12 +338,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String netrisVnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VNET, vNetName) ;
         String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
 
-        InlineResponse2004 subnetResponse = createVpcSubnetInternal(associatedVpc, vNetName, vnetCidr, netrisSubnetName);
-        if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
-            String reason = subnetResponse == null ? "Empty response" : "Operation failed on Netris";
-            logger.debug("The Netris Subnet {} for network {} creation failed: {}", vnetCidr, networkName, reason);
-            return false;
-        }
+        createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
         logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
 
         VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr);
@@ -392,6 +391,71 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return true;
     }
 
+    protected VPCListing getSystemVpc() throws ApiException {
+        List<VPCListing> systemVpcList = listVPCs().stream().filter(VPCListing::isIsSystem).collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(systemVpcList)) {
+            String msg = "Cannot find any system VPC";
+            logger.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+        return systemVpcList.get(0);
+    }
+
+    private BigDecimal getIpamAllocationIdByPrefixAndVpc(String superCidrPrefix, VPCListing vpc) throws ApiException {
+        IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+        FilterBySites filterBySites = new FilterBySites();
+        filterBySites.add(siteId);
+        FilterByVpc filterByVpc = new FilterByVpc();
+        filterByVpc.add(vpc.getId());
+        IpTree ipamTree = ipamApi.apiV2IpamGet(filterBySites, filterByVpc);
+        List<IpTreeAllocation> superCidrList = ipamTree.getData().stream()
+                .filter(x -> x.getPrefix().equals(superCidrPrefix))
+                .collect(Collectors.toList());
+        return CollectionUtils.isEmpty(superCidrList) ? null : superCidrList.get(0).getId();
+    }
+
+    private IpTreeSubnet getIpamSubnetByAllocationAndPrefixAndPurposeAndVpc(BigDecimal ipamAllocationId, String exactCidr, IpTreeSubnet.PurposeEnum purpose, VPCListing vpc) throws ApiException {
+        IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+        FilterByVpc filterByVpc = new FilterByVpc();
+        filterByVpc.add(vpc.getId());
+        SubnetResBody subnetResBody = ipamApi.apiV2IpamSubnetsGet(filterByVpc);
+        List<IpTreeSubnet> exactSubnetList = subnetResBody.getData().stream()
+                .filter(x -> x.getAllocationID().equals(ipamAllocationId) && x.getPrefix().equals(exactCidr) && x.getPurpose() == purpose)
+                .collect(Collectors.toList());
+        return CollectionUtils.isEmpty(exactSubnetList) ? null : exactSubnetList.get(0);
+    }
+
+    @Override
+    public boolean setupZoneLevelPublicRange(SetupNetrisPublicRangeCommand cmd) {
+        String superCidr = cmd.getSuperCidr();
+        String exactCidr = cmd.getExactCidr();
+        try {
+            VPCListing systemVpc = getSystemVpc();
+            logger.debug("Checking if the Netris Public Super CIDR {} exists", superCidr);
+            BigDecimal ipamAllocationId = getIpamAllocationIdByPrefixAndVpc(superCidr, systemVpc);
+            if (ipamAllocationId == null) {
+                String ipamName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, superCidr);
+                InlineResponse2004Data ipamAllocation = createIpamAllocationInternal(ipamName, superCidr, systemVpc);
+                if (ipamAllocation == null) {
+                    String msg = String.format("Could not create the zone level super CIDR %s for the system VPC", superCidr);
+                    logger.error(msg);
+                    throw new CloudRuntimeException(msg);
+                }
+                ipamAllocationId = new BigDecimal(ipamAllocation.getId());
+            }
+            IpTreeSubnet exactSubnet = getIpamSubnetByAllocationAndPrefixAndPurposeAndVpc(ipamAllocationId, exactCidr, IpTreeSubnet.PurposeEnum.NAT, systemVpc);
+            if (exactSubnet == null) {
+                String ipamSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, exactCidr);
+                createIpamSubnetInternal(ipamSubnetName, exactCidr, SubnetBody.PurposeEnum.NAT, systemVpc);
+            }
+        } catch (ApiException e) {
+            String msg = String.format("Error setting up the Netris Public Range %s on super CIDR %s", exactCidr, superCidr);
+            logAndThrowException(msg, e);
+            return false;
+        }
+        return true;
+    }
+
     private void deleteVnetInternal(VPCListing associatedVpc, FilterBySites siteFilter, FilterByVpc vpcFilter, String netrisVnetName, String vNetName) {
         try {
             VNetApi vNetApi = apiClient.getApiStubForMethod(VNetApi.class);
@@ -433,16 +497,16 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
-    private InlineResponse2004 createVpcSubnetInternal(VPCListing associatedVpc, String vNetName, String vNetCidr, String netrisSubnetName) {
-        logger.debug("Creating Netris VPC Subnet {} for VPC {} for vNet {}", vNetCidr, associatedVpc.getName(), vNetName);
+    private InlineResponse2004Data createIpamSubnetInternal(String subnetName, String subnetPrefix, SubnetBody.PurposeEnum purpose, VPCListing vpc) {
+        logger.debug("Creating Netris IPAM Subnet {} for VPC {}", subnetPrefix, vpc.getName());
         try {
 
             SubnetBody subnetBody = new SubnetBody();
-            subnetBody.setName(netrisSubnetName);
+            subnetBody.setName(subnetName);
 
             AllocationBodyVpc vpcAllocationBody = new AllocationBodyVpc();
-            vpcAllocationBody.setName(associatedVpc.getName());
-            vpcAllocationBody.setId(associatedVpc.getId());
+            vpcAllocationBody.setName(vpc.getName());
+            vpcAllocationBody.setId(vpc.getId());
             subnetBody.setVpc(vpcAllocationBody);
 
             IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
@@ -455,12 +519,18 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             subnetSites.setName(siteName);
             subnetBody.setSites(List.of(subnetSites));
 
-            subnetBody.setPurpose(SubnetBody.PurposeEnum.COMMON);
-            subnetBody.setPrefix(vNetCidr);
+            subnetBody.setPurpose(purpose);
+            subnetBody.setPrefix(subnetPrefix);
             IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
-            return ipamApi.apiV2IpamSubnetPost(subnetBody);
+            InlineResponse2004 subnetResponse = ipamApi.apiV2IpamSubnetPost(subnetBody);
+            if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
+                String reason = subnetResponse == null ? "Empty response" : "Operation failed on Netris";
+                logger.debug("The Netris IPAM Subnet {} creation failed: {}", subnetName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+            return subnetResponse.getData();
         } catch (ApiException e) {
-            logAndThrowException(String.format("Error creating Netris Subnet %s for VPC %s", vNetCidr, associatedVpc.getName()), e);
+            logAndThrowException(String.format("Error creating Netris IPAM Subnet %s for VPC %s", subnetPrefix, vpc.getName()), e);
             return null;
         }
     }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
index ef4cd265d8c..2887e03a1f5 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
@@ -16,15 +16,19 @@
 // under the License.
 package org.apache.cloudstack.service;
 
-import com.amazonaws.util.CollectionUtils;
+import com.cloud.agent.api.Answer;
 import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.VlanVO;
 import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.VlanDao;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.host.DetailVO;
 import com.cloud.host.Host;
 import com.cloud.host.dao.HostDetailsDao;
 import com.cloud.network.Network;
 import com.cloud.network.Networks;
+import com.cloud.network.dao.IPAddressDao;
+import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.NetrisProviderDao;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
@@ -36,7 +40,11 @@ import com.cloud.resource.ResourceManager;
 import com.cloud.utils.db.Transaction;
 import com.cloud.utils.db.TransactionCallback;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
 import com.google.common.annotations.VisibleForTesting;
+import inet.ipaddr.IPAddress;
+import inet.ipaddr.IPAddressString;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
 import org.apache.cloudstack.api.BaseResponse;
 import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
 import org.apache.cloudstack.api.command.DeleteNetrisProviderCmd;
@@ -44,6 +52,10 @@ import org.apache.cloudstack.api.command.ListNetrisProvidersCmd;
 import org.apache.cloudstack.api.response.NetrisProviderResponse;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.resource.NetrisResource;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
@@ -53,9 +65,12 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 public class NetrisProviderServiceImpl implements NetrisProviderService {
 
+    protected Logger logger = LogManager.getLogger(getClass());
+
     @Inject
     DataCenterDao dataCenterDao;
     @Inject
@@ -68,6 +83,10 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
     PhysicalNetworkDao physicalNetworkDao;
     @Inject
     NetworkDao networkDao;
+    @Inject
+    private IPAddressDao ipAddressDao;
+    @Inject
+    private VlanDao vlanDao;
 
     @Override
     public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
@@ -123,12 +142,77 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
             } else {
                 throw new CloudRuntimeException("Failed to add Netris controller due to internal error.");
             }
+            createNetrisPublicIpRangesOnNetrisProvider(zoneId, netrisResource);
         } catch (ConfigurationException e) {
             throw new CloudRuntimeException(e.getMessage());
         }
         return  netrisProvider;
     }
 
+    /**
+     * Calculate the minimum CIDR subnet containing the IP range (using the library: <a href="https://github.com/seancfoley/IPAddress">IPAddress</a>)
+     * From: <a href="https://github.com/seancfoley/IPAddress/wiki/Code-Examples-3:-Subnetting-and-Other-Subnet-Operations#from-start-and-end-address-get-single-cidr-block-covering-both">Example</a>
+     * @param ipRange format: startIP-endIP
+     * @return the minimum CIDR containing the IP range
+     */
+    protected String calculateSubnetCidrFromIpRange(String ipRange) {
+        if (StringUtils.isBlank(ipRange) || !ipRange.contains("-")) {
+            return null;
+        }
+        String[] rangeArray = ipRange.split("-");
+        String startIp = rangeArray[0];
+        String endIp = rangeArray[1];
+        IPAddress startIpAddress = new IPAddressString(startIp).getAddress();
+        IPAddress endIpAddress = new IPAddressString(endIp).getAddress();
+        return startIpAddress.coverWithPrefixBlock(endIpAddress).toPrefixLengthString();
+    }
+
+    /**
+     * Prepare the Netris Public Range to be used by CloudStack after the zone is created and the Netris provider is added
+     */
+    public SetupNetrisPublicRangeCommand createSetupPublicRangeCommand(long zoneId, String gateway, String netmask, String ipRange) {
+        String superCidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, netmask);
+        String subnetNatCidr = calculateSubnetCidrFromIpRange(ipRange);
+        return new SetupNetrisPublicRangeCommand(zoneId, superCidr, subnetNatCidr);
+    }
+
+    protected void createNetrisPublicIpRangesOnNetrisProvider(long zoneId, NetrisResource netrisResource) {
+        List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZoneAndTrafficType(zoneId, Networks.TrafficType.Public);
+        physicalNetworks = physicalNetworks.stream().filter(x -> x.getIsolationMethods().contains(Network.Provider.Netris.getName())).collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(physicalNetworks)) {
+            return;
+        }
+        for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
+            List<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
+            List<Long> vlanDbIds = publicIps.stream()
+                    .filter(x -> !x.isForSystemVms())
+                    .map(IPAddressVO::getVlanId)
+                    .collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(vlanDbIds)) {
+                String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
+                logger.error(msg);
+                throw new CloudRuntimeException(msg);
+            }
+            if (vlanDbIds.size() > 1) {
+                logger.warn("Expected one Netris Public IP range but found {}. Using the first one to create the Netris IPAM allocation and NAT subnet", vlanDbIds.size());
+            }
+            VlanVO vlanRecord = vlanDao.findById(vlanDbIds.get(0));
+            if (vlanRecord == null) {
+                logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
+                return;
+            }
+            String gateway = vlanRecord.getVlanGateway();
+            String netmask = vlanRecord.getVlanNetmask();
+            String ipRange = vlanRecord.getIpRange();
+            SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
+            Answer answer = netrisResource.executeRequest(cmd);
+            boolean result = answer != null && answer.getResult();
+            if (!result) {
+                throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
+            }
+        }
+    }
+
     @Override
     public List<BaseResponse> listNetrisProviders(Long zoneId) {
         List<BaseResponse> netrisControllersResponseList = new ArrayList<>();
@@ -158,7 +242,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
         List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZone(zoneId);
         for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
             List<NetworkVO> networkList = networkDao.listByPhysicalNetwork(physicalNetwork.getId());
-            if (!CollectionUtils.isNullOrEmpty(networkList)) {
+            if (!CollectionUtils.isEmpty(networkList)) {
                 validateNetworkState(networkList);
             }
         }
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java
new file mode 100644
index 00000000000..233787d9004
--- /dev/null
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.service;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NetrisProviderServiceImplTest {
+
+    private NetrisProviderServiceImpl service = new NetrisProviderServiceImpl();
+
+    @Test
+    public void testCalculateSubnetCidrFromIpRange() {
+        String ipRange = "100.99.18.140-100.99.18.159";
+        String expectedCidr = "100.99.18.128/27";
+        String cidr = service.calculateSubnetCidrFromIpRange(ipRange);
+        Assert.assertEquals(expectedCidr, cidr);
+    }
+}
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 05be4ea6307..00b21ec385a 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -4450,7 +4450,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
             if (network.getIsolationMethods().contains("GRE")) {
                 minVnet = MIN_GRE_KEY;
                 maxVnet = MAX_GRE_KEY;
-            } else if (network.getIsolationMethods().contains("VXLAN")) {
+            } else if (network.getIsolationMethods().contains("VXLAN") || network.getIsolationMethods().contains("Netris")) {
                 minVnet = MIN_VXLAN_VNI;
                 maxVnet = MAX_VXLAN_VNI;
                 // fail if zone already contains VNI, need to be unique per zone.
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 9ac5d54f98e..8365853ad64 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1772,7 +1772,7 @@
 "label.public.ips": "Public IP addresses",
 "label.public.lb": "Public LB",
 "label.public.traffic": "Public traffic",
-"label.public.traffic.netris": "Netris Public traffic",
+"label.public.traffic.netris": "Netris Public IP Pool",
 "label.public.traffic.nsx": "NSX Public traffic",
 "label.publicinterface": "Public interface",
 "label.publicip": "IP address",


(cloudstack) 26/44: Add support to add and delete and update static routes on Netris (#37)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 5ac35a2d8bdf5e3e5547a29bd80627f614517a11
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Fri Dec 20 07:22:04 2024 -0500

    Add support to add and delete and update static routes on Netris (#37)
    
    * Add support to add static routes in Netris
    
    * support to delete static routes on netris
    
    * add defensive check for nextHop
    
    * Add support to update static routes
    
    * add state
    
    * pass empty list for switched to avoid timeout
    
    * Netris: search static route by name and next hop if exists
    
    ---------
    
    Co-authored-by: Wei Zhou <we...@apache.org>
---
 .../com/cloud/network/netris/NetrisService.java    |  14 ++
 .../java/com/cloud/network/vpc/StaticRoute.java    |   1 +
 .../api/AddOrUpdateNetrisStaticRouteCommand.java   |  48 ++++++
 .../agent/api/DeleteNetrisStaticRouteCommand.java  |  23 +++
 .../apache/cloudstack/resource/NetrisResource.java |  24 ++-
 .../resource/NetrisResourceObjectUtils.java        |   6 +-
 .../apache/cloudstack/service/NetrisApiClient.java |   4 +
 .../cloudstack/service/NetrisApiClientImpl.java    | 181 +++++++++++++++++++++
 .../apache/cloudstack/service/NetrisElement.java   |  10 ++
 .../cloudstack/service/NetrisServiceImpl.java      |  16 ++
 .../cloudstack/service/NetrisServiceMockTest.java  |  10 ++
 11 files changed, 335 insertions(+), 2 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index d989a53fcdb..355586f7d91 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -22,14 +22,28 @@ import com.cloud.network.vpc.Vpc;
 
 public interface NetrisService {
     boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId);
+
     boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
+
     boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
+
     boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
+
     boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
+
     boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String sourceNatIp);
+
     boolean createPortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule);
+
     boolean deletePortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule);
+
     boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address);
+
     boolean createStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String vpcCidr, String staticNatIp, String vmIp);
+
     boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp);
+
+    boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute);
+
+    boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId);
 }
diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
index 5707ca14024..b52ed980893 100644
--- a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
+++ b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
@@ -25,6 +25,7 @@ public interface StaticRoute extends ControlledEntity, Identity, InternalIdentit
         Staged, // route been created but has never got through network rule conflict detection.  Routes in this state can not be sent to VPC virtual router.
         Add,    // Add means the route has been created and has gone through network rule conflict detection.
         Active, // Route has been sent to the VPC router and reported to be active.
+        Update,
         Revoke,  // Revoke means this route has been revoked. If this route has been sent to the VPC router, the route will be deleted from database.
         Deleting // rule has been revoked and is scheduled for deletion
     }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/AddOrUpdateNetrisStaticRouteCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/AddOrUpdateNetrisStaticRouteCommand.java
new file mode 100644
index 00000000000..392016fc238
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/AddOrUpdateNetrisStaticRouteCommand.java
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class AddOrUpdateNetrisStaticRouteCommand extends NetrisCommand {
+    private String prefix;
+    private String nextHop;
+    private Long routeId;
+    private boolean updateRoute;
+
+    public AddOrUpdateNetrisStaticRouteCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc, String prefix, String nextHop, Long routeId, boolean updateRoute) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.prefix = prefix;
+        this.nextHop = nextHop;
+        this.routeId = routeId;
+        this.updateRoute = updateRoute;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public String getNextHop() {
+        return nextHop;
+    }
+
+    public Long getRouteId() {
+        return routeId;
+    }
+
+    public boolean isUpdateRoute() {
+        return updateRoute;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisStaticRouteCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisStaticRouteCommand.java
new file mode 100644
index 00000000000..b531a049b0f
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisStaticRouteCommand.java
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class DeleteNetrisStaticRouteCommand extends AddOrUpdateNetrisStaticRouteCommand {
+    public DeleteNetrisStaticRouteCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc, String prefix, String nextHop, Long routeId) {
+        super(zoneId, accountId, domainId, name, id, isVpc, prefix, nextHop, routeId, false);
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 6f5e01b6392..4507eb7d82b 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -28,10 +28,12 @@ import com.cloud.agent.api.StartupCommand;
 import com.cloud.host.Host;
 import com.cloud.resource.ServerResource;
 import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 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.NetrisAnswer;
@@ -102,7 +104,11 @@ public class NetrisResource implements ServerResource {
         } else if (cmd instanceof DeleteNetrisNatRuleCommand) {
             return executeRequest((DeleteNetrisNatRuleCommand) cmd);
         } else if (cmd instanceof CreateOrUpdateNetrisNatCommand) {
-          return executeRequest((CreateOrUpdateNetrisNatCommand) cmd);
+            return executeRequest((CreateOrUpdateNetrisNatCommand) cmd);
+        } else if (cmd instanceof DeleteNetrisStaticRouteCommand) {
+            return executeRequest((DeleteNetrisStaticRouteCommand) cmd);
+        } else if (cmd instanceof AddOrUpdateNetrisStaticRouteCommand) {
+            return executeRequest((AddOrUpdateNetrisStaticRouteCommand) cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
@@ -297,6 +303,22 @@ public class NetrisResource implements ServerResource {
         return new NetrisAnswer(cmd, true, "OK");
     }
 
+    private Answer executeRequest(AddOrUpdateNetrisStaticRouteCommand cmd) {
+        boolean result = netrisApiClient.addOrUpdateStaticRoute(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Failed to add static route for VPC: %s, prefix: %s, nextHop: %s ", cmd.getName(), cmd.getPrefix(), cmd.getNextHop()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
+    private Answer executeRequest(DeleteNetrisStaticRouteCommand cmd) {
+        boolean result = netrisApiClient.deleteStaticRoute(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Failed to add static route for VPC: %s, prefix: %s, nextHop: %s ", cmd.getName(), cmd.getPrefix(), cmd.getNextHop()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
     @Override
     public boolean start() {
         return true;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
index fd6851696c8..f5f6072b604 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -22,7 +22,7 @@ import org.apache.commons.lang3.ArrayUtils;
 public class NetrisResourceObjectUtils {
 
     public enum NetrisObjectType {
-        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT
+        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT, STATICROUTE
     }
 
     public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, NetrisObjectType netrisObjectType, String... suffixes) {
@@ -72,6 +72,10 @@ public class NetrisResourceObjectUtils {
                 stringBuilder.append(String.format("%s%s-%s", prefix, suffixes[0], "DNAT"));
                 suffixes = ArrayUtils.subarray(suffixes, 1, suffixes.length);
                 break;
+            case STATICROUTE:
+                stringBuilder.append(String.format("%s%s-%s%s", prefix, suffixes[0], "ROUTE", suffixes[1]));
+                suffixes = new String[0];
+                break;
             case VNET:
                break;
             default:
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index cf214bb4664..59c2c9342a9 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -20,10 +20,12 @@ import io.netris.ApiException;
 import io.netris.model.GetSiteBody;
 import io.netris.model.VPCListing;
 import io.netris.model.response.TenantResponse;
+import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 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.SetupNetrisPublicRangeCommand;
@@ -74,4 +76,6 @@ public interface NetrisApiClient {
     boolean createOrUpdateDNATRule(CreateOrUpdateNetrisNatCommand cmd);
     boolean createStaticNatRule(CreateOrUpdateNetrisNatCommand cmd);
     boolean deleteNatRule(DeleteNetrisNatRuleCommand cmd);
+    boolean addOrUpdateStaticRoute(AddOrUpdateNetrisStaticRouteCommand cmd);
+    boolean deleteStaticRoute(DeleteNetrisStaticRouteCommand cmd);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 9b7783c2eaa..e2d13e47daa 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -22,6 +22,7 @@ import io.netris.ApiClient;
 import io.netris.ApiException;
 import io.netris.ApiResponse;
 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;
@@ -35,6 +36,7 @@ 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;
@@ -48,6 +50,13 @@ import io.netris.model.NatGetBody;
 import io.netris.model.NatPostBody;
 import io.netris.model.NatPutBody;
 import io.netris.model.NatResponseGetOk;
+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;
@@ -71,10 +80,12 @@ import io.netris.model.VnetsBody;
 import io.netris.model.response.AuthResponse;
 import io.netris.model.response.TenantResponse;
 import io.netris.model.response.TenantsResponse;
+import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 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.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.SetupNetrisPublicRangeCommand;
@@ -283,6 +294,176 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return true;
     }
 
+    @Override
+    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 {}", prefix, netrisVpcName);
+                    return false;
+                }
+                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.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;
+    }
+
+    private Pair<Boolean, RoutesGetBody> staticRouteExists(Integer netrisVpcId, String prefix, String nextHop, String description) {
+        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 new Pair<>(false, null);
+            }
+            List<RoutesGetBody> routesList = routesResponseGetOk.getData();
+            List<RoutesGetBody> filteredList = routesList.stream()
+                    .filter(x -> x.getName().equals(prefix) &&
+                            (Objects.isNull(nextHop) || x.getNextHop().equals(nextHop)))
+                    .collect(Collectors.toList());
+            return new Pair<>(!filteredList.isEmpty(), filteredList.isEmpty() ? null : filteredList.get(0));
+        } catch (ApiException e) {
+            logAndThrowException("Error checking Netris static routes", e);
+        }
+        return new Pair<>(false, null);
+    }
+
     private void deleteNatSubnet(Integer netrisVpcId, String natIp) {
         FilterByVpc vpcFilter = new FilterByVpc();
         vpcFilter.add(netrisVpcId);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index eb74e6f7080..b74ec53f9f5 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -61,6 +61,7 @@ import com.cloud.network.rules.PortForwardingRule;
 import com.cloud.network.rules.StaticNat;
 import com.cloud.network.vpc.NetworkACLItem;
 import com.cloud.network.vpc.PrivateGateway;
+import com.cloud.network.vpc.StaticRoute;
 import com.cloud.network.vpc.StaticRouteProfile;
 import com.cloud.network.vpc.Vpc;
 import com.cloud.network.vpc.VpcVO;
@@ -410,6 +411,15 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
 
     @Override
     public boolean applyStaticRoutes(Vpc vpc, List<StaticRouteProfile> routes) throws ResourceUnavailableException {
+        for(StaticRouteProfile staticRoute : routes) {
+            if (StaticRoute.State.Add == staticRoute.getState()) {
+                netrisService.addOrUpdateStaticRoute(vpc.getZoneId(), vpc.getAccountId(), vpc.getDomainId(), vpc.getName(), vpc.getId(), true, staticRoute.getCidr(), staticRoute.getGateway(), staticRoute.getId(), false);
+            } else if (StaticRoute.State.Revoke == staticRoute.getState()) {
+                netrisService.deleteStaticRoute(vpc.getZoneId(), vpc.getAccountId(), vpc.getDomainId(), vpc.getName(), vpc.getId(), true, staticRoute.getCidr(), staticRoute.getGateway(), staticRoute.getId());
+            } else if (StaticRoute.State.Update == staticRoute.getState()) {
+                netrisService.addOrUpdateStaticRoute(vpc.getZoneId(), vpc.getAccountId(), vpc.getDomainId(), vpc.getName(), vpc.getId(), true, staticRoute.getCidr(), staticRoute.getGateway(), staticRoute.getId(), true);
+            }
+        }
         return true;
     }
 
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index a72b50fc8ed..6d97f850243 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -42,10 +42,12 @@ import com.cloud.utils.net.NetUtils;
 import inet.ipaddr.IPAddress;
 import inet.ipaddr.IPAddressString;
 import io.netris.model.NatPostBody;
+import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 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.NetrisAnswer;
@@ -338,6 +340,20 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         return answer.getResult();
     }
 
+    @Override
+    public boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute) {
+        AddOrUpdateNetrisStaticRouteCommand cmd = new AddOrUpdateNetrisStaticRouteCommand(zoneId, accountId, domainId, networkResourceName, networkResourceId, isForVpc, prefix, nextHop, routeId, updateRoute);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId) {
+        DeleteNetrisStaticRouteCommand cmd = new DeleteNetrisStaticRouteCommand(zoneId, accountId, domainId, networkResourceName, networkResourceId, isForVpc, prefix, nextHop, routeId);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
     private String getResourceSuffix(Long vpcId, Long networkId, boolean isForVpc) {
         String suffix;
         if (isForVpc) {
diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index e0abcef63ba..b4e4509dc3a 100644
--- a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -76,4 +76,14 @@ public class NetrisServiceMockTest implements NetrisService {
     public boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp) {
         return true;
     }
+
+    @Override
+    public boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute) {
+        return true;
+    }
+
+    @Override
+    public boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId) {
+        return true;
+    }
 }


(cloudstack) 02/44: List tenants

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit b3c89c62e90331eb1cdb40e65c54b7dc8cf82455
Author: nvazquez <ni...@gmail.com>
AuthorDate: Fri Oct 11 10:05:01 2024 -0300

    List tenants
---
 .../java/org/apache/cloudstack/service/NetrisApiClient.java   |  4 +++-
 .../org/apache/cloudstack/service/NetrisApiClientImpl.java    | 11 ++++++++++-
 .../apache/cloudstack/service/NetrisApiClientImplTest.java    | 10 +++++++++-
 3 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index cc6ffee1102..1085b780a18 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -16,8 +16,10 @@
 // under the License.
 package org.apache.cloudstack.service;
 
+import io.netris.ApiException;
 import io.netris.model.GetSiteBody;
 import io.netris.model.VPCListing;
+import io.netris.model.response.TenantResponse;
 
 import java.util.List;
 
@@ -25,5 +27,5 @@ public interface NetrisApiClient {
     boolean isSessionAlive();
     List<GetSiteBody> listSites();
     List<VPCListing> listVPCs();
-
+    List<TenantResponse> listTenants() throws ApiException;
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 3be955954fe..233e1115826 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -22,6 +22,7 @@ import io.netris.ApiException;
 import io.netris.ApiResponse;
 import io.netris.api.v1.AuthenticationApi;
 import io.netris.api.v1.SitesApi;
+import io.netris.api.v1.TenantsApi;
 import io.netris.api.v2.VpcApi;
 import io.netris.model.AuthSchema;
 import io.netris.model.GetSiteBody;
@@ -29,6 +30,8 @@ import io.netris.model.SitesResponseOK;
 import io.netris.model.VPCListing;
 import io.netris.model.VPCResponseOK;
 import io.netris.model.response.AuthResponse;
+import io.netris.model.response.TenantResponse;
+import io.netris.model.response.TenantsResponse;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -53,7 +56,6 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             ApiResponse<AuthResponse> authResponse = authenticationApi.apiAuthPost(authSchema);
             if (authResponse.getStatusCode() == 200) {
                 String cookie = authResponse.getHeaders().get("Set-Cookie").get(0).split(";")[0];
-                apiClient.setApiKey(cookie);
                 apiClient.addDefaultHeader("Cookie", cookie);
             } else {
                 String msg = String.format("Authentication to the Netris Controller %s failed, please check the credentials provided", apiClient.getBasePath());
@@ -107,4 +109,11 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             throw new CloudRuntimeException(e);
         }
     }
+
+    @Override
+    public List<TenantResponse> listTenants() throws ApiException {
+        TenantsApi api = new TenantsApi(apiClient);
+        ApiResponse<TenantsResponse> response = api.apiTenantsGet();
+        return response.getData().getData();
+    }
 }
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
index dcdcafd18cb..760b25e53b7 100644
--- a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
@@ -16,8 +16,10 @@
 // under the License.
 package org.apache.cloudstack.service;
 
+import io.netris.ApiException;
 import io.netris.model.GetSiteBody;
 import io.netris.model.VPCListing;
+import io.netris.model.response.TenantResponse;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -29,7 +31,7 @@ public class NetrisApiClientImplTest {
     private static final String username = "netris";
     private static final String password = "qHHa$CZ2oJv*@!7mwoSR";
 
-    private NetrisApiClientImpl client = new NetrisApiClientImpl(endpointUrl, username, password);
+    private static final NetrisApiClientImpl client = new NetrisApiClientImpl(endpointUrl, username, password);
 
     @Test
     public void testNetrisAuthStatus() {
@@ -47,4 +49,10 @@ public class NetrisApiClientImplTest {
         List<VPCListing> vpcs = client.listVPCs();
         Assert.assertTrue(vpcs.size() > 0);
     }
+
+    @Test
+    public void testListTenants() throws ApiException {
+        List<TenantResponse> tenants = client.listTenants();
+        Assert.assertTrue(tenants.size() > 0);
+    }
 }


(cloudstack) 36/44: Keep Vpn service to default VPC offering with Natted mode only (#50)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 5289fe4b625a4aa2ace45d13b6f29166d46e3095
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Wed Feb 5 12:13:13 2025 -0300

    Keep Vpn service to default VPC offering with Natted mode only (#50)
    
    * Add Vpn service to default VPC offering with Routed mode
    
    * Revert change on VPC offering and fix VPN service only for Netris NAT mode
---
 server/src/main/java/com/cloud/server/ConfigurationServerImpl.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
index 782734c8c8b..33b8db99bb3 100644
--- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
@@ -1255,7 +1255,6 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
         serviceProviderMap.put(Service.Dhcp, routerProvider);
         serviceProviderMap.put(Service.Dns, routerProvider);
         serviceProviderMap.put(Service.UserData, routerProvider);
-        serviceProviderMap.put(Service.Vpn, routerProvider);
         if (forVpc) {
             serviceProviderMap.put(Service.NetworkACL, provider);
         } else {
@@ -1266,6 +1265,9 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
             serviceProviderMap.put(Service.StaticNat, provider);
             serviceProviderMap.put(Service.PortForwarding, provider);
             serviceProviderMap.put(Service.Lb, provider);
+            if (provider == Provider.Netris) {
+                serviceProviderMap.put(Service.Vpn, routerProvider);
+            }
         }
         return serviceProviderMap;
     }


(cloudstack) 38/44: Phase5 - Support for LB - create, delete and Update operations (#49)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit ca4c13c4a092275666751b5a635bf28dd262e248
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Wed Feb 12 11:29:52 2025 -0500

    Phase5 - Support for LB - create, delete and Update operations (#49)
    
    * Add support for Netris ACLs
    
    * acl support
    
    * Make acl api call to netris to create the rule
    
    * refactor add acl rule to populate the right fields
    
    * support icmp type acl rule
    
    * acl rule creation - move netrisnetworkRule
    
    * Update ACL naming on Netris
    
    * Add support for Deletion of netris acls
    
    * Add support to delete and re-order ACL rules
    
    * support creation of default acl rules and replacing acl rules
    
    * fix NSXNetworkRule
    
    * Fix naming convention for NAT subnets to follow other resources
    
    * Use vpc ID for nat subnets
    
    * Phase5 - Support for LB - create, delete and Update operations
    
    * Use new nat subnet name for deletion of static nat rule
    
    * add support to add netris lb rule
    
    * support deletion of LB rule on Netris
    
    * add checks when editing unsupported fields of LB rule for Netris and hide columns on the UI
    
    * fix test failure
    
    * fix imports
    
    * add license
    
    * address comments
---
 .../com/cloud/network/netris/NetrisLbBackend.java  |  41 ++++
 .../cloud/network/netris/NetrisNetworkRule.java    |  14 ++
 .../com/cloud/network/netris/NetrisService.java    |   3 +
 ...reateOrUpdateNetrisLoadBalancerRuleCommand.java |  72 ++++++
 .../api/DeleteNetrisLoadBalancerRuleCommand.java   |  34 +++
 .../apache/cloudstack/resource/NetrisResource.java |  31 +++
 .../resource/NetrisResourceObjectUtils.java        |   7 +-
 .../apache/cloudstack/service/NetrisApiClient.java |   4 +
 .../cloudstack/service/NetrisApiClientImpl.java    | 269 +++++++++++++++++++--
 .../apache/cloudstack/service/NetrisElement.java   |  92 ++++++-
 .../cloudstack/service/NetrisServiceImpl.java      |  23 ++
 .../network/lb/LoadBalancingRulesManagerImpl.java  |  14 ++
 .../cloud/network/lb/UpdateLoadBalancerTest.java   |   7 +-
 .../cloudstack/service/NetrisServiceMockTest.java  |  10 +
 ui/src/views/network/LoadBalancing.vue             |  33 ++-
 15 files changed, 617 insertions(+), 37 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java
new file mode 100644
index 00000000000..e2dfd6b59c1
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java
@@ -0,0 +1,41 @@
+// 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 com.cloud.network.netris;
+
+public class NetrisLbBackend {
+    private long vmId;
+    private String vmIp;
+    private int port;
+
+    public NetrisLbBackend(long vmId, String vmIp, int port) {
+        this.vmId = vmId;
+        this.vmIp = vmIp;
+        this.port = port;
+    }
+
+    public long getVmId() {
+        return vmId;
+    }
+
+    public String getVmIp() {
+        return vmIp;
+    }
+
+    public int getPort() {
+        return port;
+    }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
index 67612141f65..dcd7bd22acb 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
@@ -19,6 +19,8 @@ package com.cloud.network.netris;
 import com.cloud.network.SDNProviderNetworkRule;
 
 
+import java.util.List;
+
 public class NetrisNetworkRule {
     public enum NetrisRuleAction {
         PERMIT, DENY
@@ -26,11 +28,13 @@ public class NetrisNetworkRule {
 
     private SDNProviderNetworkRule baseRule;
     private NetrisRuleAction aclAction;
+    private List<NetrisLbBackend> lbBackends;
     private String reason;
 
     public NetrisNetworkRule(Builder builder) {
         this.baseRule = builder.baseRule;
         this.aclAction = builder.aclAction;
+        this.lbBackends = builder.lbBackends;
         this.reason = builder.reason;
     }
 
@@ -38,6 +42,10 @@ public class NetrisNetworkRule {
         return aclAction;
     }
 
+    public List<NetrisLbBackend> getLbBackends() {
+        return lbBackends;
+    }
+
     public String getReason() {
         return reason;
     }
@@ -50,6 +58,7 @@ public class NetrisNetworkRule {
     public static class Builder {
         private SDNProviderNetworkRule baseRule;
         private NetrisRuleAction aclAction;
+        private List<NetrisLbBackend> lbBackends;
         private String reason;
 
         public Builder baseRule(SDNProviderNetworkRule baseRule) {
@@ -62,6 +71,11 @@ public class NetrisNetworkRule {
             return this;
         }
 
+        public Builder lbBackends(List<NetrisLbBackend> lbBackends) {
+            this.lbBackends = lbBackends;
+            return this;
+        }
+
         public Builder reason(String reason) {
             this.reason = reason;
             return this;
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index 76a239422e0..d386d4f8d58 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -54,4 +54,7 @@ public interface NetrisService {
     boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId);
 
     boolean releaseNatIp(long zoneId, String publicIp);
+
+    boolean createLbRule(NetrisNetworkRule rule);
+    boolean deleteLbRule(NetrisNetworkRule rule);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisLoadBalancerRuleCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisLoadBalancerRuleCommand.java
new file mode 100644
index 00000000000..34aeb4ca5e0
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisLoadBalancerRuleCommand.java
@@ -0,0 +1,72 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+import com.cloud.network.netris.NetrisLbBackend;
+
+import java.util.List;
+
+public class CreateOrUpdateNetrisLoadBalancerRuleCommand extends NetrisCommand {
+    private final String publicPort;
+    private final String privatePort;
+    private final String algorithm;
+    private final String protocol;
+    List<NetrisLbBackend> lbBackends;
+    private String publicIp;
+    private final Long lbId;
+
+    public CreateOrUpdateNetrisLoadBalancerRuleCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc,
+                                                       List<NetrisLbBackend> lbBackends, long lbId, String publicIp, String publicPort,
+                                                       String privatePort, String algorithm, String protocol) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.lbId = lbId;
+        this.publicIp = publicIp;
+        this.publicPort = publicPort;
+        this.privatePort = privatePort;
+        this.algorithm = algorithm;
+        this.protocol = protocol;
+        this.lbBackends = lbBackends;
+    }
+
+    public String getPublicPort() {
+        return publicPort;
+    }
+
+    public String getPrivatePort() {
+        return privatePort;
+    }
+
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public List<NetrisLbBackend> getLbBackends() {
+        return lbBackends;
+    }
+
+    public Long getLbId() {
+        return lbId;
+    }
+
+    public String getPublicIp() {
+        return publicIp;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisLoadBalancerRuleCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisLoadBalancerRuleCommand.java
new file mode 100644
index 00000000000..c114466d729
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisLoadBalancerRuleCommand.java
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class DeleteNetrisLoadBalancerRuleCommand extends NetrisCommand {
+    private Long lbId;
+
+    public DeleteNetrisLoadBalancerRuleCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc, Long lbId) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.lbId = lbId;
+    }
+
+    public Long getLbId() {
+        return lbId;
+    }
+
+    public void setLbId(Long lbId) {
+        this.lbId = lbId;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 989987b9a41..5d4777d610c 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -32,8 +32,10 @@ import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 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;
@@ -118,6 +120,10 @@ public class NetrisResource implements ServerResource {
             return executeRequest((AddOrUpdateNetrisStaticRouteCommand) cmd);
         } else if (cmd instanceof ReleaseNatIpCommand) {
           return executeRequest((ReleaseNatIpCommand) cmd);
+        } else if (cmd instanceof CreateOrUpdateNetrisLoadBalancerRuleCommand) {
+            return executeRequest((CreateOrUpdateNetrisLoadBalancerRuleCommand) cmd);
+        } else if (cmd instanceof DeleteNetrisLoadBalancerRuleCommand) {
+          return executeRequest((DeleteNetrisLoadBalancerRuleCommand) cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
@@ -352,6 +358,31 @@ public class NetrisResource implements ServerResource {
         return new NetrisAnswer(cmd, true, "OK");
     }
 
+    private Answer executeRequest(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd) {
+        boolean result = netrisApiClient.createLbRule(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Failed to create Netris LB rule for %s: %s, " +
+                    "for private port: %s and public port: %s", getNetworkType(cmd.isVpc()), cmd.getName(), cmd.getPrivatePort(), cmd.getPublicPort(), cmd.getPublicPort()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
+    private Answer executeRequest(DeleteNetrisLoadBalancerRuleCommand cmd) {
+        boolean result = netrisApiClient.deleteLbRule(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Failed to delete Netris LB rule for %s: %s, " +
+                    "for private port: %s and public port: %s", getNetworkType(cmd.isVpc()), cmd.getName()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
+    private String getNetworkType(Boolean isVpc) {
+        if (isVpc) {
+            return "VPC";
+        }
+        return "Network";
+    }
+
     @Override
     public boolean start() {
         return true;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
index 9caff9793ff..296f74b1c29 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -24,7 +24,7 @@ import java.util.Objects;
 public class NetrisResourceObjectUtils {
 
     public enum NetrisObjectType {
-        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT, STATICROUTE, ACL
+        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT, STATICROUTE, ACL, LB
     }
 
     public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, NetrisObjectType netrisObjectType, String... suffixes) {
@@ -59,7 +59,7 @@ public class NetrisResourceObjectUtils {
                 break;
             case IPAM_SUBNET:
                 if (!isZoneLevel) {
-                    if (Objects.nonNull(objectId) && Objects.nonNull(objectName)) {
+                    if (Objects.nonNull(objectId) && Objects.nonNull(objectName) && !isVpc) {
                         stringBuilder.append(String.format("-N%s", objectId));
                     } else {
                         stringBuilder.append(String.format("-V%s-%s", suffixes[0], suffixes[1]));
@@ -86,6 +86,9 @@ public class NetrisResourceObjectUtils {
             case VNET:
             case ACL:
                break;
+            case LB:
+                stringBuilder.append(String.format("%s%s", prefix, objectId));
+                break;
             default:
                 stringBuilder.append(String.format("-%s", objectName));
                 break;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index f5efb4b55ea..91351bf21f7 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -24,8 +24,10 @@ import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 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;
@@ -84,4 +86,6 @@ public interface NetrisApiClient {
     boolean addOrUpdateStaticRoute(AddOrUpdateNetrisStaticRouteCommand cmd);
     boolean deleteStaticRoute(DeleteNetrisStaticRouteCommand cmd);
     boolean releaseNatIp(ReleaseNatIpCommand cmd);
+    boolean createLbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd);
+    boolean deleteLbRule(DeleteNetrisLoadBalancerRuleCommand cmd);
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index e61482a3564..e49c7c5882a 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -16,8 +16,10 @@
 // under the License.
 package org.apache.cloudstack.service;
 
+import com.cloud.network.netris.NetrisLbBackend;
 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;
@@ -29,6 +31,7 @@ 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;
@@ -52,12 +55,20 @@ 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.L4LbTenant;
+import io.netris.model.L4LbVpc;
+import io.netris.model.L4LoadBalancerBackendItem;
+import io.netris.model.L4LoadBalancerItem;
+import io.netris.model.L4lbAddItem;
+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;
@@ -90,10 +101,12 @@ import io.netris.model.response.TenantResponse;
 import io.netris.model.response.TenantsResponse;
 import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 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;
@@ -598,6 +611,169 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return true;
     }
 
+    @Override
+    public boolean createLbRule(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);
+            }
+            if (!matchingLbId.isEmpty()) {
+                logger.warn("LB rule by name: {} already exists", lbName);
+                return true;
+            }
+
+            L4lbAddItem l4lbAddItem = getL4LbRule(cmd, vpcResource, lbName, publicIp, lbBackends);
+            L4LoadBalancerApi loadBalancerApi = apiClient.getApiStubForMethod(L4LoadBalancerApi.class);
+            ResAddEditBody response = loadBalancerApi.apiV2L4lbPost(l4lbAddItem);
+            if (Objects.isNull(response) || Boolean.FALSE.equals(response.isIsSuccess())) {
+                throw new CloudRuntimeException("Failed to create Netris LB rule");
+            }
+        } catch (ApiException e) {
+            logAndThrowException("Failed to create Netris load balancer rule", e);
+        }
+        return true;
+    }
+
+    private L4lbAddItem getL4LbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd, VPCListing vpcResource, String lbName,
+                             String publicIp, List<NetrisLbBackend> lbBackends) {
+        L4lbAddItem l4lbAddItem = 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) ? L4lbAddItem.IpFamilyEnum.IPv4 : L4lbAddItem.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(L4lbAddItem.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();
+        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());
+        } catch (ApiException e) {
+            logAndThrowException("Failed to delete Netris load balancer rule", e);
+        }
+        return true;
+    }
+
+    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 Pair<Boolean, RoutesGetBody> staticRouteExists(Integer netrisVpcId, String prefix, String nextHop, String description) {
         try {
             FilterByVpc vpcFilter = new FilterByVpc();
@@ -904,7 +1080,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
 
         if (StringUtils.isNotBlank(targetIpSubnet) && existsDestinationSubnet(targetIpSubnet)) {
-            logger.debug(String.format("Creating subnet with NAT purpose for %s", targetIpSubnet));
+            logger.debug("Creating subnet with NAT purpose for {}}", targetIpSubnet);
             createNatSubnet(cmd, targetIpSubnet, vpcResource.getId());
         }
 
@@ -1022,6 +1198,27 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
+    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);
     }
@@ -1204,33 +1401,38 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
+    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 = 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);
-            }
+            SubnetBody subnetBody = getIpamSubnetBody(vpc, purpose, subnetName, subnetPrefix, isGlobalRouting);
             IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
             InlineResponse2004 subnetResponse = ipamApi.apiV2IpamSubnetPost(subnetBody);
             if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
@@ -1245,6 +1447,25 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
+    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 netrisV6Gateway, Integer vxlanId, String netrisTag) {
         logger.debug("Creating Netris VPC vNet {} for CIDR {}", netrisVnetName, netrisGateway);
         try {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 0f25d216719..12e7239a8ee 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -23,6 +23,7 @@ import com.cloud.agent.api.AgentControlCommand;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
 import com.cloud.agent.api.StartupCommand;
+import com.cloud.agent.api.to.LoadBalancerTO;
 import com.cloud.api.ApiDBUtils;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.dao.DataCenterDao;
@@ -45,16 +46,21 @@ import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.SDNProviderOpObject;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
+import com.cloud.network.dao.LoadBalancerVMMapDao;
+import com.cloud.network.dao.LoadBalancerVMMapVO;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.element.DhcpServiceProvider;
 import com.cloud.network.element.DnsServiceProvider;
 import com.cloud.network.element.IpDeployer;
+import com.cloud.network.element.LoadBalancingServiceProvider;
 import com.cloud.network.element.NetworkACLServiceProvider;
 import com.cloud.network.element.PortForwardingServiceProvider;
 import com.cloud.network.element.StaticNatServiceProvider;
 import com.cloud.network.element.VirtualRouterElement;
 import com.cloud.network.element.VpcProvider;
+import com.cloud.network.lb.LoadBalancingRule;
+import com.cloud.network.netris.NetrisLbBackend;
 import com.cloud.network.netris.NetrisService;
 import com.cloud.network.rules.FirewallRule;
 import com.cloud.network.rules.LoadBalancerContainer;
@@ -107,7 +113,8 @@ import java.util.Set;
 
 @Component
 public class NetrisElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider,
-        StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider, NetworkACLServiceProvider, ResourceStateAdapter, Listener {
+        StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider, NetworkACLServiceProvider,
+        LoadBalancingServiceProvider, ResourceStateAdapter, Listener {
 
     @Inject
     NetworkModel networkModel;
@@ -133,6 +140,8 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
     private IPAddressDao ipAddressDao;
     @Inject
     private VMInstanceDao vmInstanceDao;
+    @Inject
+    LoadBalancerVMMapDao lbVmMapDao;
 
     protected Logger logger = LogManager.getLogger(getClass());
 
@@ -693,4 +702,85 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
                 return 0;
         }
     }
+
+    private SDNProviderOpObject getNetrisObject(Network network) {
+        Pair<VpcVO, NetworkVO> vpcOrNetwork = getVpcOrNetwork(network.getVpcId(), network.getId());
+        VpcVO vpc = vpcOrNetwork.first();
+        NetworkVO networkVO = vpcOrNetwork.second();
+        long domainId = getResourceId("domain", vpc, networkVO);
+        long accountId = getResourceId("account", vpc, networkVO);
+        long zoneId = getResourceId("zone", vpc, networkVO);
+
+        return new SDNProviderOpObject.Builder()
+                .vpcVO(vpc)
+                .networkVO(networkVO)
+                .domainId(domainId)
+                .accountId(accountId)
+                .zoneId(zoneId)
+                .build();
+    }
+
+    @Override
+    public boolean applyLBRules(Network network, List<LoadBalancingRule> rules) throws ResourceUnavailableException {
+        boolean result = true;
+        for (LoadBalancingRule loadBalancingRule : rules) {
+            IPAddressVO publicIp = ipAddressDao.findByIpAndDcId(network.getDataCenterId(),
+                    loadBalancingRule.getSourceIp().addr());
+
+            List<NetrisLbBackend> lbBackends = getLoadBalancerBackends(loadBalancingRule);
+            SDNProviderOpObject netrisObject = getNetrisObject(network);
+            SDNProviderNetworkRule baseNetworkRule = new SDNProviderNetworkRule.Builder()
+                    .setDomainId(netrisObject.getDomainId())
+                    .setAccountId(netrisObject.getAccountId())
+                    .setZoneId(netrisObject.getZoneId())
+                    .setNetworkResourceId(netrisObject.getNetworkResourceId())
+                    .setNetworkResourceName(netrisObject.getNetworkResourceName())
+                    .setVpcResource(netrisObject.isVpcResource())
+                    .setPublicIp(LoadBalancerContainer.Scheme.Public == loadBalancingRule.getScheme() ?
+                            publicIp.getAddress().addr() : loadBalancingRule.getSourceIp().addr())
+                    .setPrivatePort(String.valueOf(loadBalancingRule.getDefaultPortStart()))
+                    .setPublicPort(String.valueOf(loadBalancingRule.getSourcePortStart()))
+                    .setRuleId(loadBalancingRule.getId())
+                    .setProtocol(loadBalancingRule.getProtocol().toUpperCase(Locale.ROOT))
+                    .setAlgorithm(loadBalancingRule.getAlgorithm())
+                    .build();
+
+            NetrisNetworkRule networkRule = new NetrisNetworkRule.Builder()
+                    .baseRule(baseNetworkRule)
+                    .lbBackends(lbBackends)
+                    .build();
+            if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(loadBalancingRule.getState())) {
+                result &= netrisService.createLbRule(networkRule);
+            } else if (loadBalancingRule.getState() == FirewallRule.State.Revoke) {
+                result &= netrisService.deleteLbRule(networkRule);
+            }
+        }
+        return result;
+    }
+
+    private List<NetrisLbBackend> getLoadBalancerBackends(LoadBalancingRule lbRule) {
+        List<LoadBalancerVMMapVO> lbVms = lbVmMapDao.listByLoadBalancerId(lbRule.getId(), false);
+        List<NetrisLbBackend> lbMembers = new ArrayList<>();
+
+        for (LoadBalancerVMMapVO lbVm : lbVms) {
+            NetrisLbBackend member = new NetrisLbBackend(lbVm.getInstanceId(), lbVm.getInstanceIp(), lbRule.getDefaultPortStart());
+            lbMembers.add(member);
+        }
+        return lbMembers;
+    }
+
+    @Override
+    public boolean validateLBRule(Network network, LoadBalancingRule rule) {
+        return true;
+    }
+
+    @Override
+    public List<LoadBalancerTO> updateHealthChecks(Network network, List<LoadBalancingRule> lbrules) {
+        return List.of();
+    }
+
+    @Override
+    public boolean handlesOnlyRulesInTransitionState() {
+        return false;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 337ea849dcb..62064c1ac07 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -44,10 +44,12 @@ import com.cloud.utils.net.NetUtils;
 import inet.ipaddr.IPAddress;
 import inet.ipaddr.IPAddressString;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 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;
@@ -455,6 +457,27 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         return answer.getResult();
     }
 
+    @Override
+    public boolean createLbRule(NetrisNetworkRule rule) {
+        SDNProviderNetworkRule baseRule = rule.getBaseRule();
+        CreateOrUpdateNetrisLoadBalancerRuleCommand cmd = new CreateOrUpdateNetrisLoadBalancerRuleCommand(baseRule.getZoneId(), baseRule.getAccountId(),
+                baseRule.getDomainId(), baseRule.getNetworkResourceName(), baseRule.getNetworkResourceId(), baseRule.isVpcResource(),
+                rule.getLbBackends(), baseRule.getRuleId(), baseRule.getPublicIp(), baseRule.getPublicPort(),
+                baseRule.getPrivatePort(), baseRule.getAlgorithm(), baseRule.getProtocol());
+        NetrisAnswer answer = sendNetrisCommand(cmd, baseRule.getZoneId());
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean deleteLbRule(NetrisNetworkRule rule) {
+        SDNProviderNetworkRule baseRule = rule.getBaseRule();
+        DeleteNetrisLoadBalancerRuleCommand cmd = new DeleteNetrisLoadBalancerRuleCommand(baseRule.getZoneId(), baseRule.getAccountId(),
+                baseRule.getDomainId(), baseRule.getNetworkResourceName(), baseRule.getNetworkResourceId(), baseRule.isVpcResource(),
+                baseRule.getRuleId());
+        NetrisAnswer answer = sendNetrisCommand(cmd, baseRule.getZoneId());
+        return answer.getResult();
+    }
+
     private String getResourceSuffix(Long vpcId, Long networkId, boolean isForVpc) {
         String suffix;
         if (isForVpc) {
diff --git a/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
index 320f064a31e..bb1f4ec1b9b 100644
--- a/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
@@ -24,6 +24,7 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -2248,6 +2249,7 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
             lb.setLbProtocol(lbProtocol);
         }
 
+        validateInputsForExternalNetworkProvider(lb, algorithm, lbProtocol);
         // Validate rule in LB provider
         LoadBalancingRule rule = getLoadBalancerRuleToApply(lb);
         if (!validateLbRule(rule)) {
@@ -2297,6 +2299,18 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
         return lb;
     }
 
+    private void validateInputsForExternalNetworkProvider(LoadBalancerVO lb, String algorithm, String protocol) {
+        Network network = _networkDao.findById(lb.getNetworkId());
+        if (_networkOfferingServiceDao.canProviderSupportServiceInNetworkOffering(network.getNetworkOfferingId(), Service.Lb, Provider.Netris)) {
+            if (Objects.nonNull(algorithm)) {
+                throw new InvalidParameterValueException(String.format("Algorithm: %s specified for Netris Provider is not supported.", algorithm));
+            }
+            if (Objects.nonNull(protocol) && "tcp-proxy".equalsIgnoreCase(protocol)) {
+                throw new InvalidParameterValueException("TCP Proxy protocol is not supported for Netris Provider.");
+            }
+        }
+    }
+
     @Override
     public Pair<List<? extends UserVm>, List<String>> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException {
         Account caller = CallContext.current().getCallingAccount();
diff --git a/server/src/test/java/com/cloud/network/lb/UpdateLoadBalancerTest.java b/server/src/test/java/com/cloud/network/lb/UpdateLoadBalancerTest.java
index c3e8923b943..729d2ea8ff5 100644
--- a/server/src/test/java/com/cloud/network/lb/UpdateLoadBalancerTest.java
+++ b/server/src/test/java/com/cloud/network/lb/UpdateLoadBalancerTest.java
@@ -33,6 +33,7 @@ import com.cloud.network.dao.LoadBalancerVO;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.element.LoadBalancingServiceProvider;
+import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
 import com.cloud.user.Account;
 import com.cloud.user.AccountVO;
 import com.cloud.user.MockAccountManagerImpl;
@@ -52,6 +53,7 @@ import java.util.UUID;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
@@ -63,6 +65,7 @@ public class UpdateLoadBalancerTest {
     private LoadBalancerDao lbDao = Mockito.mock(LoadBalancerDao.class);
     private NetworkDao netDao = Mockito.mock(NetworkDao.class);
     private NetworkModel netModel = Mockito.mock(NetworkModel.class);
+    private NetworkOfferingServiceMapDao ntwkOffServiceMapDao = Mockito.mock(NetworkOfferingServiceMapDao.class);
     private LoadBalancingServiceProvider lbServiceProvider= Mockito.mock(LoadBalancingServiceProvider.class);
 
     private static long domainId = 5L;
@@ -73,6 +76,7 @@ public class UpdateLoadBalancerTest {
         _lbMgr._accountMgr = new MockAccountManagerImpl();
         _lbMgr._autoScaleVmGroupDao = Mockito.mock(AutoScaleVmGroupDao.class);
         _lbMgr._networkDao = netDao;
+        _lbMgr._networkOfferingServiceDao = ntwkOffServiceMapDao;
         _lbMgr._networkModel = netModel;
         _lbMgr._lb2healthcheckDao = Mockito.mock(LBHealthCheckPolicyDao.class);
         _lbMgr._lb2stickinesspoliciesDao = Mockito.mock(LBStickinessPolicyDao.class);
@@ -99,7 +103,8 @@ public class UpdateLoadBalancerTest {
         when(netDao.findById(anyLong())).thenReturn(Mockito.mock(NetworkVO.class));
         when(lbServiceProvider.validateLBRule(any(Network.class), any(LoadBalancingRule.class))).thenReturn(true);
         when(lbDao.update(isNull(), eq(lb))).thenReturn(true);
-
+        when(netDao.findById(nullable(Long.class))).thenReturn(Mockito.mock(NetworkVO.class));
+        when(ntwkOffServiceMapDao.canProviderSupportServiceInNetworkOffering(nullable(Long.class), any(Network.Service.class), any(Network.Provider.class))).thenReturn(false);
         _lbMgr.updateLoadBalancerRule(updateLbRuleCmd);
 
         InOrder inOrder = Mockito.inOrder(lbServiceProvider, lbDao);
diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index b8e4835f589..99f319d6de7 100644
--- a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -105,4 +105,14 @@ public class NetrisServiceMockTest implements NetrisService {
     public boolean releaseNatIp(long zoneId, String publicIp) {
         return true;
     }
+
+    @Override
+    public boolean createLbRule(NetrisNetworkRule rule) {
+        return true;
+    }
+
+    @Override
+    public boolean deleteLbRule(NetrisNetworkRule rule) {
+        return true;
+    }
 }
diff --git a/ui/src/views/network/LoadBalancing.vue b/ui/src/views/network/LoadBalancing.vue
index eeec232cee5..f20ccc0e29f 100644
--- a/ui/src/views/network/LoadBalancing.vue
+++ b/ui/src/views/network/LoadBalancing.vue
@@ -40,7 +40,7 @@
           <tooltip-label :title="$t('label.cidrlist')" bold :tooltip="createLoadBalancerRuleParams.cidrlist.description" :tooltip-placement="'right'"/>
           <a-input v-model:value="newRule.cidrlist"></a-input>
         </div>
-        <div class="form__item">
+        <div class="form__item" v-if="lbProvider !== 'Netris'">
           <div class="form__label">{{ $t('label.algorithm') }}</div>
           <a-select
             v-model:value="newRule.algorithm"
@@ -64,7 +64,7 @@
             :filterOption="(input, option) => {
               return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
+            <a-select-option v-if="lbProvider !== 'Netris'" value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
             <a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
             <a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
           </a-select>
@@ -409,7 +409,7 @@
           <p class="edit-rule__label">{{ $t('label.name') }}</p>
           <a-input v-focus="true" v-model:value="editRuleDetails.name" />
         </div>
-        <div class="edit-rule__item">
+        <div v-if="lbProvider !== 'Netris'" class="edit-rule__item">
           <p class="edit-rule__label">{{ $t('label.algorithm') }}</p>
           <a-select
             v-model:value="editRuleDetails.algorithm"
@@ -423,7 +423,7 @@
             <a-select-option value="source" :label="$t('label.lb.algorithm.source')">{{ $t('label.lb.algorithm.source') }}</a-select-option>
           </a-select>
         </div>
-        <div class="edit-rule__item">
+        <div v-if="lbProvider !== 'Netris'" class="edit-rule__item">
           <p class="edit-rule__label">{{ $t('label.protocol') }}</p>
           <a-select
             v-model:value="editRuleDetails.protocol"
@@ -781,9 +781,11 @@ export default {
         vmguestip: [],
         cidrlist: ''
       },
+      lbProvider: null,
       addVmModalVisible: false,
       addVmModalLoading: false,
       addVmModalNicLoading: false,
+      zoneloading: false,
       vms: [],
       nics: [],
       totalCount: 0,
@@ -802,10 +804,6 @@ export default {
           title: this.$t('label.privateport'),
           dataIndex: 'privateport'
         },
-        {
-          key: 'algorithm',
-          title: this.$t('label.algorithm')
-        },
         {
           key: 'cidrlist',
           title: this.$t('label.cidrlist')
@@ -979,6 +977,7 @@ export default {
     fetchData () {
       this.fetchListTiers()
       this.fetchLBRules()
+      this.fetchZone()
     },
     fetchListTiers () {
       this.tiers.loading = true
@@ -1073,6 +1072,22 @@ export default {
         })
       })
     },
+    fetchZone () {
+      this.zoneloading = true
+      api('listZones', {
+        id: this.resource.zoneid
+      }).then(response => {
+        this.lbProvider = response?.listzonesresponse?.zone?.[0]?.provider || null
+      }).finally(() => {
+        this.zoneloading = false
+        if (this.lbProvider !== 'Netris') {
+          this.column.push({
+            key: 'algorithm',
+            title: this.$t('label.algorithm')
+          })
+        }
+      })
+    },
     returnAlgorithmName (name) {
       switch (name) {
         case 'leastconn':
@@ -1377,7 +1392,7 @@ export default {
       this.selectedRule = rule
       this.editRuleModalVisible = true
       this.editRuleDetails.name = this.selectedRule.name
-      this.editRuleDetails.algorithm = this.selectedRule.algorithm
+      this.editRuleDetails.algorithm = this.lbProvider !== 'Netris' ? this.selectedRule.algorithm : undefined
       this.editRuleDetails.protocol = this.selectedRule.protocol
     },
     handleSubmitEditForm () {


(cloudstack) 35/44: Fix naming convention for NAT subnets to follow other resources (#47)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 427d7328c14c82f8c0578acf328aedf99d798e3c
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Wed Feb 5 09:29:30 2025 -0500

    Fix naming convention for NAT subnets to follow other resources (#47)
    
    * Fix naming convention for NAT subnets to follow other resources
    
    * Use vpc ID for nat subnets
    
    * Use new nat subnet name for deletion of static nat rule
    
    * fix naming convevntion for nat subnet
---
 .../cloudstack/resource/NetrisResourceObjectUtils.java  |  9 ++++++++-
 .../apache/cloudstack/service/NetrisApiClientImpl.java  | 17 +++++++++--------
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
index 32225fdd199..9caff9793ff 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -19,6 +19,8 @@ package org.apache.cloudstack.resource;
 import org.apache.cloudstack.agent.api.NetrisCommand;
 import org.apache.commons.lang3.ArrayUtils;
 
+import java.util.Objects;
+
 public class NetrisResourceObjectUtils {
 
     public enum NetrisObjectType {
@@ -57,7 +59,12 @@ public class NetrisResourceObjectUtils {
                 break;
             case IPAM_SUBNET:
                 if (!isZoneLevel) {
-                    stringBuilder.append(String.format("-N%s", objectId));
+                    if (Objects.nonNull(objectId) && Objects.nonNull(objectName)) {
+                        stringBuilder.append(String.format("-N%s", objectId));
+                    } else {
+                        stringBuilder.append(String.format("-V%s-%s", suffixes[0], suffixes[1]));
+                        return stringBuilder.toString();
+                    }
                 }
                 break;
             case SNAT:
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 681924d82d8..7f80947974d 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -299,7 +299,9 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             if (ruleExists) {
                 deleteNatRule(natRuleName, existingNatRule.getId(), vpcResource.getName());
                 if (cmd.getNatRuleType().equals("STATICNAT")) {
-                    deleteNatSubnet(vpcResource.getId(), cmd.getNatIp());
+                    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) {
@@ -893,7 +895,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
 
         if (StringUtils.isNotBlank(targetIpSubnet) && existsDestinationSubnet(targetIpSubnet)) {
             logger.debug(String.format("Creating subnet with NAT purpose for %s", targetIpSubnet));
-            createNatSubnet(targetIpSubnet, vpcResource.getId());
+            createNatSubnet(cmd, targetIpSubnet, vpcResource.getId());
         }
 
         NatGetBody existingNatRule = netrisNatRuleExists(ruleName);
@@ -959,7 +961,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
                 return false;
             }
             // Create a /32 subnet for the DNAT IP
-            createNatSubnet(natIP, vpcResource.getId());
+            createNatSubnet(cmd, natIP, vpcResource.getId());
             NatApi natApi = apiClient.getApiStubForMethod(NatApi.class);
             NatPostBody natBody = new NatPostBody();
             natBody.setAction(NatPostBody.ActionEnum.DNAT);
@@ -993,15 +995,15 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return true;
     }
 
-    private void createNatSubnet(String natIp, Integer netrisVpcId) {
+    private void createNatSubnet(NetrisCommand cmd, String natIp, Integer netrisVpcId) {
         try {
             FilterByVpc vpcFilter = new FilterByVpc();
             vpcFilter.add(netrisVpcId);
-            String netrisSubnetName = natIp;
+            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(natIp, natIp, SubnetBody.PurposeEnum.NAT, systemVpc, null);
+                createIpamSubnetInternal(netrisSubnetName, natIp, SubnetBody.PurposeEnum.NAT, systemVpc, null);
                 return;
             }
             logger.debug("NAT subnet: {} already exists", natIp);
@@ -1344,10 +1346,9 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
     }
 
-    private void deleteNatSubnet(Integer netrisVpcId, String natIp) {
+    private void deleteNatSubnet(String netrisSubnetName, Integer netrisVpcId, String natIp) {
         FilterByVpc vpcFilter = new FilterByVpc();
         vpcFilter.add(netrisVpcId);
-        String netrisSubnetName = natIp + "/32";
         deleteSubnetInternal(vpcFilter, null, netrisSubnetName);
     }
 }


(cloudstack) 29/44: Netris VPN: Fix s2s vpn status update and isolated network implementation (#42)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit b525255e8519791d4f2f0a1d8fb5c5c5ceb3b4c2
Author: Wei Zhou <we...@apache.org>
AuthorDate: Fri Dec 27 09:00:14 2024 +0100

    Netris VPN: Fix s2s vpn status update and isolated network implementation (#42)
    
    * server: fix NPE when deploy vm on isolated network
    
    * vpn: fix s2s vpn status is not updated
    
    Prior to this fix
    ```
    java.lang.IllegalArgumentException: Class com.cloud.agent.api.CheckS2SVpnConnectionsAnswer declares multiple JSON fields named 'details'; conflict is caused by fields com.cloud.agent.api.CheckS2SVpnConnectionsAnswer#details and com.cloud.agent.api.Answer#details
            at com.cloud.agent.transport.ResponseTest.testCheckS2SVpnConnectionsAnswer(ResponseTest.java:42)
    ```
    
    * test: fix test_01_vpn_usage as now it is only possible to create VPN on Source NAT if it uses VR
    
    * VR: fix unable to create remote access VPN on regular isolated network
    
    the error is
    ```
      File "/opt/cloud/bin/configure.py", line 1242, in process
        self.remoteaccessvpn_iptables(self.dbag['public_interface'], public_ip, self.dbag[public_ip])
                                      ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
    KeyError: 'public_interface'
    ```
---
 .../agent/api/CheckS2SVpnConnectionsAnswer.java    |  1 -
 .../com/cloud/agent/transport/ResponseTest.java    | 46 ++++++++++++++++++++++
 .../cloud/network/router/NetworkHelperImpl.java    |  3 +-
 systemvm/debian/opt/cloud/bin/configure.py         |  2 +-
 test/integration/smoke/test_usage.py               | 12 +++++-
 5 files changed, 59 insertions(+), 5 deletions(-)

diff --git a/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java
index b299c602dde..351702a048c 100644
--- a/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java
+++ b/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java
@@ -25,7 +25,6 @@ import java.util.Map;
 public class CheckS2SVpnConnectionsAnswer extends Answer {
     Map<String, Boolean> ipToConnected;
     Map<String, String> ipToDetail;
-    String details;
 
     protected CheckS2SVpnConnectionsAnswer() {
         ipToConnected = new HashMap<String, Boolean>();
diff --git a/core/src/test/java/com/cloud/agent/transport/ResponseTest.java b/core/src/test/java/com/cloud/agent/transport/ResponseTest.java
new file mode 100644
index 00000000000..06869b42eb9
--- /dev/null
+++ b/core/src/test/java/com/cloud/agent/transport/ResponseTest.java
@@ -0,0 +1,46 @@
+//
+// 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 com.cloud.agent.transport;
+
+import junit.framework.TestCase;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.junit.Assert;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer;
+
+import com.cloud.agent.transport.Request.Version;
+
+public class ResponseTest extends TestCase {
+    protected Logger logger = LogManager.getLogger(getClass());
+
+    public void testCheckS2SVpnConnectionsAnswer() {
+        logger.info("Testing CheckS2SVpnConnectionsAnswer");
+        String content = "[{\"com.cloud.agent.api.CheckS2SVpnConnectionsAnswer\":{\"ipToConnected\":{\"10.0.53.13\":true}," +
+                "\"ipToDetail\":{\"10.0.53.13\":\"IPsec SA found;Site-to-site VPN have connected\"}," +
+                "\"details\":\"10.0.53.13:0:IPsec SA found;Site-to-site VPN have connected\\u0026\\n\"," +
+                "\"result\":true,\"contextMap\":{},\"wait\":0,\"bypassHostMaintenance\":false}}]";
+        Response response = new Response(Version.v2, 1L, 2L, 3L, 1L, (short)1, content);
+        Answer answer = response.getAnswer();
+        Assert.assertTrue(answer instanceof CheckS2SVpnConnectionsAnswer);
+    }
+
+}
diff --git a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
index dd82a2b8203..515770f1773 100644
--- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
+++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
@@ -780,7 +780,8 @@ public class NetworkHelperImpl implements NetworkHelper {
             logger.debug("Adding nic for Virtual Router in Guest network " + guestNetwork);
             String defaultNetworkStartIp = null, defaultNetworkStartIpv6 = null;
             final Nic placeholder = _networkModel.getPlaceholderNicForRouter(guestNetwork, routerDeploymentDefinition.getPodId());
-            if (!routerDeploymentDefinition.isPublicNetwork() || !vpcManager.isSrcNatIpRequiredForVpcVr(routerDeploymentDefinition.getVpc().getVpcOfferingId())) {
+            if (!routerDeploymentDefinition.isPublicNetwork()
+                    || !_networkModel.isAnyServiceSupportedInNetwork(guestNetwork.getId(), Network.Provider.VPCVirtualRouter, Network.Service.SourceNat, Network.Service.Gateway)) {
                 if (guestNetwork.getCidr() != null) {
                     if (placeholder != null && placeholder.getIPv4Address() != null) {
                         logger.debug("Requesting ipv4 address " + placeholder.getIPv4Address() + " stored in placeholder nic for the network "
diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py
index c4201b34e3b..4f769bcbe2c 100755
--- a/systemvm/debian/opt/cloud/bin/configure.py
+++ b/systemvm/debian/opt/cloud/bin/configure.py
@@ -1239,7 +1239,7 @@ class CsRemoteAccessVpn(CsDataBag):
                             break
                 else:
                     self.configure_l2tpIpsec(public_ip, self.dbag[public_ip])
-                    self.remoteaccessvpn_iptables(self.dbag['public_interface'], public_ip, self.dbag[public_ip])
+                    self.remoteaccessvpn_iptables(self.dbag[public_ip]['public_interface'], public_ip, self.dbag[public_ip])
 
                 CsHelper.execute("ipsec update")
                 CsHelper.execute("systemctl start xl2tpd")
diff --git a/test/integration/smoke/test_usage.py b/test/integration/smoke/test_usage.py
index 1a6ff37cedb..a65e4917a46 100644
--- a/test/integration/smoke/test_usage.py
+++ b/test/integration/smoke/test_usage.py
@@ -1732,6 +1732,14 @@ class TestVpnUsage(cloudstackTestCase):
             domainid=cls.virtual_machine.domainid,
             services=cls.services["server"]
         )
+        src_nat_list = PublicIPAddress.list(
+            cls.api_client,
+            accountid=cls.virtual_machine.account,
+            zoneid=cls.virtual_machine.zoneid,
+            domainid=cls.virtual_machine.domainid,
+            issourcenat=True
+        )
+        cls.public_ip = src_nat_list[0]
         return
 
     @classmethod
@@ -1770,11 +1778,11 @@ class TestVpnUsage(cloudstackTestCase):
         # 4. Delete this account.
 
         self.debug("Created VPN with public IP: %s" %
-                   self.public_ip.ipaddress.id)
+                   self.public_ip.ipaddress)
         # Assign VPN to Public IP
         vpn = Vpn.create(
             self.apiclient,
-            self.public_ip.ipaddress.id,
+            self.public_ip.id,
             account=self.account.name,
             domainid=self.account.domainid
         )


(cloudstack) 03/44: Delegate API classes creation to the SDK and simply invoke the desired API class through CloudStack (#7)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 9f76e377d49cf3086b6302a65c3fc1a4d0a0892a
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Tue Oct 15 12:23:03 2024 -0300

    Delegate API classes creation to the SDK and simply invoke the desired API class through CloudStack (#7)
    
    * Delegate API classes creation to the SDK and simply invoke the desired API class through CloudStack
    
    * Pass default auth scheme for now
---
 .../cloudstack/service/NetrisApiClientImpl.java    | 75 +++++++++-------------
 1 file changed, 31 insertions(+), 44 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 233e1115826..e2e4c9a90f1 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -24,7 +24,6 @@ import io.netris.api.v1.AuthenticationApi;
 import io.netris.api.v1.SitesApi;
 import io.netris.api.v1.TenantsApi;
 import io.netris.api.v2.VpcApi;
-import io.netris.model.AuthSchema;
 import io.netris.model.GetSiteBody;
 import io.netris.model.SitesResponseOK;
 import io.netris.model.VPCListing;
@@ -35,85 +34,73 @@ import io.netris.model.response.TenantsResponse;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-import java.math.BigDecimal;
 import java.util.List;
 
 public class NetrisApiClientImpl implements NetrisApiClient {
 
     private final Logger logger = LogManager.getLogger(getClass());
 
-    private static final ApiClient apiClient = new ApiClient();
+    private static ApiClient apiClient;
 
     public NetrisApiClientImpl(String endpointBaseUrl, String username, String password) {
-        apiClient.setBasePath(endpointBaseUrl);
-        authenticate(username, password);
-    }
-
-    private void authenticate(String username, String password) {
-        AuthSchema authSchema = createAuthSchema(username, password);
-        AuthenticationApi authenticationApi = new AuthenticationApi(apiClient);
         try {
-            ApiResponse<AuthResponse> authResponse = authenticationApi.apiAuthPost(authSchema);
-            if (authResponse.getStatusCode() == 200) {
-                String cookie = authResponse.getHeaders().get("Set-Cookie").get(0).split(";")[0];
-                apiClient.addDefaultHeader("Cookie", cookie);
-            } else {
-                String msg = String.format("Authentication to the Netris Controller %s failed, please check the credentials provided", apiClient.getBasePath());
-                logger.error(msg);
-                throw new CloudRuntimeException(msg);
-            }
+            apiClient = new ApiClient(endpointBaseUrl, username, password, 1L);
         } catch (ApiException e) {
-            String msg = String.format("Error authenticating to the Netris Controller %s: Code %s - Message: %s", apiClient.getBasePath(), e.getCode(), e.getResponseBody());
-            logger.error(msg, e);
-            throw new CloudRuntimeException(msg);
+            logAndThrowException(String.format("Error creating the Netris API Client for %s", endpointBaseUrl), e);
         }
     }
 
-    private AuthSchema createAuthSchema(String username, String password) {
-        AuthSchema authSchema = new AuthSchema();
-        authSchema.setUser(username);
-        authSchema.setPassword(password);
-        authSchema.setAuthSchemeID(new BigDecimal(1));
-        return authSchema;
+    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);
+        throw new CloudRuntimeException(msg);
     }
 
     @Override
     public boolean isSessionAlive() {
-        AuthenticationApi api = new AuthenticationApi(apiClient);
+        ApiResponse<AuthResponse> response = null;
         try {
-            ApiResponse<AuthResponse> response = api.apiAuthGet();
-            return response.getStatusCode() == 200;
+            AuthenticationApi api = apiClient.getApiStubForMethod(AuthenticationApi.class);
+            response = api.apiAuthGet();
         } catch (ApiException e) {
-            throw new CloudRuntimeException(e);
+            logAndThrowException("Error checking the Netris API session is alive", e);
         }
+        return response != null && response.getStatusCode() == 200;
     }
 
     @Override
     public List<GetSiteBody> listSites() {
-        SitesApi api = new SitesApi(apiClient);
+        SitesResponseOK response = null;
         try {
-            SitesResponseOK response = api.apiSitesGet();
-            return response.getData();
+            SitesApi api = apiClient.getApiStubForMethod(SitesApi.class);
+            response = api.apiSitesGet();
         } catch (ApiException e) {
-            throw new CloudRuntimeException(e);
+            logAndThrowException("Error listing Netris Sites", e);
         }
+        return response != null ? response.getData() : null;
     }
 
     @Override
     public List<VPCListing> listVPCs() {
-        VpcApi api = new VpcApi(apiClient);
+        VPCResponseOK response = null;
         try {
-            VPCResponseOK response = api.apiV2VpcGet();
-            return response.getData();
+            VpcApi api = apiClient.getApiStubForMethod(VpcApi.class);
+            response = api.apiV2VpcGet();
         } catch (ApiException e) {
-            throw new CloudRuntimeException(e);
+            logAndThrowException("Error listing Netris VPCs", e);
         }
+        return response != null ? response.getData() : null;
     }
 
     @Override
-    public List<TenantResponse> listTenants() throws ApiException {
-        TenantsApi api = new TenantsApi(apiClient);
-        ApiResponse<TenantsResponse> response = api.apiTenantsGet();
-        return response.getData().getData();
+    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;
     }
 }


(cloudstack) 22/44: Fix unit tests (#30)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit aa222a51ab9ac7a50d69f62591a5a30a1038e8c1
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Mon Dec 9 14:37:16 2024 +0100

    Fix unit tests (#30)
---
 .../cloudstack/service/NetrisServiceMockTest.java  | 79 ++++++++++++++++++++++
 .../src/test/resources/createNetworkOffering.xml   |  1 +
 2 files changed, 80 insertions(+)

diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
new file mode 100644
index 00000000000..e0abcef63ba
--- /dev/null
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -0,0 +1,79 @@
+// 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.network.IpAddress;
+import com.cloud.network.SDNProviderNetworkRule;
+import com.cloud.network.netris.NetrisService;
+import com.cloud.network.vpc.Vpc;
+
+public class NetrisServiceMockTest implements NetrisService {
+    @Override
+    public boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId) {
+        return true;
+    }
+
+    @Override
+    public boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork) {
+        return true;
+    }
+
+    @Override
+    public boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc) {
+        return true;
+    }
+
+    @Override
+    public boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
+        return true;
+    }
+
+    @Override
+    public boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
+        return true;
+    }
+
+    @Override
+    public boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String sourceNatIp) {
+        return true;
+    }
+
+    @Override
+    public boolean createPortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule) {
+        return true;
+    }
+
+    @Override
+    public boolean deletePortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule) {
+        return true;
+    }
+
+    @Override
+    public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
+        return true;
+    }
+
+    @Override
+    public boolean createStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String vpcCidr, String staticNatIp, String vmIp) {
+        return true;
+    }
+
+    @Override
+    public boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp) {
+        return true;
+    }
+}
diff --git a/server/src/test/resources/createNetworkOffering.xml b/server/src/test/resources/createNetworkOffering.xml
index 17afd500191..4af7802dcdf 100644
--- a/server/src/test/resources/createNetworkOffering.xml
+++ b/server/src/test/resources/createNetworkOffering.xml
@@ -74,6 +74,7 @@
     <bean id="configurationSubGroupDaoImpl" class="org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDaoImpl" />
     <bean id="nsxControllerDaoImpl" class="com.cloud.network.dao.NsxProviderDaoImpl" />
     <bean id="netrisProviderDaoImpl" class="com.cloud.network.dao.NetrisProviderDaoImpl" />
+    <bean id="netrisServiceMock" class="org.apache.cloudstack.service.NetrisServiceMockTest" />
     <bean id="vlanDetailsDao" class="com.cloud.dc.dao.VlanDetailsDaoImpl" />
     <bean id="publicIpQuarantineDaoImpl" class="com.cloud.network.dao.PublicIpQuarantineDaoImpl" />
     <bean id="reservationDao" class="org.apache.cloudstack.reservation.dao.ReservationDaoImpl" />


(cloudstack) 10/44: Update Subnet purpose for Netris Public Traffic (#17)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit b497c65ff0e7c16cbfd7f2cbd497f2193797d5d1
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Tue Nov 12 12:58:57 2024 -0500

    Update Subnet purpose for Netris Public Traffic (#17)
    
    * Update Subnet purpose for Netris Public Traffic
    
    * search for existing subnet of common purpose type
---
 .../main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index a140f171f64..d7999142945 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -444,10 +444,10 @@ public class NetrisApiClientImpl implements NetrisApiClient {
                 }
                 ipamAllocationId = new BigDecimal(ipamAllocation.getId());
             }
-            IpTreeSubnet exactSubnet = getIpamSubnetByAllocationAndPrefixAndPurposeAndVpc(ipamAllocationId, exactCidr, IpTreeSubnet.PurposeEnum.NAT, systemVpc);
+            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.NAT, systemVpc);
+                createIpamSubnetInternal(ipamSubnetName, exactCidr, SubnetBody.PurposeEnum.COMMON, systemVpc);
             }
         } catch (ApiException e) {
             String msg = String.format("Error setting up the Netris Public Range %s on super CIDR %s", exactCidr, superCidr);


(cloudstack) 24/44: Fix SourceAddress for SNAt to VPC cidr (#35)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 444fb7324d2278ad90bd093d66415b81f268cd10
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Fri Dec 13 10:42:11 2024 -0500

    Fix SourceAddress for SNAt to VPC cidr (#35)
---
 .../src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java | 1 -
 1 file changed, 1 deletion(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 3d1eb893b4e..9b7783c2eaa 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -711,7 +711,6 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         natBody.setSite(site);
 
         if (StringUtils.isNotBlank(snatIP)) {
-            natBody.setSourceAddress(snatIP);
             natBody.setSnatToIP(snatIP);
         }
 


(cloudstack) 14/44: Add support to create Netris VPC / Network offerings (#22)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 7b0d982a448b09920535c80832c571ad489e48fe
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Thu Nov 28 10:33:01 2024 -0500

    Add support to create Netris VPC / Network offerings (#22)
    
    * Add support to create Netris VPC / Network offerings
    
    * fix support services for netris provider type
---
 .../admin/network/CreateNetworkOfferingCmd.java    |  41 ++++--
 .../command/admin/vpc/CreateVPCOfferingCmd.java    |  31 +++--
 .../cloud/configuration/ConfigurationManager.java  |   3 +-
 .../configuration/ConfigurationManagerImpl.java    |  17 +--
 .../java/com/cloud/network/vpc/VpcManagerImpl.java |   3 +-
 .../cloud/vpc/MockConfigurationManagerImpl.java    |   2 +-
 .../networkoffering/CreateNetworkOfferingTest.java |  20 +--
 ui/src/components/CheckBoxSelectPair.vue           |   8 +-
 ui/src/views/offering/AddNetworkOffering.vue       | 140 +++++++++++++++------
 ui/src/views/offering/AddVpcOffering.vue           |  93 ++++++++++++--
 10 files changed, 263 insertions(+), 95 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
index af3db374a7c..362e9c8b3bb 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.api.command.admin.network;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -140,12 +141,19 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
             description = "true if network offering is meant to be used for VPC, false otherwise.")
     private Boolean forVpc;
 
+    @Deprecated
     @Parameter(name = ApiConstants.FOR_NSX,
             type = CommandType.BOOLEAN,
             description = "true if network offering is meant to be used for NSX, false otherwise.",
             since = "4.20.0")
     private Boolean forNsx;
 
+    @Parameter(name = ApiConstants.PROVIDER,
+            type = CommandType.STRING,
+            description = "Name of the provider providing the service",
+            since = "4.20.0")
+    private String provider;
+
     @Parameter(name = ApiConstants.NSX_SUPPORT_LB,
             type = CommandType.BOOLEAN,
             description = "true if network offering for NSX network offering supports Load balancer service.",
@@ -257,8 +265,25 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
         return serviceOfferingId;
     }
 
+    public boolean isExternalNetworkProvider() {
+        return Arrays.asList("NSX", "Netris").stream()
+                .anyMatch(s -> provider != null && s.equalsIgnoreCase(provider));
+    }
+
+    public boolean isForNsx() {
+        return provider != null && provider.equalsIgnoreCase("NSX");
+    }
+
+    public boolean isForNetris() {
+        return provider != null && provider.equalsIgnoreCase("Netris");
+    }
+
+    public String getProvider() {
+        return provider;
+    }
+
     public List<String> getSupportedServices() {
-        if (!isForNsx()) {
+        if (!isExternalNetworkProvider()) {
             return supportedServices == null ? new ArrayList<String>() : supportedServices;
         } else {
             List<String> services = new ArrayList<>(List.of(
@@ -308,10 +333,6 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
         return forVpc;
     }
 
-    public boolean isForNsx() {
-        return BooleanUtils.isTrue(forNsx);
-    }
-
     public String getNetworkMode() {
         return networkMode;
     }
@@ -345,7 +366,7 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
 
     public Map<String, List<String>> getServiceProviders() {
         Map<String, List<String>> serviceProviderMap = new HashMap<>();
-        if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isForNsx()) {
+        if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) {
             Collection servicesCollection = serviceProviderList.values();
             Iterator iter = servicesCollection.iterator();
             while (iter.hasNext()) {
@@ -361,13 +382,13 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
                 providerList.add(provider);
                 serviceProviderMap.put(service, providerList);
             }
-        } else if (Boolean.TRUE.equals(forNsx)) {
-            getServiceProviderMapForNsx(serviceProviderMap);
+        } else if (isExternalNetworkProvider()) {
+            getServiceProviderMapForExternalProvider(serviceProviderMap, Network.Provider.getProvider(provider).getName());
         }
         return serviceProviderMap;
     }
 
-    private void getServiceProviderMapForNsx(Map<String, List<String>> serviceProviderMap) {
+    private void getServiceProviderMapForExternalProvider(Map<String, List<String>> serviceProviderMap, String provider) {
         String routerProvider = Boolean.TRUE.equals(getForVpc()) ? VirtualRouterProvider.Type.VPCVirtualRouter.name() :
                 VirtualRouterProvider.Type.VirtualRouter.name();
         List<String> unsupportedServices = new ArrayList<>(List.of("Vpn", "SecurityGroup", "Connectivity",
@@ -385,7 +406,7 @@ public class CreateNetworkOfferingCmd extends BaseCmd {
             if (routerSupported.contains(service))
                 serviceProviderMap.put(service, List.of(routerProvider));
             else
-                serviceProviderMap.put(service, List.of(Network.Provider.Nsx.getName()));
+                serviceProviderMap.put(service, List.of(provider));
             if (!getNsxSupportsLbService()) {
                 serviceProviderMap.remove(Lb.getName());
             }
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
index 73b4f5df196..020e28e23f4 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.api.command.admin.vpc;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -112,12 +113,19 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
             since = "4.13")
     private List<Long> zoneIds;
 
+    @Deprecated
     @Parameter(name = ApiConstants.FOR_NSX,
             type = CommandType.BOOLEAN,
             description = "true if network offering is meant to be used for NSX, false otherwise.",
             since = "4.20.0")
     private Boolean forNsx;
 
+    @Parameter(name = ApiConstants.PROVIDER,
+            type = CommandType.STRING,
+            description = "Name of the provider providing the service",
+            since = "4.20.0")
+    private String provider;
+
     @Parameter(name = ApiConstants.NSX_SUPPORT_LB,
             type = CommandType.BOOLEAN,
             description = "true if network offering for NSX VPC offering supports Load balancer service.",
@@ -158,11 +166,16 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
         return StringUtils.isEmpty(displayText) ? vpcOfferingName : displayText;
     }
 
+    public boolean isExternalNetworkProvider() {
+        return Arrays.asList("NSX", "Netris").stream()
+                .anyMatch(s -> provider != null && s.equalsIgnoreCase(provider));
+    }
+
     public List<String> getSupportedServices() {
-        if (!isForNsx() && CollectionUtils.isEmpty(supportedServices)) {
+        if (!isExternalNetworkProvider() && CollectionUtils.isEmpty(supportedServices)) {
             throw new InvalidParameterValueException("Supported services needs to be provided");
         }
-        if (isForNsx()) {
+        if (isExternalNetworkProvider()) {
             supportedServices = new ArrayList<>(List.of(
                     Dhcp.getName(),
                     Dns.getName(),
@@ -179,8 +192,8 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
         return supportedServices;
     }
 
-    public boolean isForNsx() {
-        return BooleanUtils.isTrue(forNsx);
+    public String getProvider() {
+        return provider;
     }
 
     public String getNetworkMode() {
@@ -193,7 +206,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
 
     public Map<String, List<String>> getServiceProviders() {
         Map<String, List<String>> serviceProviderMap = new HashMap<>();
-        if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isForNsx()) {
+        if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) {
             Collection<? extends Map<String, String>> servicesCollection = serviceProviderList.values();
             Iterator<? extends Map<String, String>> iter = servicesCollection.iterator();
             while (iter.hasNext()) {
@@ -213,14 +226,14 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
                 providerList.add(provider);
                 serviceProviderMap.put(service, providerList);
             }
-        } else if (Boolean.TRUE.equals(forNsx)) {
-            getServiceProviderMapForNsx(serviceProviderMap);
+        } else if (isExternalNetworkProvider()) {
+            getServiceProviderMapForExternalProvider(serviceProviderMap, Network.Provider.getProvider(provider).getName());
         }
 
         return serviceProviderMap;
     }
 
-    private void getServiceProviderMapForNsx(Map<String, List<String>> serviceProviderMap) {
+    private void getServiceProviderMapForExternalProvider(Map<String, List<String>> serviceProviderMap, String provider) {
         List<String> unsupportedServices = List.of("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity",
                 "Gateway", "Firewall");
         List<String> routerSupported = List.of("Dhcp", "Dns", "UserData");
@@ -231,7 +244,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
             if (routerSupported.contains(service))
                 serviceProviderMap.put(service, List.of(VirtualRouterProvider.Type.VPCVirtualRouter.name()));
             else
-                serviceProviderMap.put(service, List.of(Network.Provider.Nsx.getName()));
+                serviceProviderMap.put(service, List.of(provider));
         }
         if (!getNsxSupportsLbService()) {
             serviceProviderMap.remove(Lb.getName());
diff --git a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
index 330fada48c3..3191d014c66 100644
--- a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
+++ b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
@@ -214,6 +214,7 @@ public interface ConfigurationManager {
      * @param forVpc
      * @param forTungsten
      * @param forNsx
+     * @param forNetris
      * @param domainIds
      * @param zoneIds
      * @return network offering object
@@ -223,7 +224,7 @@ public interface ConfigurationManager {
                                             Integer networkRate, Map<Service, Set<Provider>> serviceProviderMap, boolean isDefault, Network.GuestType type, boolean systemOnly, Long serviceOfferingId,
                                             boolean conserveMode, Map<Service, Map<Capability, String>> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent,
                                             Map<NetworkOffering.Detail, String> details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive, Boolean forVpc,
-                                            Boolean forTungsten, boolean forNsx, NetworkOffering.NetworkMode networkMode, List<Long> domainIds, List<Long> zoneIds, boolean enableOffering, final NetUtils.InternetProtocol internetProtocol,
+                                            Boolean forTungsten, boolean forNsx, boolean forNetris, NetworkOffering.NetworkMode networkMode, List<Long> domainIds, List<Long> zoneIds, boolean enableOffering, final NetUtils.InternetProtocol internetProtocol,
                                             NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber);
 
     Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, boolean forSystemVms, Long podId, String startIP, String endIP,
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 5881d8bec53..0f8f00faaa1 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -6147,7 +6147,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         final Map<String, String> detailsStr = cmd.getDetails();
         final Boolean egressDefaultPolicy = cmd.getEgressDefaultPolicy();
         Boolean forVpc = cmd.getForVpc();
-        Boolean forNsx = cmd.isForNsx();
+        boolean forNsx = cmd.isForNsx();
+        boolean forNetris = cmd.isForNetris();
         Boolean forTungsten = cmd.getForTungsten();
         String networkModeStr = cmd.getNetworkMode();
         boolean nsxSupportInternalLbSvc = cmd.getNsxSupportsInternalLbService();
@@ -6186,8 +6187,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             }
         }
 
-        if (Boolean.TRUE.equals(forNsx) && Boolean.TRUE.equals(forTungsten)) {
-            throw new InvalidParameterValueException("Network Offering cannot be for both Tungsten-Fabric and NSX");
+        if ((Boolean.TRUE.equals(forTungsten) ? 1 : 0) + (forNetris ? 1 : 0) + (forNsx ? 1 : 0) > 1) {
+            throw new InvalidParameterValueException("Network Offering cannot be for multiple providers - Tungsten-Fabric, NSX and Netris");
         }
 
         NetworkOffering.NetworkMode networkMode = null;
@@ -6400,7 +6401,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         // offering
         final Map<Capability, String> sourceNatServiceCapabilityMap = cmd.getServiceCapabilities(Service.SourceNat);
         if (!serviceProviderMap.containsKey(Service.SourceNat) && sourceNatServiceCapabilityMap != null && !sourceNatServiceCapabilityMap.isEmpty()) {
-            throw new InvalidParameterValueException("Capabilities for source NAT service can be specifed only when source NAT service is enabled for network offering.");
+            throw new InvalidParameterValueException("Capabilities for source NAT service can be specified only when source NAT service is enabled for network offering.");
         }
         validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
 
@@ -6408,7 +6409,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         // offering
         final Map<Capability, String> staticNatServiceCapabilityMap = cmd.getServiceCapabilities(Service.StaticNat);
         if (!serviceProviderMap.containsKey(Service.StaticNat) && sourceNatServiceCapabilityMap != null && !staticNatServiceCapabilityMap.isEmpty()) {
-            throw new InvalidParameterValueException("Capabilities for static NAT service can be specifed only when static NAT service is enabled for network offering.");
+            throw new InvalidParameterValueException("Capabilities for static NAT service can be specified only when static NAT service is enabled for network offering.");
         }
         validateStaticNatServiceCapablities(staticNatServiceCapabilityMap);
 
@@ -6469,7 +6470,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         }
 
         final NetworkOfferingVO offering = createNetworkOffering(name, displayText, trafficType, tags, specifyVlan, availability, networkRate, serviceProviderMap, false, guestType, false,
-                serviceOfferingId, conserveMode, serviceCapabilityMap, specifyIpRanges, isPersistent, details, egressDefaultPolicy, maxconn, enableKeepAlive, forVpc, forTungsten, forNsx, networkMode, domainIds, zoneIds, enable, internetProtocol, routingMode, specifyAsNumber);
+                serviceOfferingId, conserveMode, serviceCapabilityMap, specifyIpRanges, isPersistent, details, egressDefaultPolicy, maxconn, enableKeepAlive, forVpc, forTungsten, forNsx, forNetris, networkMode, domainIds, zoneIds, enable, internetProtocol, routingMode, specifyAsNumber);
         if (Boolean.TRUE.equals(forNsx) && nsxSupportInternalLbSvc) {
             offering.setInternalLb(true);
             offering.setPublicLb(false);
@@ -6636,7 +6637,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                                                    final Long serviceOfferingId,
                                                    final boolean conserveMode, final Map<Service, Map<Capability, String>> serviceCapabilityMap, final boolean specifyIpRanges, final boolean isPersistent,
                                                    final Map<Detail, String> details, final boolean egressDefaultPolicy, final Integer maxconn, final boolean enableKeepAlive, Boolean forVpc,
-                                                   Boolean forTungsten, boolean forNsx, NetworkOffering.NetworkMode networkMode, final List<Long> domainIds, final List<Long> zoneIds, final boolean enableOffering, final NetUtils.InternetProtocol internetProtocol,
+                                                   Boolean forTungsten, boolean forNsx, boolean forNetris, NetworkOffering.NetworkMode networkMode, final List<Long> domainIds, final List<Long> zoneIds, final boolean enableOffering, final NetUtils.InternetProtocol internetProtocol,
                                                    final NetworkOffering.RoutingMode routingMode, final boolean specifyAsNumber) {
 
         String servicePackageUuid;
@@ -6923,7 +6924,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                                 logger.trace("Added service for the network offering: " + offService + " with provider " + provider.getName());
                             }
 
-                            if (vpcOff && !forNsx) {
+                            if (vpcOff && !forNsx && !forNetris) {
                                 final List<Service> supportedSvcs = new ArrayList<Service>();
                                 supportedSvcs.addAll(serviceProviderMap.keySet());
                                 _vpcMgr.validateNtwkOffForVpc(offering, supportedSvcs);
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index 0c74d464fe3..121e0cbe223 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -511,7 +511,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         final Long serviceOfferingId = cmd.getServiceOfferingId();
         final List<Long> domainIds = cmd.getDomainIds();
         final List<Long> zoneIds = cmd.getZoneIds();
-        final Boolean forNsx = cmd.isForNsx();
+        final String provider = cmd.getProvider();
+        final Boolean forNsx = Objects.nonNull(provider) && provider.equalsIgnoreCase("NSX");
         final String networkModeStr = cmd.getNetworkMode();
         final boolean enable = cmd.getEnable();
 
diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java
index 9b0822cf0b1..c8c0395fd24 100644
--- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java
+++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java
@@ -547,7 +547,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
                                                    Integer networkRate, Map<Service, Set<Provider>> serviceProviderMap, boolean isDefault, GuestType type, boolean systemOnly, Long serviceOfferingId,
                                                    boolean conserveMode, Map<Service, Map<Capability, String>> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent,
                                                    Map<NetworkOffering.Detail, String> details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive, Boolean forVpc,
-                                                   Boolean forTungsten, boolean forNsx, NetworkOffering.NetworkMode networkMode, List<Long> domainIds, List<Long> zoneIds, boolean enableOffering, NetUtils.InternetProtocol internetProtocol,
+                                                   Boolean forTungsten, boolean forNsx, boolean forNetris, NetworkOffering.NetworkMode networkMode, List<Long> domainIds, List<Long> zoneIds, boolean enableOffering, NetUtils.InternetProtocol internetProtocol,
                                                    NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber) {
         // TODO Auto-generated method stub
         return null;
diff --git a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java
index 25b4bdda45f..9f6597182b7 100644
--- a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java
+++ b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java
@@ -138,7 +138,7 @@ public class CreateNetworkOfferingTest extends TestCase {
     public void createSharedNtwkOffWithVlan() {
         NetworkOfferingVO off =
             configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared, false,
-                null, false, null, true, false, null, false, null, true, false, false, false,  null,null, null, false, null, null, false);
+                null, false, null, true, false, null, false, null, true, false, false, false,  false,null,null, null, false, null, null, false);
         assertNotNull("Shared network offering with specifyVlan=true failed to create ", off);
     }
 
@@ -146,7 +146,7 @@ public class CreateNetworkOfferingTest extends TestCase {
     public void createSharedNtwkOffWithNoVlan() {
         NetworkOfferingVO off =
                 configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, false, Availability.Optional, 200, null, false, Network.GuestType.Shared,
-                    false, null, false, null, true, false, null, false, null, true, false, false, false, null, null,null, false, null, null, false);
+                    false, null, false, null, true, false, null, false, null, true, false, false, false, false,null, null,null, false, null, null, false);
         assertNotNull("Shared network offering with specifyVlan=false was created", off);
     }
 
@@ -154,7 +154,7 @@ public class CreateNetworkOfferingTest extends TestCase {
     public void createSharedNtwkOffWithSpecifyIpRanges() {
         NetworkOfferingVO off =
             configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared, false,
-                null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false);
+                null, false, null, true, false, null, false, null, true, false, false, false, false,null,null, null, false, null, null, false);
 
         assertNotNull("Shared network offering with specifyIpRanges=true failed to create ", off);
     }
@@ -163,7 +163,7 @@ public class CreateNetworkOfferingTest extends TestCase {
     public void createSharedNtwkOffWithoutSpecifyIpRanges() {
         NetworkOfferingVO off =
                 configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared,
-                        false, null, false, null, false, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false);
+                        false, null, false, null, false, false, null, false, null, true, false, false, false,false, null,null, null, false, null, null, false);
         assertNull("Shared network offering with specifyIpRanges=false was created", off);
     }
 
@@ -176,7 +176,7 @@ public class CreateNetworkOfferingTest extends TestCase {
         serviceProviderMap.put(Network.Service.SourceNat, vrProvider);
         NetworkOfferingVO off =
             configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false,
-                Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, null, null, null, false, null, null, false);
+                Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, false,null, null, null, false, null, null, false);
 
         assertNotNull("Isolated network offering with specifyIpRanges=false failed to create ", off);
     }
@@ -189,7 +189,7 @@ public class CreateNetworkOfferingTest extends TestCase {
         serviceProviderMap.put(Network.Service.SourceNat, vrProvider);
         NetworkOfferingVO off =
             configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false,
-                Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false);
+                Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, false,null,null, null, false, null, null, false);
         assertNotNull("Isolated network offering with specifyVlan=true wasn't created", off);
 
     }
@@ -202,7 +202,7 @@ public class CreateNetworkOfferingTest extends TestCase {
         serviceProviderMap.put(Network.Service.SourceNat, vrProvider);
         NetworkOfferingVO off =
                 configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false,
-                        Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false);
+                        Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, false,null,null, null, false, null, null, false);
         assertNull("Isolated network offering with specifyIpRanges=true and source nat service enabled, was created", off);
     }
 
@@ -213,7 +213,7 @@ public class CreateNetworkOfferingTest extends TestCase {
         Set<Network.Provider> vrProvider = new HashSet<Network.Provider>();
         NetworkOfferingVO off =
             configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false,
-                Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null, null, false);
+                Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, false,null,null, null, false, null, null, false);
         assertNotNull("Isolated network offering with specifyIpRanges=true and with no sourceNatService, failed to create", off);
 
     }
@@ -231,7 +231,7 @@ public class CreateNetworkOfferingTest extends TestCase {
         serviceProviderMap.put(Network.Service.Lb, vrProvider);
         NetworkOfferingVO off =
             configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false,
-                Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, false, null, null, false);
+                Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, false,null, null, null, false, null, null, false);
         // System.out.println("Creating Vpc Network Offering");
         assertNotNull("Vpc Isolated network offering with Vpc provider ", off);
     }
@@ -251,7 +251,7 @@ public class CreateNetworkOfferingTest extends TestCase {
         serviceProviderMap.put(Network.Service.Lb, lbProvider);
         NetworkOfferingVO off =
             configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false,
-                Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, false, null, null, false);
+                Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, false,null, null, null, false, null, null, false);
         // System.out.println("Creating Vpc Network Offering");
         assertNotNull("Vpc Isolated network offering with Vpc and Netscaler provider ", off);
     }
diff --git a/ui/src/components/CheckBoxSelectPair.vue b/ui/src/components/CheckBoxSelectPair.vue
index 4fba1da2556..480e515be29 100644
--- a/ui/src/components/CheckBoxSelectPair.vue
+++ b/ui/src/components/CheckBoxSelectPair.vue
@@ -21,7 +21,7 @@
       <a-col :md="24" :lg="layout === 'horizontal' ? 10 : 24">
         <a-checkbox
           :checked="checked"
-          :disabled="forNsx"
+          :disabled="forExternalNetProvider"
           @change="handleCheckChange">
           {{ checkBoxLabel }}
         </a-checkbox>
@@ -32,7 +32,7 @@
           :label="selectLabel">
           <a-select
             v-model:value="selected"
-            :disabled="forNsx"
+            :disabled="forExternalNetProvider"
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
@@ -86,7 +86,7 @@ export default {
       type: Boolean,
       default: false
     },
-    forNsx: {
+    forExternalNetProvider: {
       type: Boolean,
       default: false
     }
@@ -123,7 +123,7 @@ export default {
       return this.option || this.selectedOption
     },
     option () {
-      if (this.forNsx) {
+      if (this.forExternalNetProvider) {
         return this.selectOptions[0]?.name || null
       }
       return null
diff --git a/ui/src/views/offering/AddNetworkOffering.vue b/ui/src/views/offering/AddNetworkOffering.vue
index 66d7a4a675e..7bd8fdb95a0 100644
--- a/ui/src/views/offering/AddNetworkOffering.vue
+++ b/ui/src/views/offering/AddNetworkOffering.vue
@@ -41,7 +41,7 @@
             v-model:value="form.displaytext"
             :placeholder="apiParams.displaytext.description"/>
         </a-form-item>
-        <a-form-item name="networkrate" ref="networkrate" v-if="!forNsx">
+        <a-form-item name="networkrate" ref="networkrate" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
           <template #label>
             <tooltip-label :title="$t('label.networkrate')" :tooltip="apiParams.networkrate.description"/>
           </template>
@@ -60,10 +60,10 @@
             <a-radio-button value="isolated">
               {{ $t('label.isolated') }}
             </a-radio-button>
-            <a-radio-button value="l2" v-if="!forNsx">
+            <a-radio-button value="l2" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
               {{ $t('label.l2') }}
             </a-radio-button>
-            <a-radio-button value="shared" v-if="!forNsx">
+            <a-radio-button value="shared" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
               {{ $t('label.shared') }}
             </a-radio-button>
           </a-radio-group>
@@ -93,7 +93,7 @@
             </a-radio-button>
           </a-radio-group>
         </a-form-item>
-        <a-row :gutter="12" v-if="!forNsx">
+        <a-row :gutter="12" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
           <a-col :md="12" :lg="12">
             <a-form-item name="specifyvlan" ref="specifyvlan">
               <template #label>
@@ -117,19 +117,31 @@
               <template #label>
                 <tooltip-label :title="$t('label.vpc')" :tooltip="apiParams.forvpc.description"/>
               </template>
-              <a-switch v-model:checked="form.forvpc" @change="val => { handleForVpcChange(val) }" />
+              <a-switch v-model:checked="form.forvpc" @change="handleForVpcChange" />
             </a-form-item>
           </a-col>
           <a-col :md="12" :lg="12">
-            <a-form-item name="fornsx" ref="fornsx" v-if="guestType === 'isolated'">
+            <a-form-item name="provider" ref="provider">
               <template #label>
-                <tooltip-label :title="$t('label.nsx')" :tooltip="apiParams.fornsx.description"/>
+                <tooltip-label :title="$t('label.provider')" :tooltip="apiParams.provider.description"/>
               </template>
-              <a-switch v-model:checked="form.fornsx" @change="val => { handleForNsxChange(val) }" />
+              <a-select
+                v-model:value="form.provider"
+                v-focus="true"
+                @change="handleProviderChange"
+                showSearch
+                optionFilterProp="label"
+                :filterOption="(input, option) => {
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }"
+                :placeholder="apiParams.provider.description" >
+                <a-select-option :value="'NSX'" :label="$t('label.nsx')"> {{ $t('label.nsx') }} </a-select-option>
+                <a-select-option :value="'Netris'" :label="$t('label.netris')"> {{ $t('label.netris') }} </a-select-option>
+              </a-select>
             </a-form-item>
           </a-col>
         </a-row>
-        <a-row :gutter="12" v-if="forNsx">
+        <a-row :gutter="12" v-if="form.provider === 'NSX'">
           <a-col :md="12" :lg="12">
             <a-form-item name="nsxsupportlb" ref="nsxsupportlb" v-if="guestType === 'isolated'">
               <template #label>
@@ -180,7 +192,7 @@
             </a-radio-button>
           </a-radio-group>
         </a-form-item>
-        <a-row :gutter="12" v-if="routingMode === 'dynamic' && !forVpc && forNsx">
+        <a-row :gutter="12" v-if="routingMode === 'dynamic' && !forVpc && form.provider === 'NSX'">
           <a-col :md="12" :lg="12">
             <a-form-item name="specifyasnumber" ref="specifyasnumber">
               <template #label>
@@ -195,7 +207,7 @@
         </a-form-item>
         <a-row :gutter="12">
           <a-col :md="12" :lg="12">
-            <a-form-item name="promiscuousmode" ref="promiscuousmode" v-if="!forNsx">
+            <a-form-item name="promiscuousmode" ref="promiscuousmode" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
               <template #label>
                 <tooltip-label :title="$t('label.promiscuousmode')" :tooltip="$t('message.network.offering.promiscuous.mode')"/>
               </template>
@@ -213,7 +225,7 @@
                 </a-radio-button>
               </a-radio-group>
             </a-form-item>
-            <a-form-item name="macaddresschanges" ref="macaddresschanges" v-if="!forNsx">
+            <a-form-item name="macaddresschanges" ref="macaddresschanges" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
               <template #label>
                 <tooltip-label :title="$t('label.macaddresschanges')" :tooltip="$t('message.network.offering.mac.address.changes')"/>
               </template>
@@ -233,7 +245,7 @@
             </a-form-item>
           </a-col>
           <a-col :md="12" :lg="12">
-            <a-form-item name="forgedtransmits" ref="forgedtransmits" v-if="!forNsx">
+            <a-form-item name="forgedtransmits" ref="forgedtransmits" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
               <template #label>
                 <tooltip-label :title="$t('label.forgedtransmits')" :tooltip="$t('message.network.offering.forged.transmits')"/>
               </template>
@@ -251,7 +263,7 @@
                 </a-radio-button>
               </a-radio-group>
             </a-form-item>
-            <a-form-item name="maclearning" ref="maclearning" v-if="!forNsx">
+            <a-form-item name="maclearning" ref="maclearning" v-if="form.provider !== 'NSX' && form.provider !== 'Netris'">
               <template #label>
                 <tooltip-label :title="$t('label.maclearning')" :tooltip="$t('message.network.offering.mac.learning')"/>
               </template>
@@ -290,8 +302,8 @@
                   <CheckBoxSelectPair
                     :resourceKey="item.name"
                     :checkBoxLabel="item.description"
-                    :forNsx="forNsx"
-                    :defaultCheckBoxValue="forNsx"
+                    :forExternalNetProvider="form.provider === 'NSX' || form.provider === 'Netris'"
+                    :defaultCheckBoxValue="form.provider === 'NSX' || form.provider === 'Netris'"
                     :selectOptions="!supportedServiceLoading ? item.provider: []"
                     @handle-checkselectpair-change="handleSupportedServiceChange"/>
                 </a-list-item>
@@ -452,7 +464,8 @@
         <a-form-item
           name="conservemode"
           ref="conservemode"
-          v-if="(guestType === 'shared' || guestType === 'isolated') && !isVpcVirtualRouterForAtLeastOneService && !forNsx && networkmode !== 'ROUTED'">
+          v-if="(guestType === 'shared' || guestType === 'isolated') && !isVpcVirtualRouterForAtLeastOneService &&
+          (form.provider !== 'NSX' && form.provider !== 'Netris') && networkmode !== 'ROUTED'">
           <template #label>
             <tooltip-label :title="$t('label.conservemode')" :tooltip="apiParams.conservemode.description"/>
           </template>
@@ -587,7 +600,7 @@ export default {
       selectedDomains: [],
       selectedZones: [],
       forVpc: false,
-      forNsx: false,
+      provider: '',
       lbType: 'publicLb',
       macLearningValue: '',
       supportedServices: [],
@@ -643,7 +656,13 @@ export default {
         description: 'Nsx',
         enabled: true
       },
-      nsxSupportedServicesMap: {}
+      Netris: {
+        name: 'Netris',
+        description: 'Netris',
+        enabled: true
+      },
+      nsxSupportedServicesMap: {},
+      netrisSupportedServicesMap: {}
     }
   },
   beforeCreate () {
@@ -870,7 +889,7 @@ export default {
       this.supportedServiceLoading = true
       var supportedServices = this.supportedServices
       var self = this
-      if (!this.forNsx) {
+      if (this.provider !== 'NSX' && this.provider !== 'Netris') {
         if (this.networkmode === 'ROUTED' && this.guestType === 'isolated') {
           supportedServices = supportedServices.filter(service => {
             return !['SourceNat', 'StaticNat', 'Lb', 'PortForwarding', 'Vpn'].includes(service.name)
@@ -887,7 +906,7 @@ export default {
                 }
                 provider.enabled = enabledProviders.includes(provider.name)
               } else { // *** non-vpc ***
-                provider.enabled = !['InternalLbVm', 'VpcVirtualRouter', 'Nsx'].includes(provider.name)
+                provider.enabled = !['InternalLbVm', 'VpcVirtualRouter', 'Nsx', 'Netris'].includes(provider.name)
               }
               providers[providerIndex] = provider
             })
@@ -903,11 +922,25 @@ export default {
       } else {
         supportedServices = this.supportedSvcs
         supportedServices = supportedServices.filter(svc => {
-          return Object.keys(this.nsxSupportedServicesMap).includes(svc.name)
+          if (this.provider === 'NSX') {
+            return Object.keys(this.nsxSupportedServicesMap).includes(svc.name)
+          } else if (this.provider === 'Netris') {
+            return Object.keys(this.netrisSupportedServicesMap).includes(svc.name)
+          }
         })
         supportedServices = supportedServices.map(svc => {
           if (!['Dhcp', 'Dns', 'UserData'].includes(svc.name)) {
-            svc.provider = [this.NSX]
+            if (this.provider === 'NSX') {
+              svc.provider = [this.NSX]
+            } else if (this.provider === 'Netris') {
+              svc.provider = [this.Netris]
+            }
+          } else {
+            if (this.forVpc) {
+              svc.provider = [this.VPCVR]
+            } else {
+              svc.provider = [this.VR]
+            }
           }
           return svc
         })
@@ -918,7 +951,7 @@ export default {
     },
     handleForVpcChange (forVpc) {
       this.forVpc = forVpc
-      if (this.forNsx) {
+      if (this.provider === 'NSX') {
         this.nsxSupportedServicesMap = {
           Dhcp: this.forVpc ? this.VPCVR : this.VR,
           Dns: this.forVpc ? this.VPCVR : this.VR,
@@ -930,21 +963,47 @@ export default {
           ...(forVpc && { NetworkACL: this.NSX }),
           ...(!forVpc && { Firewall: this.NSX })
         }
+      } else if (this.provider === 'Netris') {
+        this.netrisSupportedServicesMap = {
+          Dhcp: this.forVpc ? this.VPCVR : this.VR,
+          Dns: this.forVpc ? this.VPCVR : this.VR,
+          UserData: this.forVpc ? this.VPCVR : this.VR,
+          SourceNat: this.Netris,
+          StaticNat: this.Netris,
+          PortForwarding: this.Netris,
+          Lb: this.Netris,
+          ...(forVpc && { NetworkACL: this.Netris }),
+          ...(!forVpc && { Firewall: this.Netris })
+        }
       }
       this.updateSupportedServices()
     },
-    handleForNsxChange (forNsx) {
-      this.forNsx = forNsx
-      this.nsxSupportedServicesMap = {
-        Dhcp: this.forVpc ? this.VPCVR : this.VR,
-        Dns: this.forVpc ? this.VPCVR : this.VR,
-        UserData: this.forVpc ? this.VPCVR : this.VR,
-        SourceNat: this.NSX,
-        StaticNat: this.NSX,
-        PortForwarding: this.NSX,
-        Lb: this.NSX,
-        ...(this.forVpc && { NetworkACL: this.NSX }),
-        ...(!this.forVpc && { Firewall: this.NSX })
+    handleProviderChange (provider) {
+      this.provider = provider
+      if (this.provider === 'NSX') {
+        this.nsxSupportedServicesMap = {
+          Dhcp: this.forVpc ? this.VPCVR : this.VR,
+          Dns: this.forVpc ? this.VPCVR : this.VR,
+          UserData: this.forVpc ? this.VPCVR : this.VR,
+          SourceNat: this.NSX,
+          StaticNat: this.NSX,
+          PortForwarding: this.NSX,
+          Lb: this.NSX,
+          ...(this.forVpc && { NetworkACL: this.NSX }),
+          ...(!this.forVpc && { Firewall: this.NSX })
+        }
+      } else if (this.provider === 'Netris') {
+        this.netrisSupportedServicesMap = {
+          Dhcp: this.forVpc ? this.VPCVR : this.VR,
+          Dns: this.forVpc ? this.VPCVR : this.VR,
+          UserData: this.forVpc ? this.VPCVR : this.VR,
+          SourceNat: this.Netris,
+          StaticNat: this.Netris,
+          PortForwarding: this.Netris,
+          Lb: this.Netris,
+          ...(this.forVpc && { NetworkACL: this.Netris }),
+          ...(!this.forVpc && { Firewall: this.Netris })
+        }
       }
       this.fetchSupportedServiceData()
     },
@@ -1071,11 +1130,16 @@ export default {
           params.specifyasnumber = values.specifyasnumber
         }
         params.routingmode = values.routingmode
-        if (values.fornsx === true) {
-          params.fornsx = true
+        const forNsx = values.provider === 'NSX'
+        if (forNsx === true) {
+          params.provider = 'NSX'
           params.nsxsupportlb = values.nsxsupportlb
           params.nsxsupportsinternallb = values.nsxsupportsinternallb
         }
+        const forNetris = values.provider === 'Netris'
+        if (forNetris) {
+          params.provider = 'Netris'
+        }
         if (values.guestiptype === 'isolated') {
           params.networkmode = values.networkmode
         }
diff --git a/ui/src/views/offering/AddVpcOffering.vue b/ui/src/views/offering/AddVpcOffering.vue
index c914467a088..1480705aa29 100644
--- a/ui/src/views/offering/AddVpcOffering.vue
+++ b/ui/src/views/offering/AddVpcOffering.vue
@@ -69,14 +69,26 @@
         </a-form-item>
         <a-row :gutter="12">
           <a-col :md="12" :lg="12">
-            <a-form-item name="fornsx" ref="fornsx">
+            <a-form-item name="provider" ref="provider">
               <template #label>
-                <tooltip-label :title="$t('label.nsx')" :tooltip="apiParams.fornsx.description"/>
+                <tooltip-label :title="$t('label.provider')" :tooltip="apiParams.provider.description"/>
               </template>
-              <a-switch v-model:checked="form.fornsx" @change="val => { handleForNsxChange(val) }" />
+              <a-select
+                v-model:value="form.provider"
+                v-focus="true"
+                @change="val => handleProviderChange(val)"
+                showSearch
+                optionFilterProp="label"
+                :filterOption="(input, option) => {
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }"
+                :placeholder="apiParams.provider.description" >
+                <a-select-option :value="'NSX'" :label="$t('label.nsx')"> {{ $t('label.nsx') }} </a-select-option>
+                <a-select-option :value="'Netris'" :label="$t('label.netris')"> {{ $t('label.netris') }} </a-select-option>
+              </a-select>
             </a-form-item>
           </a-col>
-          <a-col :md="12" :lg="12" v-if="forNsx">
+          <a-col :md="12" :lg="12" v-if="form.provider === 'NSX'">
             <a-form-item name="nsxsupportlb" ref="nsxsupportlb">
               <template #label>
                 <tooltip-label :title="$t('label.nsx.supports.lb')" :tooltip="apiParams.nsxsupportlb.description"/>
@@ -85,7 +97,7 @@
             </a-form-item>
           </a-col>
         </a-row>
-        <a-row :gutter="12" v-if="routingMode === 'dynamic' && forNsx">
+        <a-row :gutter="12" v-if="routingMode === 'dynamic' && form.provider === 'NSX'">
           <a-col :md="12" :lg="12">
             <a-form-item name="specifyasnumber" ref="specifyasnumber">
               <template #label>
@@ -139,8 +151,8 @@
                   <CheckBoxSelectPair
                     :resourceKey="item.name"
                     :checkBoxLabel="item.description"
-                    :forNsx="forNsx"
-                    :defaultCheckBoxValue="forNsx"
+                    :forExternalNetProvider="form.provider === 'NSX' || form.provider === 'Netris'"
+                    :defaultCheckBoxValue="form.provider === 'NSX' || form.provider === 'Netris'"
                     :selectOptions="item.provider"
                     @handle-checkselectpair-change="handleSupportedServiceChange"/>
                 </a-list-item>
@@ -273,6 +285,7 @@ export default {
       zones: [],
       zoneLoading: false,
       forNsx: false,
+      provider: '',
       loading: false,
       supportedServices: [],
       supportedServiceLoading: false,
@@ -305,6 +318,11 @@ export default {
         description: 'Nsx',
         enabled: true
       },
+      Netris: {
+        name: 'Netris',
+        description: 'Netris',
+        enabled: true
+      },
       nsxSupportedServicesMap: {}
     }
   },
@@ -389,7 +407,7 @@ export default {
     },
     fetchSupportedServiceData () {
       var services = []
-      if (this.forNsx) {
+      if (this.provider === 'NSX') {
         services.push({
           name: 'Dhcp',
           enabled: true,
@@ -432,6 +450,49 @@ export default {
           enabled: true,
           provider: [{ name: 'VpcVirtualRouter' }]
         })
+      } else if (this.provider === 'Netris') {
+        services.push({
+          name: 'Dhcp',
+          enabled: true,
+          provider: [
+            { name: 'VpcVirtualRouter' }
+          ]
+        })
+        services.push({
+          name: 'Dns',
+          enabled: true,
+          provider: [{ name: 'VpcVirtualRouter' }]
+        })
+        services.push({
+          name: 'Lb',
+          enabled: true,
+          provider: [{ name: 'Netris' }]
+        })
+        services.push({
+          name: 'StaticNat',
+          enabled: true,
+          provider: [{ name: 'Netris' }]
+        })
+        services.push({
+          name: 'SourceNat',
+          enabled: true,
+          provider: [{ name: 'Netris' }]
+        })
+        services.push({
+          name: 'NetworkACL',
+          enabled: true,
+          provider: [{ name: 'Netris' }]
+        })
+        services.push({
+          name: 'PortForwarding',
+          enabled: true,
+          provider: [{ name: 'Netris' }]
+        })
+        services.push({
+          name: 'UserData',
+          enabled: true,
+          provider: [{ name: 'VpcVirtualRouter' }]
+        })
       } else {
         services.push({
           name: 'Dhcp',
@@ -521,15 +582,16 @@ export default {
         self.supportedServiceLoading = false
       }, 50)
     },
-    async handleForNsxChange (forNsx) {
-      this.forNsx = forNsx
-      if (forNsx) {
+    async handleProviderChange (value) {
+      this.provider = value
+      if (this.provider === 'NSX') {
         this.form.nsxsupportlb = true
         this.handleNsxLbService(true)
       }
       this.fetchSupportedServiceData()
     },
     handleNsxLbService (supportLb) {
+      console.log(supportLb)
       if (!supportLb) {
         this.supportedServices = this.supportedServices.filter(svc => svc.name !== 'Lb')
       }
@@ -619,10 +681,15 @@ export default {
         if (values.internetprotocol) {
           params.internetprotocol = values.internetprotocol
         }
-        if (values.fornsx === true) {
-          params.fornsx = true
+        const forNsx = values.provider === 'NSX'
+        if (forNsx) {
+          params.provider = 'NSX'
           params.nsxsupportlb = values.nsxsupportlb
         }
+        const forNetris = values.provider === 'Netris'
+        if (forNetris) {
+          params.provider = 'Netris'
+        }
         params.networkmode = values.networkmode
         if (!values.forVpc) {
           params.specifyasnumber = values.specifyasnumber


(cloudstack) 23/44: Add Netris Tag parameter to the Network provider and fix zone creation wizard (#33)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 554ed63440dee1fc7bc54d08fb761aea760e2db9
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Tue Dec 10 13:45:50 2024 -0500

    Add Netris Tag parameter to the Network provider and fix zone creation wizard (#33)
    
    * Add Netris Tag parameter to the Network provider
    
    * remove unused import
    
    * Fix public IP ranges creation on zone creation (#34)
    
    * use single quotes
    
    ---------
    
    Co-authored-by: Nicolas Vazquez <ni...@gmail.com>
---
 .../java/com/cloud/network/netris/NetrisProvider.java |  1 +
 .../java/org/apache/cloudstack/api/ApiConstants.java  |  1 +
 .../com/cloud/network/element/NetrisProviderVO.java   | 19 +++++++++++++++++++
 .../resources/META-INF/db/schema-41910to42000.sql     |  1 +
 .../cloudstack/agent/api/CreateNetrisVnetCommand.java |  9 +++++++++
 .../cloudstack/api/command/AddNetrisProviderCmd.java  |  7 +++++++
 .../api/response/NetrisProviderResponse.java          | 12 ++++++++++++
 .../cloudstack/service/NetrisApiClientImpl.java       |  9 ++++++---
 .../cloudstack/service/NetrisProviderServiceImpl.java |  4 ++++
 .../apache/cloudstack/service/NetrisServiceImpl.java  |  2 ++
 .../cloud/configuration/ConfigurationManagerImpl.java | 12 +++++++-----
 ui/public/locales/en.json                             |  3 +++
 ui/src/views/infra/network/ServiceProvidersTab.vue    |  2 +-
 ui/src/views/infra/zone/ZoneWizardLaunchZone.vue      |  1 +
 .../views/infra/zone/ZoneWizardNetworkSetupStep.vue   |  6 ++++++
 15 files changed, 80 insertions(+), 9 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
index 82dc5a271a7..1a76f948a29 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
@@ -27,4 +27,5 @@ public interface NetrisProvider extends InternalIdentity, Identity {
     String getUsername();
     String getSiteName();
     String getTenantName();
+    String getNetrisTag();
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index c0b478977c8..17fe3d624d8 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -1149,6 +1149,7 @@ public class ApiConstants {
     public static final String HAS_RULES = "hasrules";
     public static final String NSX_DETAIL_KEY = "forNsx";
     public static final String NETRIS_DETAIL_KEY = "forNetris";
+    public static final String NETRIS_TAG = "netristag";
     public static final String DISK_PATH = "diskpath";
     public static final String IMPORT_SOURCE = "importsource";
     public static final String TEMP_PATH = "temppath";
diff --git a/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java
index 2df1faae077..12d7f22e340 100644
--- a/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java
@@ -66,6 +66,9 @@ public class NetrisProviderVO implements NetrisProvider {
     @Column(name = "tenant_name")
     private String tenantName;
 
+    @Column(name = "netris_tag")
+    private String netrisTag;
+
     @Column(name = "created")
     private Date created;
 
@@ -171,6 +174,14 @@ public class NetrisProviderVO implements NetrisProvider {
         this.tenantName = tenantName;
     }
 
+    public String getNetrisTag() {
+        return netrisTag;
+    }
+
+    public void setNetrisTag(String netrisTag) {
+        this.netrisTag = netrisTag;
+    }
+
     public Date getCreated() {
         return created;
     }
@@ -197,6 +208,7 @@ public class NetrisProviderVO implements NetrisProvider {
         private String password;
         private String siteName;
         private String tenantName;
+        private String netrisTag;
 
         public Builder() {
             // Default constructor
@@ -246,6 +258,12 @@ public class NetrisProviderVO implements NetrisProvider {
             this.tenantName = tenantName;
             return this;
         }
+
+        public Builder setNetrisTag(String netrisTag) {
+            this.netrisTag = netrisTag;
+            return this;
+        }
+
         public NetrisProviderVO build() {
             NetrisProviderVO provider = new NetrisProviderVO();
             provider.setZoneId(this.zoneId);
@@ -258,6 +276,7 @@ public class NetrisProviderVO implements NetrisProvider {
             provider.setPassword(this.password);
             provider.setSiteName(this.siteName);
             provider.setTenantName(this.tenantName);
+            provider.setNetrisTag(this.netrisTag);
             provider.setCreated(new Date());
             return provider;
         }
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
index 9873c355baa..3f39a7da089 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
@@ -439,6 +439,7 @@ CREATE TABLE `cloud`.`netris_providers` (
     `password` varchar(255) NOT NULL,
     `site_name` varchar(255) NOT NULL,
     `tenant_name` varchar(255) NOT NULL,
+    `netris_tag` varchar(255) NOT NULL,
     `created` datetime NOT NULL COMMENT 'created date',
     `removed` datetime COMMENT 'removed date if not null',
     PRIMARY KEY (`id`),
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
index 12ef9482ea8..7ba6c9250fd 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
@@ -22,6 +22,7 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
     private String cidr;
     private Integer vxlanId;
     private String gateway;
+    private String netrisTag;
 
     public CreateNetrisVnetCommand(Long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, String cidr, String gateway, boolean isVpc) {
         super(zoneId, accountId, domainId, vNetName, networkId, isVpc);
@@ -55,4 +56,12 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
     public String getGateway() {
         return gateway;
     }
+
+    public String getNetrisTag() {
+        return netrisTag;
+    }
+
+    public void setNetrisTag(String netrisTag) {
+        this.netrisTag = netrisTag;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/AddNetrisProviderCmd.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/AddNetrisProviderCmd.java
index d8e49e776ae..6bd9bddb17c 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/AddNetrisProviderCmd.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/command/AddNetrisProviderCmd.java
@@ -68,6 +68,9 @@ public class AddNetrisProviderCmd extends BaseCmd {
     @Parameter(name = ApiConstants.TENANT_NAME, type = CommandType.STRING, required = true, description = "Password to login into Netris")
     private String tenantName;
 
+    @Parameter(name = ApiConstants.NETRIS_TAG, type = CommandType.STRING, required = true, description = "Netris tag for vNets")
+    private String netrisTag;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -104,6 +107,10 @@ public class AddNetrisProviderCmd extends BaseCmd {
         return tenantName;
     }
 
+    public String getNetrisTag() {
+        return netrisTag;
+    }
+
     @Override
     public void execute() throws ServerApiException, ConcurrentOperationException {
         NetrisProvider provider = netrisProviderService.addProvider(this);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/response/NetrisProviderResponse.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/response/NetrisProviderResponse.java
index 0a6030659f0..d130c3a7372 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/response/NetrisProviderResponse.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/api/response/NetrisProviderResponse.java
@@ -57,6 +57,10 @@ public class NetrisProviderResponse extends BaseResponse {
     @Param(description = "Netris Admin tenant name")
     private String tenantName;
 
+    @SerializedName(ApiConstants.NETRIS_TAG)
+    @Param(description = "Netris Tag for vNets")
+    private String netrisTag;
+
     public String getName() {
         return name;
     }
@@ -120,4 +124,12 @@ public class NetrisProviderResponse extends BaseResponse {
     public void setTenantName(String tenantName) {
         this.tenantName = tenantName;
     }
+
+    public String getNetrisTag() {
+        return netrisTag;
+    }
+
+    public void setNetrisTag(String netrisTag) {
+        this.netrisTag = netrisTag;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 51de34552a5..3d1eb893b4e 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -86,6 +86,7 @@ import org.apache.logging.log4j.Logger;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -382,7 +383,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         Long networkId = cmd.getId();
         String vnetCidr = cmd.getCidr();
         Integer vxlanId = cmd.getVxlanId();
-        String gateway = cmd.getGateway();
+        String netrisTag = cmd.getNetrisTag();
         String netmask = vnetCidr.split("/")[1];
         String netrisGateway = cmd.getGateway() + "/" + netmask;
         boolean isVpc = cmd.isVpc();
@@ -407,7 +408,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
         logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
 
-        VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, netrisGateway, vxlanId);
+        VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, netrisGateway, 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);
@@ -899,7 +900,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
-    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String netrisGateway, Integer vxlanId) {
+    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String netrisGateway, Integer vxlanId, String netrisTag) {
         logger.debug("Creating Netris VPC vNet {} for CIDR {}", netrisVnetName, netrisGateway);
         try {
             VnetAddBody vnetBody = new VnetAddBody();
@@ -950,6 +951,8 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             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) {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
index a18251cee11..ff2d13d7f1b 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
@@ -86,6 +86,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
         final String password = cmd.getPassword();
         final String tenantName = cmd.getTenantName();
         final String siteName = cmd.getSiteName();
+        final String netrisTag = cmd.getNetrisTag();
 
         Map<String, String> params = new HashMap<>();
         params.put("guid", UUID.randomUUID().toString());
@@ -97,6 +98,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
         params.put("password", password);
         params.put("siteName", siteName);
         params.put("tenantName", tenantName);
+        params.put("netrisTag", netrisTag);
 
         Map<String, Object> hostdetails = new HashMap<>(params);
         NetrisProvider netrisProvider;
@@ -117,6 +119,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
                             .setPassword(password)
                             .setSiteName(siteName)
                             .setTenantName(tenantName)
+                            .setNetrisTag(netrisTag)
                             .build();
 
                     netrisProviderDao.persist(netrisProviderVO);
@@ -190,6 +193,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
         response.setZoneName(zone.getName());
         response.setSiteName(provider.getSiteName());
         response.setTenantName(provider.getTenantName());
+        response.setNetrisTag(provider.getNetrisTag());
         response.setObjectName("netrisProvider");
         return response;
     }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index a8fe3ae2054..a72b50fc8ed 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -203,6 +203,8 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         String vxlanId = Networks.BroadcastDomainType.getValue(network.getBroadcastUri());
         CreateNetrisVnetCommand cmd = new CreateNetrisVnetCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, cidr, network.getGateway(), !Objects.isNull(vpcId));
         cmd.setVxlanId(Integer.parseInt(vxlanId));
+        NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId);
+        cmd.setNetrisTag(netrisProvider.getNetrisTag());
         NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
         return answer.getResult();
     }
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 5db38ef83fc..7f72d4fa279 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -4580,12 +4580,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
         }
 
-        // If external provider is provided, verify zone has that provider enabled
+        // If external provider is provided, verify zone has that provider enabled and the controller added
         Provider provider = cmd.getProvider();
-        if (Objects.nonNull(provider)) {
+        NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId);
+        NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId);
+        if (Objects.nonNull(provider) && ObjectUtils.anyNotNull(nsxProvider, netrisProvider)) {
             boolean unsupported =
-                    (Provider.Nsx == provider && nsxProviderDao.findByZoneId(zoneId) == null) ||
-                            (Provider.Netris == provider && netrisProviderDao.findByZoneId(zoneId) == null);
+                    (Provider.Nsx == provider && nsxProvider == null) ||
+                            (Provider.Netris == provider && netrisProvider == null);
             if (unsupported) {
                 throw new InvalidParameterValueException(String.format("Cannot add public IP range as the zone does not support provider: %s", provider.getName()));
             }
@@ -4762,7 +4764,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                 }
             });
 
-            if (provider == Provider.Netris) {
+            if (provider == Provider.Netris && netrisProviderDao.findByZoneId(zoneId) != null) {
                 netrisService.createIPAMAllocationsForZoneLevelPublicRanges(zoneId);
             }
             messageBus.publish(_name, MESSAGE_CREATE_VLAN_IP_RANGE_EVENT, PublishScope.LOCAL, vlan);
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 8365853ad64..16c364f4280 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1485,6 +1485,7 @@
 "label.ncc": "NCC",
 "label.netmask": "Netmask",
 "label.netris": "Netris",
+"label.netristag": "Netris tag",
 "label.netris.provider": "Netris Provider",
 "label.netris.provider.name": "Netris provider name",
 "label.netris.provider.hostname": "Netris provider hostname",
@@ -1493,6 +1494,7 @@
 "label.netris.provider.password": "Netris provider password",
 "label.netris.provider.site": "Netris provider Site name",
 "label.netris.provider.tenant.name": "Netris provider Admin Tenant name",
+"label.netris.provider.tag": "Netris Tag",
 "label.netscaler": "NetScaler",
 "label.netscaler.mpx": "NetScaler MPX LoadBalancer",
 "label.netscaler.sdx": "NetScaler SDX LoadBalancer",
@@ -3228,6 +3230,7 @@
 "message.installwizard.tooltip.netris.provider.username": "Netris Provider username not provided",
 "message.installwizard.tooltip.netris.provider.password": "Netris Provider password not provided",
 "message.installwizard.tooltip.netris.provider.site": "Netris Provider Site name not provided",
+"message.installwizard.tooltip.netris.provider.tag": "Netris Tag to be assigned to vNets",
 "message.installwizard.tooltip.netris.provider.tenant.name": "Netris Provider Admin Tenant name not provided",
 "message.installwizard.tooltip.nsx.provider.hostname": "NSX Provider hostname / IP address not provided",
 "message.installwizard.tooltip.nsx.provider.username": "NSX Provider username not provided",
diff --git a/ui/src/views/infra/network/ServiceProvidersTab.vue b/ui/src/views/infra/network/ServiceProvidersTab.vue
index 16c965763c3..5da28bcab7f 100644
--- a/ui/src/views/infra/network/ServiceProvidersTab.vue
+++ b/ui/src/views/infra/network/ServiceProvidersTab.vue
@@ -1169,7 +1169,7 @@ export default {
                   value: (record) => { return record.zoneid }
                 }
               },
-              columns: ['name', 'hostname', 'port', 'site', 'tenantname']
+              columns: ['name', 'hostname', 'port', 'site', 'tenantname', 'netristag']
             }
           ]
         }
diff --git a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
index 8e773e196c7..ebbf92142dd 100644
--- a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
+++ b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
@@ -1127,6 +1127,7 @@ export default {
           providerParams.zoneid = this.stepData.zoneReturned.id
           providerParams.sitename = this.prefillContent?.siteName || ''
           providerParams.tenantname = this.prefillContent?.tenantName || ''
+          providerParams.netristag = this.prefillContent?.netrisTag || ''
 
           await this.addNetrisProvider(providerParams)
           this.stepData.stepMove.push('addNetrisProvider')
diff --git a/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue b/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue
index 863bb6a375c..66dbe3aeddc 100644
--- a/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue
+++ b/ui/src/views/infra/zone/ZoneWizardNetworkSetupStep.vue
@@ -519,6 +519,12 @@ export default {
           key: 'tenantName',
           placeHolder: 'message.installwizard.tooltip.netris.provider.tenant.name',
           required: true
+        },
+        {
+          title: 'label.netris.provider.tag',
+          key: 'netrisTag',
+          placeHolder: 'message.installwizard.tooltip.netris.provider.tag',
+          required: true
         }
       ]
       return fields


(cloudstack) 42/44: Update netris VPC and tier name

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 8572d6a776223edfc778c34322457b76f9cce1cd
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Tue Mar 4 22:55:37 2025 -0500

    Update netris VPC and tier name
---
 .../com/cloud/network/element/VpcProvider.java     |  2 +
 .../com/cloud/network/netris/NetrisService.java    |  2 +
 .../management/ContrailVpcElementImpl.java         |  5 +++
 .../agent/api/UpdateNetrisVnetCommand.java         | 17 ++++++++
 .../agent/api/UpdateNetrisVpcCommand.java          | 32 +++++++++++++++
 .../apache/cloudstack/resource/NetrisResource.java | 17 ++++++++
 .../apache/cloudstack/service/NetrisApiClient.java |  3 ++
 .../cloudstack/service/NetrisApiClientImpl.java    | 45 ++++++++++++++++++++++
 .../apache/cloudstack/service/NetrisElement.java   |  5 +++
 .../cloudstack/service/NetrisServiceImpl.java      |  9 +++++
 .../org/apache/cloudstack/service/NsxElement.java  |  5 +++
 .../network/element/VpcVirtualRouterElement.java   |  5 +++
 .../java/com/cloud/network/vpc/VpcManagerImpl.java | 11 ++++++
 .../cloudstack/service/NetrisServiceMockTest.java  |  5 +++
 14 files changed, 163 insertions(+)

diff --git a/api/src/main/java/com/cloud/network/element/VpcProvider.java b/api/src/main/java/com/cloud/network/element/VpcProvider.java
index 6debd1fbc2d..47e70d6597d 100644
--- a/api/src/main/java/com/cloud/network/element/VpcProvider.java
+++ b/api/src/main/java/com/cloud/network/element/VpcProvider.java
@@ -55,4 +55,6 @@ public interface VpcProvider extends NetworkElement {
     boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException;
 
     boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address);
+
+    boolean updateVpc(Vpc vpc, String previousVpcName);
 }
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index 43f94e0c6b7..f14e531b4da 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -28,6 +28,8 @@ public interface NetrisService {
 
     boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
 
+    boolean updateVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, String previousVpcName);
+
     boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
 
     boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting);
diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailVpcElementImpl.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailVpcElementImpl.java
index d5060443c9e..d492e6214ec 100644
--- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailVpcElementImpl.java
+++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailVpcElementImpl.java
@@ -201,4 +201,9 @@ public class ContrailVpcElementImpl extends ContrailElementImpl implements Netwo
     public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
         return true;
     }
+
+    @Override
+    public boolean updateVpc(Vpc vpc, String previousVpcName) {
+        return true;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java
new file mode 100644
index 00000000000..98fa9b7643a
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java
@@ -0,0 +1,17 @@
+package org.apache.cloudstack.agent.api;
+
+public class UpdateNetrisVnetCommand extends NetrisCommand{
+    private String prevNetworkName;
+
+    public UpdateNetrisVnetCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+    }
+
+    public String getPrevNetworkName() {
+        return prevNetworkName;
+    }
+
+    public void setPrevNetworkName(String prevNetworkName) {
+        this.prevNetworkName = prevNetworkName;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVpcCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVpcCommand.java
new file mode 100644
index 00000000000..764d9155a02
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVpcCommand.java
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.agent.api;
+
+public class UpdateNetrisVpcCommand extends NetrisCommand {
+    private String previousVpcName;
+    public UpdateNetrisVpcCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+    }
+
+    public String getPreviousVpcName() {
+        return previousVpcName;
+    }
+
+    public void setPreviousVpcName(String previousVpcName) {
+        this.previousVpcName = previousVpcName;
+    }
+}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index e7419caf743..6cb072e87e3 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -44,6 +44,7 @@ import org.apache.cloudstack.agent.api.NetrisAnswer;
 import org.apache.cloudstack.StartupNetrisCommand;
 import org.apache.cloudstack.agent.api.ReleaseNatIpCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
+import org.apache.cloudstack.agent.api.UpdateNetrisVpcCommand;
 import org.apache.cloudstack.service.NetrisApiClient;
 import org.apache.cloudstack.service.NetrisApiClientImpl;
 import org.apache.logging.log4j.LogManager;
@@ -98,6 +99,8 @@ public class NetrisResource implements ServerResource {
             return executeRequest((CheckHealthCommand) cmd);
         } else if (cmd instanceof CreateNetrisVpcCommand) {
             return executeRequest((CreateNetrisVpcCommand) cmd);
+        } else if (cmd instanceof UpdateNetrisVpcCommand) {
+          return executeRequest((UpdateNetrisVpcCommand) cmd);
         } else if (cmd instanceof DeleteNetrisVpcCommand) {
             return executeRequest((DeleteNetrisVpcCommand) cmd);
         } else if (cmd instanceof CreateNetrisVnetCommand) {
@@ -242,6 +245,20 @@ public class NetrisResource implements ServerResource {
         }
     }
 
+    private Answer executeRequest(UpdateNetrisVpcCommand cmd) {
+        try {
+            boolean result = netrisApiClient.updateVpc(cmd);
+            if (!result) {
+                return new NetrisAnswer(cmd, false, String.format("Netris VPC %s creation failed", cmd.getName()));
+            }
+            return new NetrisAnswer(cmd, true, "OK");
+        } catch (CloudRuntimeException e) {
+            String msg = String.format("Error creating Netris VPC: %s", e.getMessage());
+            logger.error(msg, e);
+            return new NetrisAnswer(cmd, new CloudRuntimeException(msg));
+        }
+    }
+
     private Answer executeRequest(CreateNetrisVnetCommand cmd) {
         try {
             String vpcName = cmd.getVpcName();
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index 5957805d22d..8f7fcbbb4c7 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -34,6 +34,7 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.ReleaseNatIpCommand;
 import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
+import org.apache.cloudstack.agent.api.UpdateNetrisVpcCommand;
 
 import java.util.List;
 
@@ -50,6 +51,8 @@ public interface NetrisApiClient {
      */
     boolean createVpc(CreateNetrisVpcCommand cmd);
 
+    boolean updateVpc(UpdateNetrisVpcCommand cmd);
+
     /**
      * Delete a VPC on CloudStack removes the following Netris resources:
      * - Delete the IPAM Allocation for the VPC using the Prefix = VPC CIDR
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 25c24bf0bf5..857959d4e1d 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -98,6 +98,8 @@ 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;
@@ -117,6 +119,7 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 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.UpdateNetrisVpcCommand;
 import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.ObjectUtils;
@@ -257,6 +260,30 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         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 {
@@ -302,6 +329,24 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         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 {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 5cc2a1c2a41..04268a292bd 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -466,6 +466,11 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
         return netrisService.updateVpcSourceNatIp(vpc, address);
     }
 
+    @Override
+    public boolean updateVpc(Vpc vpc, String previousVpcName) {
+        return netrisService.updateVpcResource(vpc.getZoneId(), vpc.getAccountId(), vpc.getDomainId(), vpc.getId(), vpc.getName(), previousVpcName);
+    }
+
     @Override
     public boolean applyNetworkACLs(Network network, List<? extends NetworkACLItem> rules) throws ResourceUnavailableException {
         if (!canHandle(network, Network.Service.NetworkACL)) {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 45e9147d632..b47f908958e 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -58,6 +58,7 @@ import org.apache.cloudstack.agent.api.NetrisAnswer;
 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.UpdateNetrisVpcCommand;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
@@ -198,6 +199,14 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         return answer.getResult();
     }
 
+    @Override
+    public boolean updateVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, String previousVpcName) {
+        UpdateNetrisVpcCommand cmd = new UpdateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, vpcId, true);
+        cmd.setPreviousVpcName(previousVpcName);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
     @Override
     public boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc) {
         DeleteNetrisVpcCommand cmd = new DeleteNetrisVpcCommand(zoneId, accountId, domainId, vpc.getName(), vpc.getCidr(), vpc.getId(), true);
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
index f56e69ab352..18e7ea26c8b 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
@@ -945,4 +945,9 @@ public class NsxElement extends AdapterBase implements  DhcpServiceProvider, Dns
     public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
         return nsxService.updateVpcSourceNatIp(vpc, address);
     }
+
+    @Override
+    public boolean updateVpc(Vpc vpc, String previousVpcName) {
+        return true;
+    }
 }
diff --git a/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java
index 841f6221182..c4f5a565d71 100644
--- a/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java
+++ b/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java
@@ -732,4 +732,9 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc
     public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) {
         return true;
     }
+
+    @Override
+    public boolean updateVpc(Vpc vpc, String previousVpcName) {
+        return true;
+    }
 }
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index dd49bbd79f9..62cd7afeaff 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -1582,6 +1582,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
         _accountMgr.checkAccess(caller, null, false, vpcToUpdate);
 
         final VpcVO vpc = vpcDao.createForUpdate(vpcId);
+        String previousVpcName = vpcToUpdate.getName();
 
         if (vpcName != null) {
             vpc.setName(vpcName);
@@ -1617,6 +1618,16 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
             } else {
                 if (logger.isDebugEnabled()) {
                     logger.debug("no restart needed.");
+                    if (isVpcForProvider(Provider.Netris, vpcToUpdate)) {
+                        final String sourceNatProvider = _vpcSrvcDao.getProviderForServiceInVpc(vpc.getId(), Service.SourceNat);
+                        for (final VpcProvider provider : getVpcElements()) {
+                            if ((provider instanceof StaticNatServiceProvider && provider.getName().equalsIgnoreCase(sourceNatProvider))) {
+                                vpcToUpdate.setName(vpcName);
+                                provider.updateVpc(vpcToUpdate, previousVpcName);
+                                break;
+                            }
+                        }
+                    }
                 }
             }
             return vpcDao.findById(vpcId);
diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index 7dbfc0b9cbb..dd3707c6051 100644
--- a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -36,6 +36,11 @@ public class NetrisServiceMockTest implements NetrisService {
         return true;
     }
 
+    @Override
+    public boolean updateVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, String previousVpcName) {
+        return true;
+    }
+
     @Override
     public boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc) {
         return true;


(cloudstack) 33/44: Add global routing flag on subnet creation (#45)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 48481945d1f02d27d765bf9ed1ecd91befa98c07
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Tue Feb 4 00:16:31 2025 -0300

    Add global routing flag on subnet creation (#45)
---
 .../main/java/com/cloud/network/netris/NetrisService.java  |  2 +-
 .../cloudstack/agent/api/CreateNetrisVnetCommand.java      |  9 +++++++++
 .../org/apache/cloudstack/service/NetrisApiClientImpl.java | 14 +++++++++-----
 .../apache/cloudstack/service/NetrisGuestNetworkGuru.java  | 11 ++++++++---
 .../org/apache/cloudstack/service/NetrisServiceImpl.java   |  3 ++-
 .../apache/cloudstack/service/NetrisServiceMockTest.java   |  2 +-
 6 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index 750a82ffe70..76a239422e0 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -30,7 +30,7 @@ public interface NetrisService {
 
     boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
 
-    boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
+    boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting);
 
     boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
 
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
index 7095c4549b1..e6de0244e48 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateNetrisVnetCommand.java
@@ -24,6 +24,7 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
     private String gateway;
     private String netrisTag;
     private String ipv6Cidr;
+    private Boolean globalRouting;
 
     public CreateNetrisVnetCommand(Long zoneId, Long accountId, Long domainId, String vpcName, Long vpcId, String vNetName, Long networkId, String cidr, String gateway, boolean isVpc) {
         super(zoneId, accountId, domainId, vNetName, networkId, isVpc);
@@ -73,4 +74,12 @@ public class CreateNetrisVnetCommand extends NetrisCommand {
     public void setIpv6Cidr(String ipv6Cidr) {
         this.ipv6Cidr = ipv6Cidr;
     }
+
+    public Boolean isGlobalRouting() {
+        return globalRouting;
+    }
+
+    public void setGlobalRouting(Boolean globalRouting) {
+        this.globalRouting = globalRouting;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 77d5aa40591..681924d82d8 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -716,6 +716,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String netrisGateway = cmd.getGateway() + "/" + netmask;
         String netrisV6Cidr = cmd.getIpv6Cidr();
         boolean isVpc = cmd.isVpc();
+        Boolean isGlobalRouting = cmd.isGlobalRouting();
 
         String netrisVpcName = getNetrisVpcName(cmd, vpcId, vpcName);
         VPCListing associatedVpc = getNetrisVpcResource(netrisVpcName);
@@ -733,7 +734,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String netrisVnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VNET, vNetName) ;
         String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
 
-        createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
+        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, netrisV6Cidr) ;
@@ -741,7 +742,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             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);
+            createIpamSubnetInternal(netrisV6SubnetName, netrisV6Cidr, SubnetBody.PurposeEnum.COMMON, associatedVpc, isGlobalRouting);
         }
         logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
 
@@ -851,7 +852,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             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);
+                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);
@@ -1000,7 +1001,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             List<IpTreeSubnet> matchedSubnets = getSubnet(vpcFilter, netrisSubnetName);
             if (matchedSubnets.isEmpty()) {
                 VPCListing systemVpc = getSystemVpc();
-                createIpamSubnetInternal(natIp, natIp, SubnetBody.PurposeEnum.NAT, systemVpc);
+                createIpamSubnetInternal(natIp, natIp, SubnetBody.PurposeEnum.NAT, systemVpc, null);
                 return;
             }
             logger.debug("NAT subnet: {} already exists", natIp);
@@ -1191,7 +1192,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
-    private InlineResponse2004Data createIpamSubnetInternal(String subnetName, String subnetPrefix, SubnetBody.PurposeEnum purpose, VPCListing vpc) {
+    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 {
 
@@ -1215,6 +1216,9 @@ public class NetrisApiClientImpl implements NetrisApiClient {
 
             subnetBody.setPurpose(purpose);
             subnetBody.setPrefix(subnetPrefix);
+            if (isGlobalRouting != null) {
+                subnetBody.setGlobalRouting(isGlobalRouting);
+            }
             IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
             InlineResponse2004 subnetResponse = ipamApi.apiV2IpamSubnetPost(subnetBody);
             if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
index 3bd508b52e3..e1f05c2a905 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
@@ -286,6 +286,12 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
         }
         String vpcName = null;
         Long vpcId = null;
+        Boolean globalRouting = null;
+        long networkOfferingId = networkVO.getNetworkOfferingId();
+        NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(networkOfferingId);
+        if (NetworkOffering.NetworkMode.ROUTED.equals(networkOfferingVO.getNetworkMode())) {
+            globalRouting = true;
+        }
         if (nonNull(networkVO.getVpcId())) {
             VpcVO vpc = _vpcDao.findById(networkVO.getVpcId());
             if (isNull(vpc)) {
@@ -295,8 +301,6 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
             vpcId = vpc.getId();
         } else {
             logger.debug(String.format("Creating IPAM Allocation before creating IPAM Subnet", networkVO.getName()));
-            long networkOfferingId = networkVO.getNetworkOfferingId();
-            NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(networkOfferingId);
             boolean isSourceNatSupported = !NetworkOffering.NetworkMode.ROUTED.equals(networkOfferingVO.getNetworkMode()) &&
                     networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(networkVO.getNetworkOfferingId(), Network.Service.SourceNat);
             boolean result = netrisService.createVpcResource(zone.getId(), networkVO.getAccountId(), networkVO.getDomainId(),
@@ -307,7 +311,8 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
                 throw new CloudRuntimeException(msg);
             }
         }
-        boolean result = netrisService.createVnetResource(zone.getId(), account.getId(), domain.getId(), vpcName, vpcId, networkVO.getName(), networkVO.getId(), networkVO.getCidr());
+        boolean result = netrisService.createVnetResource(zone.getId(), account.getId(), domain.getId(), vpcName, vpcId,
+                networkVO.getName(), networkVO.getId(), networkVO.getCidr(), globalRouting);
         if (!result) {
             throw new CloudRuntimeException("Failed to create Netris vNet resource for network: " + networkVO.getName());
         }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index a615090c429..9a4f5d972ab 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -204,13 +204,14 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
     }
 
     @Override
-    public boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
+    public boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting) {
         NetworkVO network = networkDao.findById(networkId);
         String vxlanId = Networks.BroadcastDomainType.getValue(network.getBroadcastUri());
         CreateNetrisVnetCommand cmd = new CreateNetrisVnetCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, cidr, network.getGateway(), !Objects.isNull(vpcId));
         cmd.setVxlanId(Integer.parseInt(vxlanId));
         NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId);
         cmd.setNetrisTag(netrisProvider.getNetrisTag());
+        cmd.setGlobalRouting(globalRouting);
         if (Objects.nonNull(networkId)) {
             Ipv6GuestPrefixSubnetNetworkMapVO ipv6PrefixNetworkMapVO = ipv6PrefixNetworkMapDao.findByNetworkId(networkId);
             if (Objects.nonNull(ipv6PrefixNetworkMapVO)) {
diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index ff715db4027..b8e4835f589 100644
--- a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -42,7 +42,7 @@ public class NetrisServiceMockTest implements NetrisService {
     }
 
     @Override
-    public boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
+    public boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting) {
         return true;
     }
 


(cloudstack) 41/44: Increase code coverage (#54)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit af27e88c828df27d48ef1d2f5b51d5e99ce63e78
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Fri Feb 28 10:04:10 2025 -0300

    Increase code coverage (#54)
    
    * Increase code coverage
    
    * More unit tests
    
    * Remove credentials and mock api client
    
    * NetrisResource tests
    
    * Fix unit test
---
 .../apache/cloudstack/resource/NetrisResource.java |   5 +-
 .../cloudstack/service/NetrisApiClientImpl.java    |   6 +-
 .../apache/cloudstack/service/NetrisElement.java   |   2 +-
 .../resource/NetrisResourceObjectUtilsTest.java    |  31 ++-
 .../cloudstack/resource/NetrisResourceTest.java    | 141 +++++++++++++
 .../service/NetrisApiClientImplTest.java           |  81 ++++++--
 .../cloudstack/service/NetrisElementTest.java      | 141 +++++++++++++
 .../service/NetrisGuestNetworkGuruTest.java        | 226 +++++++++++++++++++++
 .../service/NetrisPublicNetworkGuruTest.java       | 225 ++++++++++++++++++++
 9 files changed, 831 insertions(+), 27 deletions(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 2a02b5de1b4..e7419caf743 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -362,7 +362,7 @@ public class NetrisResource implements ServerResource {
         boolean result = netrisApiClient.createOrUpdateLbRule(cmd);
         if (!result) {
             return new NetrisAnswer(cmd, false, String.format("Failed to create Netris LB rule for %s: %s, " +
-                    "for private port: %s and public port: %s", getNetworkType(cmd.isVpc()), cmd.getName(), cmd.getPrivatePort(), cmd.getPublicPort(), cmd.getPublicPort()));
+                    "for private port: %s and public port: %s", getNetworkType(cmd.isVpc()), cmd.getName(), cmd.getPrivatePort(), cmd.getPublicPort()));
         }
         return new NetrisAnswer(cmd, true, "OK");
     }
@@ -370,8 +370,7 @@ public class NetrisResource implements ServerResource {
     private Answer executeRequest(DeleteNetrisLoadBalancerRuleCommand cmd) {
         boolean result = netrisApiClient.deleteLbRule(cmd);
         if (!result) {
-            return new NetrisAnswer(cmd, false, String.format("Failed to delete Netris LB rule for %s: %s, " +
-                    "for private port: %s and public port: %s", getNetworkType(cmd.isVpc()), cmd.getName()));
+            return new NetrisAnswer(cmd, false, String.format("Failed to delete Netris LB rule for %s: %s", getNetworkType(cmd.isVpc()), cmd.getName()));
         }
         return new NetrisAnswer(cmd, true, "OK");
     }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 0136a96cb79..25c24bf0bf5 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -139,11 +139,11 @@ public class NetrisApiClientImpl implements NetrisApiClient {
     private static final String ANY_IP = "0.0.0.0/0";
     private static final String[] PROTOCOL_LIST = new String[]{"TCP", "UDP", "ICMP", "ALL"};
 
-    private static ApiClient apiClient;
+    protected ApiClient apiClient;
 
-    private final int siteId;
+    protected final int siteId;
     private final String siteName;
-    private final int tenantId;
+    protected final int tenantId;
     private final String tenantName;
 
     public NetrisApiClientImpl(String endpointBaseUrl, String username, String password, String siteName, String adminTenantName) {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 27f8c99fcd0..5cc2a1c2a41 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -159,7 +159,7 @@ public class NetrisElement extends AdapterBase implements DhcpServiceProvider, D
         return counterList;
     }
 
-    private static Map<Network.Service, Map<Network.Capability, String>> initCapabilities() {
+    protected static Map<Network.Service, Map<Network.Capability, String>> initCapabilities() {
         Map<Network.Service, Map<Network.Capability, String>> capabilities = new HashMap<>();
 
         Map<Network.Capability, String> dhcpCapabilities = Map.of(Network.Capability.DhcpAccrossMultipleSubnets, "true");
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java
index e7d223672e4..acec5425929 100644
--- a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceObjectUtilsTest.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.resource;
 
+import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
@@ -28,7 +29,7 @@ public class NetrisResourceObjectUtilsTest {
     private static final long accountId = 2;
     private static final long domainId = 2;
 
-    private static final long vpcId = 10;
+    private static final long vpcId = 8;
     private static final String vpcName = "testVpc";
     private static final String vpcCidr = "10.10.0.0/16";
 
@@ -40,6 +41,14 @@ public class NetrisResourceObjectUtilsTest {
         Assert.assertEquals(expectedNetrisVpcName, netrisVpcName);
     }
 
+    @Test
+    public void testCreateVpcNameWithSuffix() {
+        CreateNetrisVpcCommand cmd = new CreateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, vpcCidr, vpcId, true);
+        String netrisVpcName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VPC, String.valueOf(vpcId));
+        String expectedNetrisVpcName = String.format("D%s-A%s-Z%s-V%s", domainId, accountId, zoneId, vpcId);
+        Assert.assertEquals(expectedNetrisVpcName, netrisVpcName);
+    }
+
     @Test
     public void testCreateVpcIpamAllocationName() {
         CreateNetrisVpcCommand cmd = new CreateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, vpcCidr, vpcId, true);
@@ -66,4 +75,24 @@ public class NetrisResourceObjectUtilsTest {
         String expectedNetrisRuleName = String.format("D%s-A%s-Z%s-V%s-DNAT-R%s", domainId, accountId, zoneId, vpcId, ruleId);
         Assert.assertEquals(expectedNetrisRuleName, ruleName);
     }
+
+    @Test
+    public void testSubnetName() {
+        String vNetName = "<NETRIS_VNET_NAME>";
+        Long vpcTierNetworkId = 240L;
+        String vpcTierNetworkCidr = "10.10.30.0/24";
+        String vpcTierNetworkGateway = "10.10.30.1";
+        CreateNetrisVnetCommand cmd = new CreateNetrisVnetCommand(zoneId, accountId, domainId, vpcName, vpcId, vNetName, vpcTierNetworkId, vpcTierNetworkCidr, vpcTierNetworkGateway, true);
+        String subnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, String.valueOf(vpcId), vpcTierNetworkCidr);
+        String expectedName = String.format("D%s-A%s-Z%s-V%s-%s", domainId, accountId, zoneId, vpcId, vpcTierNetworkCidr);
+        Assert.assertEquals(expectedName, subnetName);
+    }
+
+    @Test
+    public void testSourceNatName() {
+        CreateOrUpdateNetrisNatCommand cmd = new CreateOrUpdateNetrisNatCommand(zoneId, accountId, domainId, vpcName, vpcId, null, null, true, vpcCidr);
+        String snatRuleName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.SNAT, String.valueOf(vpcId));
+        String expectedName = String.format("D%s-A%s-Z%s-V%s-SNAT", domainId, accountId, zoneId, vpcId);
+        Assert.assertEquals(expectedName, snatRuleName);
+    }
 }
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceTest.java
new file mode 100644
index 00000000000..2f96e2cac0e
--- /dev/null
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/resource/NetrisResourceTest.java
@@ -0,0 +1,141 @@
+// 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.resource;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
+import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
+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.ReleaseNatIpCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
+import org.apache.cloudstack.service.NetrisApiClient;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class NetrisResourceTest {
+
+    @Mock
+    private NetrisApiClient netrisApiClient;
+
+    @Spy
+    @InjectMocks
+    private NetrisResource netrisResource = new NetrisResource();
+
+    private AutoCloseable closeable;
+
+    @Mock
+    private CreateNetrisVpcCommand createNetrisVpcCommand;
+    @Mock
+    private CreateNetrisVnetCommand createNetrisVnetCommand;
+    @Mock
+    private DeleteNetrisVnetCommand deleteNetrisVnetCommand;
+    @Mock
+    private DeleteNetrisVpcCommand deleteNetrisVpcCommand;
+    @Mock
+    private SetupNetrisPublicRangeCommand setupNetrisPublicRangeCommand;
+    @Mock
+    private DeleteNetrisNatRuleCommand deleteNetrisNatRuleCommand;
+    @Mock
+    private CreateNetrisACLCommand createNetrisACLCommand;
+    @Mock
+    private DeleteNetrisACLCommand deleteNetrisACLCommand;
+    @Mock
+    private AddOrUpdateNetrisStaticRouteCommand addOrUpdateNetrisStaticRouteCommand;
+    @Mock
+    private DeleteNetrisStaticRouteCommand deleteNetrisStaticRouteCommand;
+    @Mock
+    private ReleaseNatIpCommand releaseNatIpCommand;
+    @Mock
+    private CreateOrUpdateNetrisLoadBalancerRuleCommand createOrUpdateNetrisLoadBalancerRuleCommand;
+    @Mock
+    private DeleteNetrisLoadBalancerRuleCommand deleteNetrisLoadBalancerRuleCommand;
+
+    @Before
+    public void setup() {
+        closeable = MockitoAnnotations.openMocks(this);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        closeable.close();
+    }
+
+    @Test
+    public void testExecuteRequest() {
+        List<Command> commands = Arrays.asList(createNetrisVpcCommand, createNetrisVnetCommand, deleteNetrisVnetCommand,
+                deleteNetrisVpcCommand, setupNetrisPublicRangeCommand, deleteNetrisNatRuleCommand, createNetrisACLCommand,
+                deleteNetrisACLCommand, addOrUpdateNetrisStaticRouteCommand, deleteNetrisStaticRouteCommand,
+                releaseNatIpCommand, createOrUpdateNetrisLoadBalancerRuleCommand, deleteNetrisLoadBalancerRuleCommand);
+
+        for (boolean res : new boolean[]{true, false}) {
+            setMocksToValue(res);
+            for (Command command : commands) {
+                Answer answer = netrisResource.executeRequest(command);
+                Assert.assertEquals(res, answer.getResult());
+            }
+        }
+
+        Mockito.verify(netrisApiClient, Mockito.times(2)).createVpc(createNetrisVpcCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).createVnet(createNetrisVnetCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).deleteVnet(deleteNetrisVnetCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).deleteVpc(deleteNetrisVpcCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).setupZoneLevelPublicRange(setupNetrisPublicRangeCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).deleteNatRule(deleteNetrisNatRuleCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).addAclRule(createNetrisACLCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).deleteAclRule(deleteNetrisACLCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).addOrUpdateStaticRoute(addOrUpdateNetrisStaticRouteCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).deleteStaticRoute(deleteNetrisStaticRouteCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).releaseNatIp(releaseNatIpCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).createOrUpdateLbRule(createOrUpdateNetrisLoadBalancerRuleCommand);
+        Mockito.verify(netrisApiClient, Mockito.times(2)).deleteLbRule(deleteNetrisLoadBalancerRuleCommand);
+    }
+
+    private void setMocksToValue(boolean value) {
+        Mockito.when(netrisApiClient.createVpc(createNetrisVpcCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.createVnet(createNetrisVnetCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.deleteVnet(deleteNetrisVnetCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.deleteVpc(deleteNetrisVpcCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.setupZoneLevelPublicRange(setupNetrisPublicRangeCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.deleteNatRule(deleteNetrisNatRuleCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.addAclRule(createNetrisACLCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.deleteAclRule(deleteNetrisACLCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.addOrUpdateStaticRoute(addOrUpdateNetrisStaticRouteCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.deleteStaticRoute(deleteNetrisStaticRouteCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.releaseNatIp(releaseNatIpCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.createOrUpdateLbRule(createOrUpdateNetrisLoadBalancerRuleCommand)).thenReturn(value);
+        Mockito.when(netrisApiClient.deleteLbRule(deleteNetrisLoadBalancerRuleCommand)).thenReturn(value);
+    }
+}
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
index d57fbae1c86..6ae440afbff 100644
--- a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisApiClientImplTest.java
@@ -16,45 +16,88 @@
 // under the License.
 package org.apache.cloudstack.service;
 
-import io.netris.ApiException;
+import io.netris.ApiClient;
+import io.netris.ApiResponse;
+import io.netris.api.v1.AuthenticationApi;
+import io.netris.api.v1.SitesApi;
+import io.netris.api.v1.TenantsApi;
 import io.netris.model.GetSiteBody;
-import io.netris.model.VPCListing;
+import io.netris.model.SitesResponseOK;
+import io.netris.model.response.AuthResponse;
 import io.netris.model.response.TenantResponse;
+import io.netris.model.response.TenantsResponse;
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.MockedConstruction;
+import org.mockito.Mockito;
+import org.mockito.Spy;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 public class NetrisApiClientImplTest {
 
-    private static final String endpointUrl = "https://shapeblue-ctl.netris.dev";
-    private static final String username = "netris";
-    private static final String password = "qHHa$CZ2oJv*@!7mwoSR";
+    private static final String endpointUrl = "https://my-netris-controller.localdomain";
+    private static final String username = "user";
+    private static final String password = "password";
     private static final String siteName = "Datacenter-1";
     private static final String adminTenantName = "Admin";
+    private static final int siteId = 1;
+    private static final int adminTenantId = 1;
 
-    private static final NetrisApiClientImpl client = new NetrisApiClientImpl(endpointUrl, username, password, siteName, adminTenantName);
+    private MockedConstruction<ApiClient> apiClientMockedConstruction;
 
-    @Test
-    public void testNetrisAuthStatus() {
-        Assert.assertTrue(client.isSessionAlive());
+    @Spy
+    @InjectMocks
+    private NetrisApiClientImpl client;
+
+    @Before
+    public void setUp() {
+        GetSiteBody site = Mockito.mock(GetSiteBody.class);
+        SitesApi sitesApiMock = Mockito.mock(SitesApi.class);
+        Mockito.when(site.getName()).thenReturn(siteName);
+        Mockito.when(site.getId()).thenReturn(siteId);
+        TenantsApi tenantsApi = Mockito.mock(TenantsApi.class);
+        TenantResponse tenant = Mockito.mock(TenantResponse.class);
+        Mockito.when(tenant.getName()).thenReturn(adminTenantName);
+        Mockito.when(tenant.getId()).thenReturn(new BigDecimal(adminTenantId));
+
+        apiClientMockedConstruction = Mockito.mockConstruction(ApiClient.class, (mock, context) -> {
+            SitesResponseOK sitesResponse = Mockito.mock(SitesResponseOK.class);
+            Mockito.when(sitesResponse.getData()).thenReturn(List.of(site));
+            Mockito.when(sitesApiMock.apiSitesGet()).thenReturn(sitesResponse);
+            Mockito.when(mock.getApiStubForMethod(SitesApi.class)).thenReturn(sitesApiMock);
+            Mockito.when(mock.getApiStubForMethod(TenantsApi.class)).thenReturn(tenantsApi);
+            ApiResponse<TenantsResponse> tenantsResponse = Mockito.mock(ApiResponse.class);
+            Mockito.when(tenantsApi.apiTenantsGet()).thenReturn(tenantsResponse);
+            TenantsResponse tenantsResponseData = Mockito.mock(TenantsResponse.class);
+            Mockito.when(tenantsResponseData.getData()).thenReturn(List.of(tenant));
+            Mockito.when(tenantsResponse.getData()).thenReturn(tenantsResponseData);
+            AuthenticationApi authenticationApi = Mockito.mock(AuthenticationApi.class);
+            Mockito.when(mock.getApiStubForMethod(AuthenticationApi.class)).thenReturn(authenticationApi);
+            ApiResponse<AuthResponse> authResponseApiResponse = Mockito.mock(ApiResponse.class);
+            Mockito.when(authenticationApi.apiAuthGet()).thenReturn(authResponseApiResponse);
+            Mockito.when(authResponseApiResponse.getStatusCode()).thenReturn(200);
+        });
+        client = new NetrisApiClientImpl(endpointUrl, username, password, siteName, adminTenantName);
     }
 
-    @Test
-    public void testListSites() {
-        List<GetSiteBody> sites = client.listSites();
-        Assert.assertTrue(sites.size() > 0);
+    @After
+    public void tearDown() {
+        apiClientMockedConstruction.close();
     }
 
     @Test
-    public void testListVpcs() {
-        List<VPCListing> vpcs = client.listVPCs();
-        Assert.assertTrue(vpcs.size() > 0);
+    public void testConstructor() {
+        Assert.assertEquals(siteId, client.siteId);
+        Assert.assertEquals(adminTenantId, client.tenantId);
     }
 
     @Test
-    public void testListTenants() throws ApiException {
-        List<TenantResponse> tenants = client.listTenants();
-        Assert.assertTrue(tenants.size() > 0);
+    public void testNetrisAuthStatus() {
+        Assert.assertTrue(client.isSessionAlive());
     }
 }
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisElementTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisElementTest.java
new file mode 100644
index 00000000000..a2622549deb
--- /dev/null
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisElementTest.java
@@ -0,0 +1,141 @@
+// 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.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.domain.DomainVO;
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.Network;
+import com.cloud.network.as.AutoScaleCounter;
+import com.cloud.network.as.Counter;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
+import com.cloud.network.netris.NetrisService;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.vm.ReservationContext;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class NetrisElementTest {
+
+    @Spy
+    @InjectMocks
+    private NetrisElement netrisElement = new NetrisElement();;
+
+    @Mock
+    private NetrisService netrisService;
+    @Mock
+    private AccountManager accountManager;
+    @Mock
+    private NetworkDao networkDao;
+    @Mock
+    private DataCenterDao dataCenterDao;
+    @Mock
+    private DomainDao domainDao;
+    @Mock
+    private VpcDao vpcDao;
+
+    private AutoCloseable closeable;
+
+    private static long accountId = 2L;
+    private static long zoneId = 1L;
+
+    @Before
+    public void setup() {
+        closeable = MockitoAnnotations.openMocks(this);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        closeable.close();
+    }
+
+    @Test
+    public void testAutoscalingCounterList() {
+        List<AutoScaleCounter> counters = NetrisElement.getNetrisAutoScaleCounters();
+        Assert.assertEquals(2, counters.size());
+        Set<String> counterNamesSet = counters.stream().map(AutoScaleCounter::getName).collect(Collectors.toSet());
+        Set<String> expected = Set.of(Counter.Source.CPU.name(), Counter.Source.MEMORY.name());
+        Assert.assertEquals(expected, counterNamesSet);
+    }
+
+    @Test
+    public void testInitCapabilities() {
+        Map<Network.Service, Map<Network.Capability, String>> capabilities = NetrisElement.initCapabilities();
+        Assert.assertTrue(capabilities.containsKey(Network.Service.Dns));
+        Assert.assertTrue(capabilities.containsKey(Network.Service.Dhcp));
+        Assert.assertTrue(capabilities.containsKey(Network.Service.SourceNat));
+        Assert.assertTrue(capabilities.containsKey(Network.Service.StaticNat));
+        Assert.assertTrue(capabilities.containsKey(Network.Service.Lb));
+        Assert.assertTrue(capabilities.containsKey(Network.Service.PortForwarding));
+        Assert.assertTrue(capabilities.containsKey(Network.Service.NetworkACL));
+    }
+
+    @Test
+    public void testDeleteNetwork() throws ResourceUnavailableException {
+        long networkId = 210L;
+        long domainId = 2L;
+        long vpcId = 8L;
+        String vpcName = "testVpc";
+        String networkName = "testVpcTier";
+        String networkCidr = "10.10.30.0/24";
+        VpcVO vpc = Mockito.mock(VpcVO.class);
+        Mockito.when(vpc.getName()).thenReturn(vpcName);
+        Network network = Mockito.mock(Network.class);
+        Mockito.when(network.getAccountId()).thenReturn(accountId);
+        Mockito.when(network.getId()).thenReturn(networkId);
+        Mockito.when(network.getDataCenterId()).thenReturn(zoneId);
+        Mockito.when(network.getName()).thenReturn(networkName);
+        Mockito.when(network.getCidr()).thenReturn(networkCidr);
+        Mockito.when(network.getVpcId()).thenReturn(vpcId);
+        Account account = Mockito.mock(Account.class);
+        Mockito.when(account.getId()).thenReturn(accountId);
+        Mockito.when(account.getDomainId()).thenReturn(domainId);
+        NetworkVO networkVO = Mockito.mock(NetworkVO.class);
+        Mockito.when(networkVO.getName()).thenReturn(networkName);
+        DataCenterVO dataCenterVO = Mockito.mock(DataCenterVO.class);
+        Mockito.when(dataCenterVO.getId()).thenReturn(zoneId);
+        DomainVO domain = Mockito.mock(DomainVO.class);
+        Mockito.when(domain.getId()).thenReturn(domainId);
+        Mockito.when(accountManager.getAccount(accountId)).thenReturn(account);
+        Mockito.when(networkDao.findById(networkId)).thenReturn(networkVO);
+        Mockito.when(dataCenterDao.findById(zoneId)).thenReturn(dataCenterVO);
+        Mockito.when(domainDao.findById(domainId)).thenReturn(domain);
+        Mockito.when(vpcDao.findById(vpcId)).thenReturn(vpc);
+        ReservationContext context = Mockito.mock(ReservationContext.class);
+        netrisElement.destroy(network, context);
+        Mockito.verify(netrisService).deleteVnetResource(zoneId, accountId, domainId, vpcName, vpcId,
+                networkName, networkId, networkCidr);
+    }
+}
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisGuestNetworkGuruTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisGuestNetworkGuruTest.java
new file mode 100644
index 00000000000..9548855998a
--- /dev/null
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisGuestNetworkGuruTest.java
@@ -0,0 +1,226 @@
+// 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.dc.DataCenter;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.deploy.DeployDestination;
+import com.cloud.deploy.DeploymentPlan;
+import com.cloud.domain.DomainVO;
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.event.ActionEventUtils;
+import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
+import com.cloud.network.Network;
+import com.cloud.network.NetworkModel;
+import com.cloud.network.Networks;
+import com.cloud.network.dao.NetworkVO;
+import com.cloud.network.dao.PhysicalNetworkDao;
+import com.cloud.network.dao.PhysicalNetworkVO;
+import com.cloud.network.netris.NetrisService;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.offering.NetworkOffering;
+import com.cloud.offerings.NetworkOfferingVO;
+import com.cloud.offerings.dao.NetworkOfferingDao;
+import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
+import com.cloud.user.AccountVO;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.vm.ReservationContext;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+
+public class NetrisGuestNetworkGuruTest {
+
+    @Mock
+    private NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
+    @Mock
+    private PhysicalNetworkDao physicalNetworkDao;
+    @Mock
+    private DataCenterDao dataCenterDao;
+    @Mock
+    private NetworkModel networkModel;
+    @Mock
+    private AccountDao accountDao;
+    @Mock
+    private DomainDao domainDao;
+    @Mock
+    private NetworkOfferingDao networkOfferingDao;
+    @Mock
+    private VpcDao vpcDao;
+    @Mock
+    private NetrisService netrisService;
+
+    @Spy
+    @InjectMocks
+    private NetrisGuestNetworkGuru guru = new NetrisGuestNetworkGuru();
+
+    @Mock
+    private NetworkOfferingVO networkOffering;
+    @Mock
+    private PhysicalNetworkVO physicalNetwork;
+    @Mock
+    private DeploymentPlan plan;
+    @Mock
+    private NetworkVO network;
+    @Mock
+    private AccountVO account;
+    @Mock
+    private DomainVO domain;
+    @Mock
+    private DataCenterVO zone;
+    @Mock
+    private VpcVO vpc;
+
+    private AutoCloseable closeable;
+    private MockedStatic<ActionEventUtils> actionEventUtilsMocked;
+
+    private static final long networkOfferingId = 10L;
+    private static final long physicalNetworkId = 2L;
+    private static final long zoneId = 1L;
+    private static final long accountId = 2L;
+    private static final long domainId = 7L;
+    private static final long vpcId = 12L;
+    private static final long networkId = 210L;
+    private static final String networkName = "test-network";
+    private static final String vpcName = "test-vpc";
+    private static final String networkCidr = "172.20.10.0/24";
+
+    @Before
+    public void setup() {
+        closeable = MockitoAnnotations.openMocks(this);
+        Mockito.when(networkOffering.getId()).thenReturn(networkOfferingId);
+        Mockito.when(networkOffering.getTrafficType()).thenReturn(Networks.TrafficType.Guest);
+        Mockito.when(networkOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.NATTED);
+        Mockito.when(networkOffering.isRedundantRouter()).thenReturn(false);
+        Mockito.when(networkOfferingDao.findById(networkOfferingId)).thenReturn(networkOffering);
+        Mockito.when(physicalNetwork.getIsolationMethods()).thenReturn(List.of("netris"));
+        Mockito.when(physicalNetworkDao.findById(physicalNetworkId)).thenReturn(physicalNetwork);
+        Mockito.when(networkOfferingServiceMapDao.isProviderForNetworkOffering(networkOfferingId, Network.Provider.Netris)).thenReturn(true);
+        Mockito.when(plan.getPhysicalNetworkId()).thenReturn(physicalNetworkId);
+        Mockito.when(plan.getDataCenterId()).thenReturn(zoneId);
+        Mockito.when(dataCenterDao.findById(zoneId)).thenReturn(zone);
+        Mockito.when(zone.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
+        Mockito.when(zone.getGuestNetworkCidr()).thenReturn("172.20.0.0/16");
+        Mockito.when(zone.getId()).thenReturn(zoneId);
+        List<Network.Service> offeringServices = Arrays.asList(Network.Service.Dns, Network.Service.Dhcp,
+                Network.Service.SourceNat, Network.Service.StaticNat, Network.Service.PortForwarding,
+                Network.Service.NetworkACL, Network.Service.Vpn);
+        Mockito.when(networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(offeringServices);
+        Mockito.when(accountDao.findById(accountId)).thenReturn(account);
+        Mockito.when(account.getDomainId()).thenReturn(domainId);
+        Mockito.when(account.getId()).thenReturn(accountId);
+        Mockito.when(domain.getId()).thenReturn(domainId);
+        Mockito.when(domainDao.findById(domainId)).thenReturn(domain);
+        Mockito.when(network.getAccountId()).thenReturn(accountId);
+        Mockito.when(network.getNetworkOfferingId()).thenReturn(networkOfferingId);
+        Mockito.when(network.getVpcId()).thenReturn(vpcId);
+        Mockito.when(network.getName()).thenReturn(networkName);
+        Mockito.when(network.getId()).thenReturn(networkId);
+        Mockito.when(network.getCidr()).thenReturn(networkCidr);
+        Mockito.when(network.getGateway()).thenReturn("172.20.10.1");
+        Mockito.when(network.getDataCenterId()).thenReturn(zoneId);
+        Mockito.when(network.getPhysicalNetworkId()).thenReturn(physicalNetworkId);
+        Mockito.when(network.getTrafficType()).thenReturn(Networks.TrafficType.Guest);
+        Mockito.when(network.getBroadcastDomainType()).thenReturn(Networks.BroadcastDomainType.Netris);
+        Mockito.when(vpcDao.findById(vpcId)).thenReturn(vpc);
+        Mockito.when(vpc.getName()).thenReturn(vpcName);
+        Mockito.when(vpc.getId()).thenReturn(vpcId);
+        actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class);
+        Mockito.when(ActionEventUtils.onCompletedActionEvent(anyLong(), anyLong(), anyString(), anyString(), anyString(), anyLong(), anyString(), anyLong())).thenReturn(1L);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        closeable.close();
+        actionEventUtilsMocked.close();
+    }
+
+    @Test
+    public void testCanHandleNetrisOfferingNatted() {
+        Assert.assertTrue(guru.canHandle(networkOffering, DataCenter.NetworkType.Advanced, physicalNetwork));
+    }
+
+    @Test
+    public void testCanHandleNetrisOfferingRouted() {
+        Mockito.when(networkOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED);
+        Assert.assertTrue(guru.canHandle(networkOffering, DataCenter.NetworkType.Advanced, physicalNetwork));
+    }
+
+    @Test
+    public void testCannotHandleBasicNetwork() {
+        Assert.assertFalse(guru.canHandle(networkOffering, DataCenter.NetworkType.Basic, physicalNetwork));
+    }
+
+    @Test
+    public void testCannotHandleVlanIsolation() {
+        Mockito.when(physicalNetwork.getIsolationMethods()).thenReturn(List.of("vlan"));
+        Assert.assertFalse(guru.canHandle(networkOffering, DataCenter.NetworkType.Advanced, physicalNetwork));
+    }
+
+    @Test
+    public void testCannotHandleDifferentOfferingProvider() {
+        Mockito.when(networkOfferingServiceMapDao.isProviderForNetworkOffering(networkOfferingId, Network.Provider.Netris)).thenReturn(false);
+        Assert.assertFalse(guru.canHandle(networkOffering, DataCenter.NetworkType.Advanced, physicalNetwork));
+    }
+
+    @Test
+    public void testDesignNetrisNetwork() {
+        Network designedNetwork = guru.design(networkOffering, plan, network, networkName, 1L, account);
+        Assert.assertEquals(Networks.BroadcastDomainType.Netris, designedNetwork.getBroadcastDomainType());
+        Assert.assertEquals(Network.State.Allocated, designedNetwork.getState());
+        Assert.assertEquals(Networks.TrafficType.Guest, designedNetwork.getTrafficType());
+    }
+
+    @Test
+    public void testCreateNetrisVnetVpcNetworkRoutedMode() {
+        Mockito.when(networkOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED);
+        Mockito.when(netrisService.createVnetResource(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(),
+                Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(),
+                Mockito.anyBoolean())).thenReturn(true);
+        guru.createNetrisVnet(network, zone);
+        Mockito.verify(netrisService).createVnetResource(zoneId, accountId, domainId, vpcName, vpcId,
+                networkName, networkId, networkCidr, true);
+    }
+
+    @Test
+    public void testImplementNetrisVpcNetwork() throws InsufficientVirtualNetworkCapacityException {
+        DeployDestination destination = Mockito.mock(DeployDestination.class);
+        ReservationContext context = Mockito.mock(ReservationContext.class);
+        String vnet = "1234";
+        Mockito.when(dataCenterDao.allocateVnet(Mockito.eq(zoneId), Mockito.eq(physicalNetworkId),
+                Mockito.eq(accountId), Mockito.nullable(String.class), Mockito.anyBoolean())).thenReturn(vnet);
+        actionEventUtilsMocked.when(() -> ActionEventUtils.onCompletedActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong())).thenReturn(1L);
+        Network implemented = guru.implement(network, networkOffering, destination, context);
+        Assert.assertEquals(String.format("netris://%s", vnet), implemented.getBroadcastUri().toString());
+        Assert.assertEquals(Network.State.Implemented, implemented.getState());
+    }
+}
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisPublicNetworkGuruTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisPublicNetworkGuruTest.java
new file mode 100644
index 00000000000..531e343f6aa
--- /dev/null
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisPublicNetworkGuruTest.java
@@ -0,0 +1,225 @@
+// 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.dc.VlanDetailsVO;
+import com.cloud.dc.dao.VlanDetailsDao;
+import com.cloud.deploy.DeploymentPlan;
+import com.cloud.exception.InsufficientAddressCapacityException;
+import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
+import com.cloud.network.Network;
+import com.cloud.network.NetworkModel;
+import com.cloud.network.Networks;
+import com.cloud.network.dao.IPAddressDao;
+import com.cloud.network.dao.IPAddressVO;
+import com.cloud.network.dao.NetworkVO;
+import com.cloud.network.netris.NetrisService;
+import com.cloud.network.vpc.VpcOfferingVO;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.network.vpc.dao.VpcOfferingDao;
+import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
+import com.cloud.offering.NetworkOffering;
+import com.cloud.offerings.NetworkOfferingVO;
+import com.cloud.user.AccountVO;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.Ip;
+import com.cloud.vm.NicProfile;
+import com.cloud.vm.VirtualMachineProfile;
+import org.apache.cloudstack.api.ApiConstants;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.Arrays;
+
+public class NetrisPublicNetworkGuruTest {
+
+    @Mock
+    private NetworkModel networkModel;
+    @Mock
+    private IPAddressDao ipAddressDao;
+    @Mock
+    private VpcDao vpcDao;
+    @Mock
+    private VlanDetailsDao vlanDetailsDao;
+    @Mock
+    private VpcOfferingDao vpcOfferingDao;
+    @Mock
+    private VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
+    @Mock
+    private NetrisService netrisService;
+    @Spy
+    @InjectMocks
+    private NetrisPublicNetworkGuru guru = new NetrisPublicNetworkGuru();
+
+    @Mock
+    private NetworkOfferingVO networkOffering;
+    @Mock
+    private DeploymentPlan deploymentPlan;
+    @Mock
+    private NetworkVO network;
+    @Mock
+    private AccountVO account;
+    @Mock
+    private NicProfile nicProfile;
+    @Mock
+    private VirtualMachineProfile virtualMachineProfile;
+    @Mock
+    private IPAddressVO ipAddressVpcVR;
+    @Mock
+    private IPAddressVO ipAddressVpcSourceNat;
+    @Mock
+    private VpcVO vpc;
+
+    private AutoCloseable closeable;
+
+    private static final long networkOfferingId = 10L;
+    private static final long physicalNetworkId = 2L;
+    private static final long zoneId = 1L;
+    private static final long vpcId = 12L;
+    private static final String vrNicIp = "10.10.10.10";
+    private static final String vpcSourceNatIp = "10.10.20.20";
+
+    @Before
+    public void setup() {
+        closeable = MockitoAnnotations.openMocks(this);
+        Mockito.when(networkOffering.getId()).thenReturn(networkOfferingId);
+        Mockito.when(networkOffering.getTrafficType()).thenReturn(Networks.TrafficType.Public);
+        Mockito.when(networkOffering.isSystemOnly()).thenReturn(true);
+        Mockito.when(networkModel.isProviderForNetworkOffering(Network.Provider.Netris, networkOfferingId)).thenReturn(true);
+        Mockito.when(network.getBroadcastDomainType()).thenReturn(Networks.BroadcastDomainType.Netris);
+        Mockito.when(deploymentPlan.getDataCenterId()).thenReturn(zoneId);
+        Mockito.when(deploymentPlan.getPhysicalNetworkId()).thenReturn(physicalNetworkId);
+        Mockito.when(networkOffering.isRedundantRouter()).thenReturn(false);
+        Mockito.when(nicProfile.getIPv4Address()).thenReturn(vrNicIp);
+        Mockito.when(ipAddressDao.findByIp(vrNicIp)).thenReturn(ipAddressVpcVR);
+        Mockito.when(ipAddressVpcVR.getVpcId()).thenReturn(vpcId);
+        Mockito.when(vpcDao.findById(vpcId)).thenReturn(vpc);
+        Mockito.when(vpc.getId()).thenReturn(vpcId);
+        Mockito.when(ipAddressDao.listByAssociatedVpc(vpcId, true))
+                .thenReturn(Arrays.asList(ipAddressVpcVR, ipAddressVpcSourceNat));
+        Ip ipMock = Mockito.mock(Ip.class);
+        Mockito.when(ipMock.addr()).thenReturn(vrNicIp);
+        Mockito.when(ipAddressVpcVR.getAddress()).thenReturn(ipMock);
+        Ip ipVrMock = Mockito.mock(Ip.class);
+        Mockito.when(ipVrMock.addr()).thenReturn(vpcSourceNatIp);
+        Mockito.when(ipAddressVpcSourceNat.getAddress()).thenReturn(ipVrMock);
+        Mockito.when(ipAddressVpcSourceNat.isSourceNat()).thenReturn(true);
+        Mockito.when(ipAddressVpcSourceNat.isForSystemVms()).thenReturn(false);
+        Mockito.when(ipAddressVpcSourceNat.getVlanId()).thenReturn(4L);
+        VlanDetailsVO vlanDetailsVO = Mockito.mock(VlanDetailsVO.class);
+        Mockito.when(vlanDetailsVO.getValue()).thenReturn("true");
+        Mockito.when(vlanDetailsDao.findDetail(4L, ApiConstants.NETRIS_DETAIL_KEY)).thenReturn(vlanDetailsVO);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        closeable.close();
+    }
+
+    @Test
+    public void testCanHandleNetrisPublic() {
+        Assert.assertTrue(guru.canHandle(networkOffering));
+    }
+
+    @Test
+    public void testCannotHandleNonNetrisPublic() {
+        Mockito.when(networkModel.isProviderForNetworkOffering(Network.Provider.Netris, networkOfferingId)).thenReturn(false);
+        Assert.assertFalse(guru.canHandle(networkOffering));
+    }
+
+    @Test
+    public void testCannotHandleNonPublicTraffic() {
+        Mockito.when(networkOffering.getTrafficType()).thenReturn(Networks.TrafficType.Guest);
+        Assert.assertFalse(guru.canHandle(networkOffering));
+    }
+
+    @Test
+    public void testDesignNetrisNetwork() {
+        String name = "test-network";
+        long vpcId = 10L;
+        Network design = guru.design(networkOffering, deploymentPlan, network, name, vpcId, account);
+        Assert.assertEquals(Network.State.Setup, design.getState());
+        Assert.assertEquals(Networks.BroadcastDomainType.Netris, design.getBroadcastDomainType());
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testAllocateNetrisNetworkMissingIpAddress() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
+        Mockito.when(ipAddressDao.findByIp(vrNicIp)).thenReturn(null);
+        guru.allocate(network, nicProfile, virtualMachineProfile);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testAllocateNetrisNetworkMissingVpc() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
+        Mockito.when(vpcDao.findById(vpcId)).thenReturn(null);
+        guru.allocate(network, nicProfile, virtualMachineProfile);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testAllocateNetrisNetworkSourceNatIps() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
+        Mockito.when(vpcDao.findById(vpcId)).thenReturn(null);
+        guru.allocate(network, nicProfile, virtualMachineProfile);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testAllocateNetrisNetworkMissingIps() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
+        Mockito.when(ipAddressDao.listByAssociatedVpc(vpcId, true)).thenReturn(null);
+        guru.allocate(network, nicProfile, virtualMachineProfile);
+    }
+
+    @Test
+    public void testAllocateNetrisNetwork() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
+        long vpcOfferingId = 20L;
+        long accountId = 2L;
+        long domainId = 8L;
+        long networkId = 210L;
+        String vpcName = "test-vpc";
+        String vpcCidr = "172.20.10.1/28";
+        String networkName = "vpc-tier";
+        Mockito.when(vpc.getAccountId()).thenReturn(accountId);
+        Mockito.when(vpc.getZoneId()).thenReturn(zoneId);
+        Mockito.when(vpc.getDomainId()).thenReturn(domainId);
+        Mockito.when(vpc.getVpcOfferingId()).thenReturn(vpcOfferingId);
+        Mockito.when(vpc.getName()).thenReturn(vpcName);
+        Mockito.when(vpc.getCidr()).thenReturn(vpcCidr);
+        Mockito.when(network.getName()).thenReturn(networkName);
+        Mockito.when(network.getId()).thenReturn(networkId);
+        VpcOfferingVO vpcOffering = Mockito.mock(VpcOfferingVO.class);
+        Mockito.when(vpcOfferingDao.findById(vpcOfferingId)).thenReturn(vpcOffering);
+        Mockito.when(vpcOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.NATTED);
+        Mockito.when(vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(
+                vpcOfferingId, new Network.Service[]{Network.Service.SourceNat})).thenReturn(true);
+        Mockito.when(netrisService.createVpcResource(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(),
+                Mockito.anyLong(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyString(),
+                Mockito.anyBoolean())).thenReturn(true);
+        Mockito.when(netrisService.createSnatRule(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(),
+                Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong(),
+                Mockito.anyBoolean(), Mockito.anyString(), Mockito.anyString())).thenReturn(true);
+        guru.allocate(network, nicProfile, virtualMachineProfile);
+        Mockito.verify(netrisService).createVpcResource(zoneId, accountId, domainId, vpcId, vpcName, true,
+                vpcCidr, true);
+        Mockito.verify(netrisService).createSnatRule(zoneId, accountId, domainId, vpcName, vpcId, networkName,
+                networkId, true, vpcCidr, vpcSourceNatIp);
+    }
+}


(cloudstack) 43/44: add support to update vpc tier name

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 1ee69a923abb91fb11bd4ba4971b14bc7c14c489
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Wed Mar 5 17:48:27 2025 -0500

    add support to update vpc tier name
---
 .../java/com/cloud/network/guru/NetworkGuru.java   |   4 +
 .../com/cloud/network/netris/NetrisService.java    |   2 +
 .../engine/orchestration/NetworkOrchestrator.java  |   4 +-
 .../agent/api/UpdateNetrisVnetCommand.java         |  18 +++
 .../apache/cloudstack/resource/NetrisResource.java |  19 +++
 .../apache/cloudstack/service/NetrisApiClient.java |   3 +
 .../cloudstack/service/NetrisApiClientImpl.java    | 139 ++++++++++++++++++++-
 .../cloudstack/service/NetrisGuestNetworkGuru.java |  14 +++
 .../cloudstack/service/NetrisServiceImpl.java      |  11 ++
 .../java/com/cloud/network/NetworkServiceImpl.java |  11 +-
 .../cloudstack/service/NetrisServiceMockTest.java  |   5 +
 11 files changed, 225 insertions(+), 5 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java
index 7b81c75ed84..ced664e54a9 100644
--- a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java
+++ b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java
@@ -215,4 +215,8 @@ public interface NetworkGuru extends Adapter {
     default boolean isSlaacV6Only() {
         return true;
     }
+
+    default boolean update(Network network, String prevNetworkName) {
+        return true;
+    }
 }
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index f14e531b4da..5607d53f55b 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -34,6 +34,8 @@ public interface NetrisService {
 
     boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting);
 
+    boolean updateVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String prevNetworkName);
+
     boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
 
     boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String sourceNatIp);
diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 7e1ca652f47..30b63053927 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -1099,8 +1099,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                 nsxGuru.allocate(network, profile, vm);
             } else if (isNicAllocatedForProviderPublicNetworkOnVR(network, profile, vm, Provider.Netris)) {
                 String guruName = "NetrisPublicNetworkGuru";
-                NetworkGuru nsxGuru = AdapterBase.getAdapterByName(networkGurus, guruName);
-                nsxGuru.allocate(network, profile, vm);
+                NetworkGuru netrisGuru = AdapterBase.getAdapterByName(networkGurus, guruName);
+                netrisGuru.allocate(network, profile, vm);
             }
 
             if (isDefaultNic != null) {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java
index 98fa9b7643a..e3307cb8842 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/UpdateNetrisVnetCommand.java
@@ -2,6 +2,8 @@ package org.apache.cloudstack.agent.api;
 
 public class UpdateNetrisVnetCommand extends NetrisCommand{
     private String prevNetworkName;
+    private String vpcName;
+    private Long vpcId;
 
     public UpdateNetrisVnetCommand(long zoneId, Long accountId, Long domainId, String name, Long id, boolean isVpc) {
         super(zoneId, accountId, domainId, name, id, isVpc);
@@ -14,4 +16,20 @@ public class UpdateNetrisVnetCommand extends NetrisCommand{
     public void setPrevNetworkName(String prevNetworkName) {
         this.prevNetworkName = prevNetworkName;
     }
+
+    public String getVpcName() {
+        return vpcName;
+    }
+
+    public void setVpcName(String vpcName) {
+        this.vpcName = vpcName;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public void setVpcId(Long vpcId) {
+        this.vpcId = vpcId;
+    }
 }
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 6cb072e87e3..32dac893073 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -44,6 +44,7 @@ import org.apache.cloudstack.agent.api.NetrisAnswer;
 import org.apache.cloudstack.StartupNetrisCommand;
 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.service.NetrisApiClient;
 import org.apache.cloudstack.service.NetrisApiClientImpl;
@@ -105,6 +106,8 @@ public class NetrisResource implements ServerResource {
             return executeRequest((DeleteNetrisVpcCommand) cmd);
         } else if (cmd instanceof CreateNetrisVnetCommand) {
             return executeRequest((CreateNetrisVnetCommand) cmd);
+        } else if (cmd instanceof UpdateNetrisVnetCommand) {
+          return executeRequest((UpdateNetrisVnetCommand) cmd);
         } else if (cmd instanceof DeleteNetrisVnetCommand) {
           return executeRequest((DeleteNetrisVnetCommand) cmd);
         } else if (cmd instanceof SetupNetrisPublicRangeCommand) {
@@ -274,6 +277,22 @@ public class NetrisResource implements ServerResource {
         }
     }
 
+    private Answer executeRequest(UpdateNetrisVnetCommand cmd) {
+        try {
+            String networkName = cmd.getName();
+            String prevNetworkName = cmd.getPrevNetworkName();
+            boolean result = netrisApiClient.updateVnet(cmd);
+            if (!result) {
+                return new NetrisAnswer(cmd, false, String.format("Failed to update network name from %s to %s", prevNetworkName, networkName));
+            }
+            return new NetrisAnswer(cmd, true, "OK");
+        } catch (CloudRuntimeException e) {
+            String msg = String.format("Error updating Netris vNet: %s", e.getMessage());
+            logger.error(msg, e);
+            return new NetrisAnswer(cmd, new CloudRuntimeException(msg));
+        }
+    }
+
     private Answer executeRequest(DeleteNetrisVnetCommand cmd) {
         try {
             String networkName = cmd.getName();
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index 8f7fcbbb4c7..1b4e64bf971 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -34,6 +34,7 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 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 java.util.List;
@@ -67,6 +68,8 @@ public interface NetrisApiClient {
      */
     boolean createVnet(CreateNetrisVnetCommand cmd);
 
+    boolean updateVnet(UpdateNetrisVnetCommand cmd);
+
     /**
      * Deletion of a VPC network tier deletes the following Netris resources:
      * - Deletes the Netris IPAM Subnet for the specified network tier's CIDR
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 857959d4e1d..c2b52093934 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -94,6 +94,9 @@ 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;
@@ -119,6 +122,7 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
 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;
@@ -989,6 +993,127 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         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();
@@ -1400,14 +1525,24 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         return true;
     }
 
-    private void deleteVnetInternal(VPCListing associatedVpc, FilterBySites siteFilter, FilterByVpc vpcFilter, String netrisVnetName, String vNetName) {
+    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));
             }
-            List<VnetsBody> vnetsList = vnetList.getData().stream().filter(vnet -> vnet.getName().equals(netrisVnetName)).collect(Collectors.toList());
+            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;
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
index e1f05c2a905..3e3b75e0dc7 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
@@ -159,6 +159,20 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
         // Do nothing
     }
 
+    @Override
+    public boolean update(Network network, String prevNetworkName) {
+        Long vpcId = network.getVpcId();
+        String vpcName = null;
+        if (Objects.nonNull(vpcId)) {
+            VpcVO vpc = _vpcDao.findById(vpcId);
+            if (Objects.nonNull(vpc)) {
+                vpcName = vpc.getName();
+            }
+        }
+        return netrisService.updateVnetResource(network.getDataCenterId(), network.getAccountId(), network.getDomainId(),
+                vpcName, vpcId, network.getName(), network.getId(), prevNetworkName);
+    }
+
     @Override
     public Network implement(Network network, NetworkOffering offering, DeployDestination dest,
                              ReservationContext context) throws InsufficientVirtualNetworkCapacityException {
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index b47f908958e..541390a8053 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -58,6 +58,7 @@ import org.apache.cloudstack.agent.api.NetrisAnswer;
 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.api.ApiConstants;
 import org.apache.cloudstack.framework.config.ConfigKey;
@@ -233,6 +234,16 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
         return answer.getResult();
     }
 
+    @Override
+    public boolean updateVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String prevNetworkName) {
+        UpdateNetrisVnetCommand cmd = new UpdateNetrisVnetCommand(zoneId, accountId, domainId, networkName, networkId, Objects.nonNull(vpcId));
+        cmd.setPrevNetworkName(prevNetworkName);
+        cmd.setVpcId(vpcId);
+        cmd.setVpcName(vpcName);
+        NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+        return answer.getResult();
+    }
+
     @Override
     public boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
         DeleteNetrisVnetCommand cmd = new DeleteNetrisVnetCommand(zoneId, accountId, domainId, networkName, networkId, vpcName, vpcId, cidr, Objects.nonNull(vpcName));
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 3d218f3b0a5..4c8d599a28e 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -51,6 +51,7 @@ import com.cloud.network.element.NsxProviderVO;
 import com.cloud.network.element.VirtualRouterProviderVO;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.utils.component.AdapterBase;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import org.apache.cloudstack.alert.AlertService;
@@ -3087,7 +3088,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
 
         // verify input parameters
         final NetworkVO network = getNetworkVO(networkId, "Specified network id doesn't exist in the system");
-
+        String prevNetworkName = network.getName();
         //perform below validation if the network is vpc network
         if (network.getVpcId() != null && networkOfferingId != null) {
             Vpc vpc = _entityMgr.findById(Vpc.class, network.getVpcId());
@@ -3572,9 +3573,17 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
         Network updatedNetwork = getNetwork(network.getId());
         UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_UPDATE, updatedNetwork.getAccountId(), updatedNetwork.getDataCenterId(), updatedNetwork.getId(),
                 updatedNetwork.getName(), updatedNetwork.getNetworkOfferingId(), null, updatedNetwork.getState().name(), Network.class.getName(), updatedNetwork.getUuid(), true);
+        updateProviderNetwork(updatedNetwork, prevNetworkName);
         return updatedNetwork;
     }
 
+    private void updateProviderNetwork(Network network, String prevNetworkName) {
+        final NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName());
+        if (Objects.nonNull(guru) && !guru.update(network, prevNetworkName)) {
+            logger.error("Failed to update name of network on provider");
+        }
+    }
+
     protected Pair<Integer, Integer> validateMtuOnUpdate(NetworkVO network, Long zoneId, Integer publicMtu, Integer privateMtu) {
         if (!AllowUsersToSpecifyVRMtu.valueIn(zoneId)) {
             return new Pair<>(null, null);
diff --git a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index dd3707c6051..d2b63253d22 100644
--- a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -51,6 +51,11 @@ public class NetrisServiceMockTest implements NetrisService {
         return true;
     }
 
+    @Override
+    public boolean updateVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String prevNetworkName) {
+        return true;
+    }
+
     @Override
     public boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
         return true;


(cloudstack) 20/44: Support to pass provider when creating public ip range and create IPAM on Netris (#28)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 4bf4dafcb3e84d88afbf7358c732d11cfb4c6fc3
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Fri Dec 6 11:48:45 2024 -0500

    Support to pass provider when creating public ip range and create IPAM on Netris (#28)
    
    * UI: support to pass provider when creating public ip range
    
    * prevent adding public ip range for a provider that isnt supported in zone
    
    * Create public range on Netris when created on CloudStack
    
    ---------
    
    Co-authored-by: nvazquez <ni...@gmail.com>
---
 .../com/cloud/network/netris/NetrisService.java    |  1 +
 .../service/NetrisProviderServiceImpl.java         | 91 +-------------------
 .../cloudstack/service/NetrisServiceImpl.java      | 96 ++++++++++++++++++++++
 ...iceImplTest.java => NetrisServiceImplTest.java} |  4 +-
 .../configuration/ConfigurationManagerImpl.java    | 17 ++++
 ui/src/views/infra/network/IpRangesTabPublic.vue   | 11 +++
 6 files changed, 130 insertions(+), 90 deletions(-)

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


(cloudstack) 09/44: Pass VXLAN ID during creation of Netris vNets (#16)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit c8eb0f377929c005555a5f073e87868f25d7475b
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Thu Nov 7 10:21:09 2024 -0500

    Pass VXLAN ID during creation of Netris vNets (#16)
    
    * add zone params to accepts management vnet
    
    * Release vxlan associated to the netris broadcast domain type
    
    * handle update network broadcast uri
---
 api/src/main/java/com/cloud/event/EventTypes.java  |  2 ++
 .../cloudstack/service/NetrisApiClientImpl.java    |  8 ++++--
 .../cloudstack/service/NetrisGuestNetworkGuru.java | 32 ++++++++++++++++++++--
 .../cloudstack/service/NetrisServiceImpl.java      |  8 ++++++
 .../com/cloud/network/guru/GuestNetworkGuru.java   |  9 ++++--
 5 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java
index 5e5309965c1..6215afa4513 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -495,6 +495,8 @@ public class EventTypes {
 
     public static final String EVENT_ZONE_VLAN_ASSIGN = "ZONE.VLAN.ASSIGN";
     public static final String EVENT_ZONE_VLAN_RELEASE = "ZONE.VLAN.RELEASE";
+    public static final String EVENT_ZONE_VXLAN_ASSIGN = "ZONE.VXLAN.ASSIGN";
+    public static final String EVENT_ZONE_VXLAN_RELEASE = "ZONE.VXLAN.RELEASE";
 
     // Projects
     public static final String EVENT_PROJECT_CREATE = "PROJECT.CREATE";
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index b61c84e0110..a140f171f64 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -281,7 +281,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             List<VPCListing> vpcs = vpcListings.stream()
                     .filter(x -> x.getName().equals(vpcName) && x.getAdminTenant().getId().equals(tenantId))
                     .collect(Collectors.toList());
-            return vpcs.get(0);
+            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);
         }
@@ -319,6 +319,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         String networkName = cmd.getName();
         Long networkId = cmd.getId();
         String vnetCidr = cmd.getCidr();
+        Integer vxlanId = cmd.getVxlanId();
         boolean isVpc = cmd.isVpc();
 
         String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, networkName, isVpc);
@@ -341,7 +342,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
         logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
 
-        VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr);
+        VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr, vxlanId);
         if (vnetResponse == null || !vnetResponse.isIsSuccess()) {
             String reason = vnetResponse == null ? "Empty response" : "Operation failed on Netris";
             logger.debug("The Netris vNet creation {} failed: {}", vNetName, reason);
@@ -535,7 +536,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         }
     }
 
-    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String vNetCidr) {
+    VnetResAddBody createVnetInternal(VPCListing associatedVpc, String netrisVnetName, String vNetCidr, Integer vxlanId) {
         logger.debug("Creating Netris VPC vNet {} for CIDR {}", netrisVnetName, vNetCidr);
         try {
             VnetAddBody vnetBody = new VnetAddBody();
@@ -558,6 +559,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
             vnetBody.setL3vpn(false);
             vnetBody.setName(netrisVnetName);
             vnetBody.setNativeVlan(0);
+            vnetBody.setVxlanID(vxlanId);
             vnetBody.setPorts(new ArrayList<>());
 
             IpTreeSubnetSites subnetSites = new IpTreeSubnetSites();
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
index 95056c95460..2a4891eb28c 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisGuestNetworkGuru.java
@@ -20,6 +20,9 @@ import com.cloud.dc.DataCenter;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.domain.DomainVO;
+import com.cloud.event.ActionEventUtils;
+import com.cloud.event.EventTypes;
+import com.cloud.event.EventVO;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 import com.cloud.exception.InvalidParameterValueException;
@@ -41,6 +44,8 @@ import com.cloud.vm.NicProfile;
 import com.cloud.vm.ReservationContext;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
+import org.apache.cloudstack.api.ApiCommandResourceType;
+import org.apache.cloudstack.context.CallContext;
 
 import javax.inject.Inject;
 import java.util.Objects;
@@ -132,6 +137,12 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
             if (isNull(zone)) {
                 throw new CloudRuntimeException(String.format("Failed to find zone with id: %s", zoneId));
             }
+            try {
+                allocateVnet(network, designedNetwork, network.getDataCenterId(), network.getPhysicalNetworkId(), network.getReservationId());
+            } catch (Exception e) {
+                throw new CloudRuntimeException(String.format("Failed to allocate VXLAN for Netris guest Network %s", network.getName()));
+            }
+            _networkDao.update(designedNetwork.getId(), designedNetwork);
             createNetrisVnet(designedNetwork, zone);
         } catch (Exception ex) {
             throw new CloudRuntimeException("unable to create Netris network " + network.getUuid() + "due to: " + ex.getMessage());
@@ -146,7 +157,7 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
 
     @Override
     public Network implement(Network network, NetworkOffering offering, DeployDestination dest,
-                             ReservationContext context) {
+                             ReservationContext context) throws InsufficientVirtualNetworkCapacityException {
         NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(),
                 network.getBroadcastDomainType(), network.getNetworkOfferingId(), Network.State.Implemented,
                 network.getDataCenterId(), network.getPhysicalNetworkId(), offering.isRedundantRouter());
@@ -167,10 +178,27 @@ public class NetrisGuestNetworkGuru  extends GuestNetworkGuru implements Network
         if (network.getName() != null) {
             implemented.setName(network.getName());
         }
-        implemented.setBroadcastUri(Networks.BroadcastDomainType.Netris.toUri("netris"));
+        allocateVnet(network, implemented, network.getDataCenterId(), network.getPhysicalNetworkId(), network.getReservationId());
         return implemented;
     }
 
+    @Override
+    protected void allocateVnet(Network network, NetworkVO implemented, long dcId, long physicalNetworkId, String reservationId)
+            throws InsufficientVirtualNetworkCapacityException {
+        if (network.getBroadcastUri() == null) {
+            String vnet = _dcDao.allocateVnet(dcId, physicalNetworkId, network.getAccountId(), reservationId, UseSystemGuestVlans.valueIn(network.getAccountId()));
+            if (vnet == null) {
+                throw new InsufficientVirtualNetworkCapacityException("Unable to allocate vnet as a " + "part of network " + network + " implement ", DataCenter.class,
+                        dcId);
+            }
+            implemented.setBroadcastUri(Networks.BroadcastDomainType.Netris.toUri(vnet));
+            ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), network.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VXLAN_ASSIGN,
+                    "Assigned Zone vNet: " + vnet + " Network Id: " + implemented.getId(), implemented.getId(), ApiCommandResourceType.Network.toString(), 0);
+        } else {
+            implemented.setBroadcastUri(network.getBroadcastUri());
+        }
+    }
+
     @Override
     public NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
         NicProfile nicProfile = super.allocate(network, nic, vm);
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index eb2046718c2..90c80ddabf1 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -19,7 +19,10 @@ package org.apache.cloudstack.service;
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Networks;
 import com.cloud.network.dao.NetrisProviderDao;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.netris.NetrisService;
 import com.cloud.network.vpc.Vpc;
@@ -44,6 +47,8 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
     @Inject
     private NetrisProviderDao netrisProviderDao;
     @Inject
+    private NetworkDao networkDao;
+    @Inject
     private AgentManager agentMgr;
 
     @Override
@@ -91,7 +96,10 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
 
     @Override
     public boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr) {
+        NetworkVO network = networkDao.findById(networkId);
+        String vxlanId = Networks.BroadcastDomainType.getValue(network.getBroadcastUri());
         CreateNetrisVnetCommand cmd = new CreateNetrisVnetCommand(zoneId, accountId, domainId, vpcName, vpcId, networkName, networkId, cidr, !Objects.isNull(vpcId));
+        cmd.setVxlanId(Integer.parseInt(vxlanId));
         NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
         return answer.getResult();
     }
diff --git a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java
index 957c5b1287a..dfbb0e28fde 100644
--- a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java
+++ b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java
@@ -17,6 +17,7 @@
 package com.cloud.network.guru;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
 
@@ -523,11 +524,15 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur
             return; // Nothing to do here if the uri is null already
         }
 
-        if ((profile.getBroadcastDomainType() == BroadcastDomainType.Vlan || profile.getBroadcastDomainType() == BroadcastDomainType.Vxlan) && !offering.isSpecifyVlan()) {
+        if ((profile.getBroadcastDomainType() == BroadcastDomainType.Vlan || profile.getBroadcastDomainType() == BroadcastDomainType.Vxlan || profile.getBroadcastDomainType() == BroadcastDomainType.Netris) && !offering.isSpecifyVlan()) {
             logger.debug("Releasing vnet for the network id=" + profile.getId());
             _dcDao.releaseVnet(BroadcastDomainType.getValue(profile.getBroadcastUri()), profile.getDataCenterId(), profile.getPhysicalNetworkId(), profile.getAccountId(),
                     profile.getReservationId());
-            ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), profile.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_RELEASE,
+            String eventType = EventTypes.EVENT_ZONE_VLAN_RELEASE;
+            if (Arrays.asList(BroadcastDomainType.Vxlan, BroadcastDomainType.Netris).contains(profile.getBroadcastDomainType())) {
+                eventType = EventTypes.EVENT_ZONE_VXLAN_RELEASE;
+            }
+            ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), profile.getAccountId(), EventVO.LEVEL_INFO, eventType,
                     "Released Zone Vnet: " + BroadcastDomainType.getValue(profile.getBroadcastUri()) + " for Network: " + profile.getId(),
                     profile.getDataCenterId(), ApiCommandResourceType.Zone.toString(), 0);
         }


(cloudstack) 16/44: Merge pull request #25 from shapeblue/netris-420-backport

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit d24b6f229d69114527aa9d032957707f17964459
Merge: c087de4adfe a9982c4d4d2
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Tue Dec 3 15:50:04 2024 +0100

    Merge pull request #25 from shapeblue/netris-420-backport
    
    Update netris-release to the 4.20.0.0 release

 agent/pom.xml                                      |   2 +-
 api/pom.xml                                        |   2 +-
 .../main/java/com/cloud/user/AccountService.java   |   2 +
 .../command/user/backup/ListBackupScheduleCmd.java |   2 +-
 client/pom.xml                                     |   2 +-
 core/pom.xml                                       |   2 +-
 debian/changelog                                   |   6 +
 debian/control                                     |   2 +-
 developer/pom.xml                                  |   2 +-
 engine/api/pom.xml                                 |   2 +-
 engine/components-api/pom.xml                      |   2 +-
 engine/orchestration/pom.xml                       |   2 +-
 engine/pom.xml                                     |   2 +-
 engine/schema/pom.xml                              |   2 +-
 .../com/cloud/storage/dao/VolumeStatsDaoImpl.java  |   9 +-
 .../main/java/com/cloud/vm/dao/VmStatsDaoImpl.java |   9 +-
 engine/service/pom.xml                             |   2 +-
 engine/storage/cache/pom.xml                       |   2 +-
 engine/storage/configdrive/pom.xml                 |   2 +-
 engine/storage/datamotion/pom.xml                  |   2 +-
 .../motion/StorageSystemDataMotionStrategy.java    |  78 +++---
 engine/storage/image/pom.xml                       |   2 +-
 .../storage/image/TemplateServiceImpl.java         |   5 +
 engine/storage/integration-test/pom.xml            |   2 +-
 engine/storage/object/pom.xml                      |   2 +-
 engine/storage/pom.xml                             |   2 +-
 engine/storage/snapshot/pom.xml                    |   2 +-
 engine/storage/volume/pom.xml                      |   2 +-
 engine/userdata/cloud-init/pom.xml                 |   2 +-
 engine/userdata/pom.xml                            |   2 +-
 framework/agent-lb/pom.xml                         |   2 +-
 framework/ca/pom.xml                               |   2 +-
 framework/cluster/pom.xml                          |   2 +-
 framework/config/pom.xml                           |   2 +-
 framework/db/pom.xml                               |   2 +-
 .../main/java/com/cloud/utils/db/GenericDao.java   |   8 -
 .../java/com/cloud/utils/db/GenericDaoBase.java    |  48 ++--
 framework/direct-download/pom.xml                  |   2 +-
 framework/events/pom.xml                           |   2 +-
 framework/ipc/pom.xml                              |   2 +-
 framework/jobs/pom.xml                             |   2 +-
 framework/managed-context/pom.xml                  |   2 +-
 framework/pom.xml                                  |   2 +-
 framework/quota/pom.xml                            |   2 +-
 framework/rest/pom.xml                             |   2 +-
 framework/security/pom.xml                         |   2 +-
 framework/spring/lifecycle/pom.xml                 |   2 +-
 framework/spring/module/pom.xml                    |   2 +-
 packaging/el8/cloud.spec                           |   1 +
 plugins/acl/dynamic-role-based/pom.xml             |   2 +-
 plugins/acl/project-role-based/pom.xml             |   2 +-
 plugins/acl/static-role-based/pom.xml              |   2 +-
 .../explicit-dedication/pom.xml                    |   2 +-
 .../host-affinity/pom.xml                          |   2 +-
 .../host-anti-affinity/pom.xml                     |   2 +-
 .../non-strict-host-affinity/pom.xml               |   2 +-
 .../non-strict-host-anti-affinity/pom.xml          |   2 +-
 plugins/alert-handlers/snmp-alerts/pom.xml         |   2 +-
 plugins/alert-handlers/syslog-alerts/pom.xml       |   2 +-
 plugins/api/discovery/pom.xml                      |   2 +-
 plugins/api/rate-limit/pom.xml                     |   2 +-
 plugins/api/solidfire-intg-test/pom.xml            |   2 +-
 plugins/api/vmware-sioc/pom.xml                    |   2 +-
 plugins/backup/dummy/pom.xml                       |   2 +-
 plugins/backup/nas/pom.xml                         |   2 +-
 plugins/backup/networker/pom.xml                   |   2 +-
 plugins/backup/veeam/pom.xml                       |   2 +-
 plugins/ca/root-ca/pom.xml                         |   2 +-
 plugins/database/mysql-ha/pom.xml                  |   2 +-
 plugins/database/quota/pom.xml                     |   2 +-
 .../cloudstack/api/command/QuotaBalanceCmd.java    |  14 +-
 .../cloudstack/api/command/QuotaCreditsCmd.java    |   6 +
 .../cloudstack/api/command/QuotaStatementCmd.java  |   6 +
 plugins/dedicated-resources/pom.xml                |   2 +-
 .../implicit-dedication/pom.xml                    |   2 +-
 .../user-concentrated-pod/pom.xml                  |   2 +-
 .../deployment-planners/user-dispersing/pom.xml    |   2 +-
 plugins/drs/cluster/balanced/pom.xml               |   2 +-
 plugins/drs/cluster/condensed/pom.xml              |   2 +-
 plugins/event-bus/inmemory/pom.xml                 |   2 +-
 plugins/event-bus/kafka/pom.xml                    |   2 +-
 plugins/event-bus/rabbitmq/pom.xml                 |   2 +-
 plugins/event-bus/webhook/pom.xml                  |   2 +-
 plugins/ha-planners/skip-heurestics/pom.xml        |   2 +-
 plugins/host-allocators/random/pom.xml             |   2 +-
 plugins/hypervisors/baremetal/pom.xml              |   2 +-
 plugins/hypervisors/hyperv/pom.xml                 |   2 +-
 plugins/hypervisors/kvm/pom.xml                    |   2 +-
 .../kvm/resource/LibvirtComputingResource.java     |  19 +-
 .../kvm/resource/LibvirtStoragePoolDef.java        |   2 +-
 .../hypervisor/kvm/resource/MigrateKVMAsync.java   |   2 +
 .../LibvirtOvsFetchInterfaceCommandWrapper.java    |   2 +-
 .../kvm/storage/IscsiAdmStorageAdaptor.java        |   2 +-
 .../kvm/storage/KVMStoragePoolManager.java         |   2 +-
 .../kvm/storage/KVMStorageProcessor.java           |  31 ++-
 .../kvm/storage/LibvirtStorageAdaptor.java         |  58 ++++-
 .../kvm/storage/ManagedNfsStorageAdaptor.java      |   2 +-
 .../kvm/storage/MultipathSCSIAdapterBase.java      |   2 +-
 .../kvm/storage/ScaleIOStorageAdaptor.java         |   2 +-
 .../hypervisor/kvm/storage/StorageAdaptor.java     |   2 +-
 .../kvm/resource/LibvirtComputingResourceTest.java | 113 +++++++++
 .../kvm/storage/LibvirtStorageAdaptorTest.java     |   2 +-
 plugins/hypervisors/ovm/pom.xml                    |   2 +-
 plugins/hypervisors/ovm3/pom.xml                   |   2 +-
 plugins/hypervisors/simulator/pom.xml              |   2 +-
 plugins/hypervisors/ucs/pom.xml                    |   2 +-
 plugins/hypervisors/vmware/pom.xml                 |   2 +-
 plugins/hypervisors/xenserver/pom.xml              |   2 +-
 plugins/integrations/cloudian/pom.xml              |   2 +-
 plugins/integrations/kubernetes-service/pom.xml    |   2 +-
 plugins/integrations/prometheus/pom.xml            |   2 +-
 plugins/metrics/pom.xml                            |   2 +-
 plugins/network-elements/bigswitch/pom.xml         |   2 +-
 plugins/network-elements/brocade-vcs/pom.xml       |   2 +-
 plugins/network-elements/cisco-vnmc/pom.xml        |   2 +-
 plugins/network-elements/dns-notifier/pom.xml      |   2 +-
 .../network-elements/elastic-loadbalancer/pom.xml  |   2 +-
 plugins/network-elements/globodns/pom.xml          |   2 +-
 .../network-elements/internal-loadbalancer/pom.xml |   2 +-
 plugins/network-elements/juniper-contrail/pom.xml  |   2 +-
 .../contrail/management/MockAccountManager.java    |   5 +
 plugins/network-elements/netscaler/pom.xml         |   2 +-
 plugins/network-elements/nicira-nvp/pom.xml        |   2 +-
 plugins/network-elements/nsx/pom.xml               |   2 +-
 plugins/network-elements/opendaylight/pom.xml      |   2 +-
 plugins/network-elements/ovs/pom.xml               |   2 +-
 plugins/network-elements/palo-alto/pom.xml         |   2 +-
 plugins/network-elements/stratosphere-ssp/pom.xml  |   2 +-
 plugins/network-elements/tungsten/pom.xml          |   2 +-
 plugins/network-elements/vxlan/pom.xml             |   2 +-
 .../outofbandmanagement-drivers/ipmitool/pom.xml   |   2 +-
 .../nested-cloudstack/pom.xml                      |   2 +-
 .../outofbandmanagement-drivers/redfish/pom.xml    |   2 +-
 plugins/pom.xml                                    |   2 +-
 plugins/shutdown/pom.xml                           |   2 +-
 plugins/storage-allocators/random/pom.xml          |   2 +-
 plugins/storage/image/default/pom.xml              |   2 +-
 plugins/storage/image/s3/pom.xml                   |   2 +-
 plugins/storage/image/sample/pom.xml               |   2 +-
 plugins/storage/image/swift/pom.xml                |   2 +-
 plugins/storage/object/ceph/pom.xml                |   2 +-
 .../driver/CephObjectStoreDriverImpl.java          |  13 +-
 .../lifecycle/CephObjectStoreLifeCycleImpl.java    |  10 +-
 plugins/storage/object/minio/pom.xml               |   2 +-
 plugins/storage/object/simulator/pom.xml           |   2 +-
 plugins/storage/sharedfs/storagevm/pom.xml         |   2 +-
 plugins/storage/volume/adaptive/pom.xml            |   2 +-
 plugins/storage/volume/cloudbyte/pom.xml           |   2 +-
 plugins/storage/volume/datera/pom.xml              |   2 +-
 plugins/storage/volume/default/pom.xml             |   2 +-
 plugins/storage/volume/flasharray/pom.xml          |   2 +-
 plugins/storage/volume/linstor/CHANGELOG.md        |  13 +
 plugins/storage/volume/linstor/pom.xml             |   2 +-
 .../kvm/storage/LinstorStorageAdaptor.java         |  79 ++++--
 .../storage/datastore/util/LinstorUtil.java        |  24 ++
 plugins/storage/volume/nexenta/pom.xml             |   2 +-
 plugins/storage/volume/primera/pom.xml             |   2 +-
 plugins/storage/volume/sample/pom.xml              |   2 +-
 plugins/storage/volume/scaleio/pom.xml             |   2 +-
 plugins/storage/volume/solidfire/pom.xml           |   2 +-
 plugins/storage/volume/storpool/pom.xml            |   2 +-
 .../kvm/storage/StorPoolStorageAdaptor.java        |   2 +-
 .../storage/datastore/util/StorPoolUtil.java       |   4 +-
 .../storage/snapshot/StorPoolSnapshotStrategy.java |  58 +++--
 plugins/user-authenticators/ldap/pom.xml           |   2 +-
 plugins/user-authenticators/md5/pom.xml            |   2 +-
 plugins/user-authenticators/oauth2/pom.xml         |   2 +-
 plugins/user-authenticators/pbkdf2/pom.xml         |   2 +-
 plugins/user-authenticators/plain-text/pom.xml     |   2 +-
 plugins/user-authenticators/saml2/pom.xml          |   2 +-
 .../api/command/ListAndSwitchSAMLAccountCmd.java   |   7 +-
 .../apache/cloudstack/saml/SAML2AuthManager.java   |   6 +
 .../cloudstack/saml/SAML2AuthManagerImpl.java      |   3 +-
 .../java/org/apache/cloudstack/saml/SAMLUtils.java |  27 ++-
 .../command/ListAndSwitchSAMLAccountCmdTest.java   |  25 +-
 plugins/user-authenticators/sha256salted/pom.xml   |   2 +-
 .../static-pin/pom.xml                             |   2 +-
 .../user-two-factor-authenticators/totp/pom.xml    |   2 +-
 pom.xml                                            |   2 +-
 quickcloud/pom.xml                                 |   2 +-
 server/pom.xml                                     |   2 +-
 server/src/main/java/com/cloud/api/ApiServer.java  |  26 +-
 server/src/main/java/com/cloud/api/ApiServlet.java |  54 +++--
 .../com/cloud/api/dispatch/ParamProcessWorker.java |  42 ++--
 .../java/com/cloud/api/query/QueryManagerImpl.java |  26 +-
 .../java/com/cloud/user/AccountManagerImpl.java    |  14 ++
 .../cloud/api/dispatch/ParamProcessWorkerTest.java | 138 +++++++++--
 .../com/cloud/user/MockAccountManagerImpl.java     |   5 +
 .../java/com/cloud/user/MockUsageEventDao.java     |   5 -
 services/console-proxy/pom.xml                     |   2 +-
 services/console-proxy/rdpconsole/pom.xml          |   2 +-
 services/console-proxy/server/pom.xml              |   2 +-
 services/pom.xml                                   |   2 +-
 services/secondary-storage/controller/pom.xml      |   2 +-
 services/secondary-storage/pom.xml                 |   2 +-
 services/secondary-storage/server/pom.xml          |   2 +-
 .../storage/formatinspector/Qcow2HeaderField.java  |  51 ++++
 .../storage/formatinspector/Qcow2Inspector.java    | 268 +++++++++++++++++++++
 .../resource/NfsSecondaryStorageResource.java      |  14 +-
 .../storage/template/DownloadManagerImpl.java      |  39 ++-
 systemvm/debian/opt/cloud/bin/cs/CsFile.py         |   2 +-
 systemvm/pom.xml                                   |   2 +-
 test/integration/smoke/test_login.py               |   1 +
 test/pom.xml                                       |   2 +-
 tools/apidoc/pom.xml                               |   2 +-
 tools/build/build_asf.sh                           |  10 +-
 tools/checkstyle/pom.xml                           |   2 +-
 tools/devcloud-kvm/pom.xml                         |   2 +-
 tools/devcloud4/pom.xml                            |   2 +-
 tools/docker/Dockerfile                            |   2 +-
 tools/docker/Dockerfile.marvin                     |   4 +-
 tools/marvin/pom.xml                               |   2 +-
 tools/marvin/setup.py                              |   2 +-
 tools/pom.xml                                      |   2 +-
 ui/public/locales/pt_BR.json                       |   4 +-
 ui/src/api/index.js                                |  11 +-
 ui/src/components/view/DetailSettings.vue          |  13 +-
 ui/src/config/section/compute.js                   |   2 +-
 ui/src/store/modules/user.js                       |  10 +-
 ui/src/views/compute/KubernetesServiceTab.vue      |   2 +-
 ui/src/views/compute/StartVirtualMachine.vue       |  16 +-
 .../views/image/UpdateTemplateIsoPermissions.vue   |  18 +-
 ui/src/views/infra/UpdatePrimaryStorage.vue        |  10 +-
 ui/tests/unit/components/view/ActionButton.spec.js |  10 +
 ui/tests/unit/views/compute/MigrateWizard.spec.js  |  10 +
 ui/vue.config.js                                   |   6 +-
 usage/pom.xml                                      |   2 +-
 utils/pom.xml                                      |   2 +-
 utils/src/main/java/com/cloud/utils/HttpUtils.java |  42 +++-
 .../test/java/com/cloud/utils/HttpUtilsTest.java   |  86 ++++++-
 vmware-base/pom.xml                                |   2 +-
 231 files changed, 1527 insertions(+), 443 deletions(-)


(cloudstack) 37/44: Validate if given CIDR belongs to a bigger allocation in Netris before creating the zone-level allocation (#48)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 7f318032328cd827f287586d35fdc9cd93836d60
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Wed Feb 5 10:37:46 2025 -0500

    Validate if given CIDR belongs to a bigger allocation in Netris before creating the zone-level allocation (#48)
    
    * Validate if given CIDR belongs to a bigger allocation in Netris before creating
    
    * rename method
---
 .../org/apache/cloudstack/service/NetrisApiClientImpl.java   | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index 7f80947974d..e61482a3564 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -18,6 +18,8 @@ package org.apache.cloudstack.service;
 
 import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
+import inet.ipaddr.IPAddress;
+import inet.ipaddr.IPAddressString;
 import io.netris.ApiClient;
 import io.netris.ApiException;
 import io.netris.ApiResponse;
@@ -815,11 +817,19 @@ public class NetrisApiClientImpl implements NetrisApiClient {
         filterByVpc.add(vpc.getId());
         IpTree ipamTree = ipamApi.apiV2IpamGet(filterBySites, filterByVpc);
         List<IpTreeAllocation> superCidrList = ipamTree.getData().stream()
-                .filter(x -> x.getPrefix().equals(superCidrPrefix))
+                .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();


(cloudstack) 40/44: Fix the test failures noticed on #44 (#52)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit f5a96e77a5a8623c1030ff143ed9825a90876413
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Thu Feb 13 11:18:53 2025 -0500

    Fix the test failures noticed on #44 (#52)
---
 .../main/java/com/cloud/network/IpAddressManagerImpl.java  | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
index e52eeb84fbd..ac8c499e06f 100644
--- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
@@ -827,14 +827,12 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
                 providers = servicesMap.stream().map(VpcOfferingServiceMapVO::getProvider).collect(Collectors.toList());
             }
 
-            if (providers.isEmpty()) {
-                throw new InvalidParameterValueException("Unable to find the provider for this network");
-            }
-
-            String provider = providers.get(0);
-            NetworkElement element = _networkModel.getElementImplementingProvider(provider);
-            if (element != null) {
-                element.releaseIp(ipToBeDisassociated);
+            if (!providers.isEmpty()) {
+                String provider = providers.get(0);
+                NetworkElement element = _networkModel.getElementImplementingProvider(provider);
+                if (element != null) {
+                    element.releaseIp(ipToBeDisassociated);
+                }
             }
         } finally {
             _ipAddressDao.releaseFromLockTable(addrId);