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

pearl11594 pushed a commit to branch netris-integration-upstream
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 465965a6daa4da88ec5ee598e5df7180586337ba
Author: Pearl Dsilva <pearl1...@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 <weiz...@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;
+    }
 }

Reply via email to