Implement PCE RPC path-computation-reroute-request 22/102022/5
authorThierry Jiao <thierry.jiao@orange.com>
Tue, 9 Aug 2022 08:56:53 +0000 (10:56 +0200)
committerGilles Thouenon <gilles.thouenon@orange.com>
Fri, 16 Sep 2022 08:59:13 +0000 (10:59 +0200)
Before rerouting services, serviceListener will now call the RPC
path-computation-reroute-request to check that any other route exists.
If that's the case, the rerouting process will start.
Otherwise, the rerouting process is cancelled.

JIRA: TRNSPRTPCE-682
Signed-off-by: Thierry Jiao <thierry.jiao@orange.com>
Change-Id: I91c7a40ffdced804b059f523790f78245b81fe23

pce/src/main/java/org/opendaylight/transportpce/pce/PceComplianceCheck.java
pce/src/main/java/org/opendaylight/transportpce/pce/PceSendingPceRPCs.java
pce/src/main/java/org/opendaylight/transportpce/pce/impl/PceServiceRPCImpl.java
pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/PceCalculation.java
pce/src/main/java/org/opendaylight/transportpce/pce/networkanalyzer/PceOpticalNode.java
pce/src/main/java/org/opendaylight/transportpce/pce/service/PathComputationService.java
pce/src/main/java/org/opendaylight/transportpce/pce/service/PathComputationServiceImpl.java
servicehandler/src/main/java/org/opendaylight/transportpce/servicehandler/ModelMappingUtils.java
servicehandler/src/main/java/org/opendaylight/transportpce/servicehandler/listeners/ServiceListener.java
servicehandler/src/main/resources/OSGI-INF/blueprint/servicehandler-blueprint.xml
servicehandler/src/test/java/org/opendaylight/transportpce/servicehandler/listeners/ServiceListenerTest.java

index 4e76bf49ab34e4327d20ceee62a5b9f748de0df5..92585ecba08a0d190b277656020aff5502b353a5 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.transportpce.pce;
 
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestInput;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,4 +57,18 @@ public final class PceComplianceCheck {
         return new PceComplianceCheckResult(result, message);
     }
 
+    public static PceComplianceCheckResult check(PathComputationRerouteRequestInput input) {
+        if (input == null) {
+            return new PceComplianceCheckResult(false, "");
+        }
+        if (input.getEndpoints() == null
+                || input.getEndpoints().getAEndTp() == null
+                || input.getEndpoints().getZEndTp() == null) {
+            String message = "At least one of the termination points is missing";
+            LOG.debug(message);
+            return new PceComplianceCheckResult(false, message);
+        }
+        return new PceComplianceCheckResult(true, "");
+    }
+
 }
index 04a9e009fd1c8eb58cd62790770e40b24034eb01..284603edcf7de50ac64b3218296a72aaaa0f6eb9 100644 (file)
@@ -22,6 +22,7 @@ import org.opendaylight.transportpce.pce.networkanalyzer.PceCalculation;
 import org.opendaylight.transportpce.pce.networkanalyzer.PceResult;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInputBuilder;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.reroute.request.input.Endpoints;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.service.path.rpc.result.PathDescriptionBuilder;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.routing.constraints.rev211210.routing.constraints.HardConstraints;
 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.path.description.AToZDirection;
@@ -34,6 +35,7 @@ import org.slf4j.LoggerFactory;
  * Class for Sending
  * PCE requests :
  * - path-computation-request
+ * - path-computation-reroute
  * - cancel-resource-reserve.
  * @author Martial Coulibaly ( martial.coulibaly@gfi.com ) on behalf of Orange
  *
@@ -61,6 +63,8 @@ public class PceSendingPceRPCs {
     private String responseCode;
     private final GnpyConsumer gnpyConsumer;
     private PortMapping portMapping;
+    // Define the termination points whose reserved status is not taken into account during the pruning process
+    private Endpoints endpoints;
 
     public PceSendingPceRPCs(GnpyConsumer gnpyConsumer) {
         setPathDescription(null);
@@ -69,15 +73,26 @@ public class PceSendingPceRPCs {
         this.gnpyConsumer = gnpyConsumer;
     }
 
-    public PceSendingPceRPCs(PathComputationRequestInput input,
-        NetworkTransactionService networkTransaction, GnpyConsumer gnpyConsumer, PortMapping portMapping) {
+    public PceSendingPceRPCs(PathComputationRequestInput input, NetworkTransactionService networkTransaction,
+                             GnpyConsumer gnpyConsumer, PortMapping portMapping) {
         this.gnpyConsumer = gnpyConsumer;
         setPathDescription(null);
-
         // TODO compliance check to check that input is not empty
         this.input = input;
         this.networkTransaction = networkTransaction;
         this.portMapping = portMapping;
+        this.endpoints = null;
+    }
+
+    public PceSendingPceRPCs(PathComputationRequestInput input, NetworkTransactionService networkTransaction,
+                             GnpyConsumer gnpyConsumer, PortMapping portMapping,
+                             Endpoints endpoints) {
+        this.gnpyConsumer = gnpyConsumer;
+        setPathDescription(null);
+        this.input = input;
+        this.networkTransaction = networkTransaction;
+        this.portMapping = portMapping;
+        this.endpoints = endpoints;
     }
 
     public void cancelResourceReserve() {
@@ -95,8 +110,8 @@ public class PceSendingPceRPCs {
 
     public void pathComputationWithConstraints(PceConstraints hardConstraints, PceConstraints softConstraints) {
 
-        PceCalculation nwAnalizer =
-            new PceCalculation(input, networkTransaction, hardConstraints, softConstraints, rc, portMapping);
+        PceCalculation nwAnalizer = new PceCalculation(input, networkTransaction, hardConstraints, softConstraints, rc,
+                portMapping, endpoints);
         nwAnalizer.retrievePceNetwork();
         rc = nwAnalizer.getReturnStructure();
         String serviceType = nwAnalizer.getServiceType();
index af68f19ae9ac64dca35ddab3daa494c2752ef12d..13fd7684d842a0e27c2f85f5122c19eb31690fa7 100644 (file)
@@ -39,13 +39,15 @@ public class PceServiceRPCImpl implements TransportpcePceService {
     public ListenableFuture<RpcResult<CancelResourceReserveOutput>>
             cancelResourceReserve(CancelResourceReserveInput input) {
         LOG.info("RPC cancelResourceReserve request received");
-        CancelResourceReserveOutput output = null;
         try {
-            output = this.pathComputationService.cancelResourceReserve(input).get();
+            return RpcResultBuilder
+                    .success(
+                            this.pathComputationService.cancelResourceReserve(input).get())
+                    .buildFuture();
         } catch (InterruptedException | ExecutionException e) {
             LOG.error("RPC cancelResourceReserve failed !", e);
+            return RpcResultBuilder.success((CancelResourceReserveOutput) null).buildFuture();
         }
-        return RpcResultBuilder.success(output).buildFuture();
     }
 
     @Override
@@ -53,20 +55,30 @@ public class PceServiceRPCImpl implements TransportpcePceService {
             pathComputationRequest(PathComputationRequestInput input) {
         LOG.info("RPC path computation request received");
         LOG.debug("input parameters are : input = {}", input);
-        PathComputationRequestOutput output = null;
         try {
-            output = this.pathComputationService.pathComputationRequest(input).get();
+            return RpcResultBuilder
+                    .success(
+                            this.pathComputationService.pathComputationRequest(input).get())
+                    .buildFuture();
         } catch (InterruptedException | ExecutionException e) {
             LOG.error("RPC path computation request failed !", e);
         }
-        return RpcResultBuilder.success(output).buildFuture();
+        return RpcResultBuilder.success((PathComputationRequestOutput) null).buildFuture();
     }
 
     @Override
-    public ListenableFuture<RpcResult<PathComputationRerouteRequestOutput>>
-           pathComputationRerouteRequest(PathComputationRerouteRequestInput input) {
+    public ListenableFuture<RpcResult<PathComputationRerouteRequestOutput>> pathComputationRerouteRequest(
+            PathComputationRerouteRequestInput input) {
         LOG.info("RPC path computation reroute request received");
         LOG.debug("input parameters are : input = {}", input);
-        return null;
+        try {
+            return RpcResultBuilder
+                    .success(
+                            this.pathComputationService.pathComputationRerouteRequest(input).get())
+                    .buildFuture();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("RPC path computation request failed !", e);
+            return RpcResultBuilder.success((PathComputationRerouteRequestOutput) null).buildFuture();
+        }
     }
 }
index 22a68851ee5209125a694e39cfcd13478b748203..7652ea3bb01bce4e0ed9ec610b6c3d2ff90dfec7 100644 (file)
@@ -30,6 +30,7 @@ import org.opendaylight.transportpce.common.service.ServiceTypes;
 import org.opendaylight.transportpce.pce.PceComplianceCheck;
 import org.opendaylight.transportpce.pce.constraints.PceConstraints;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.reroute.request.input.Endpoints;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220316.mc.capabilities.McCapabilities;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.network.rev211210.Link1;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.network.rev211210.Node1;
@@ -86,6 +87,8 @@ public class PceCalculation {
     private List<LinkId> linksToExclude = new ArrayList<>();
     private PceResult returnStructure;
     private PortMapping portMapping;
+    // Define the termination points whose reserved status is not taken into account during the pruning process
+    private Endpoints endpoints;
 
     private enum ConstraintTypes {
         NONE, HARD_EXCLUDE, HARD_INCLUDE, HARD_DIVERSITY, SOFT_EXCLUDE, SOFT_INCLUDE, SOFT_DIVERSITY;
@@ -94,15 +97,28 @@ public class PceCalculation {
     private MappingUtils mappingUtils;
 
     public PceCalculation(PathComputationRequestInput input, NetworkTransactionService networkTransactionService,
-            PceConstraints pceHardConstraints, PceConstraints pceSoftConstraints, PceResult rc,
-            PortMapping portMapping) {
+                          PceConstraints pceHardConstraints, PceConstraints pceSoftConstraints, PceResult rc,
+                          PortMapping portMapping) {
         this.input = input;
         this.networkTransactionService = networkTransactionService;
         this.returnStructure = rc;
+        this.pceHardConstraints = pceHardConstraints;
+        this.mappingUtils = new MappingUtilsImpl(networkTransactionService.getDataBroker());
+        this.portMapping = portMapping;
+        this.endpoints = null;
+        parseInput();
+    }
 
+    public PceCalculation(PathComputationRequestInput input, NetworkTransactionService networkTransactionService,
+                          PceConstraints pceHardConstraints, PceConstraints pceSoftConstraints, PceResult rc,
+                          PortMapping portMapping, Endpoints endpoints) {
+        this.input = input;
+        this.networkTransactionService = networkTransactionService;
+        this.returnStructure = rc;
         this.pceHardConstraints = pceHardConstraints;
         this.mappingUtils = new MappingUtilsImpl(networkTransactionService.getDataBroker());
         this.portMapping = portMapping;
+        this.endpoints = endpoints;
         parseInput();
     }
 
@@ -450,6 +466,9 @@ public class PceCalculation {
         PceOpticalNode pceNode = new PceOpticalNode(deviceNodeId, this.serviceType, portMapping, node, nodeType,
             mappingUtils.getOpenRoadmVersion(deviceNodeId), getSlotWidthGranularity(deviceNodeId, node.getNodeId()),
             getCentralFreqGranularity(deviceNodeId, node.getNodeId()));
+        if (endpoints != null) {
+            pceNode.setEndpoints(endpoints);
+        }
         pceNode.validateAZxponder(anodeId, znodeId, input.getServiceAEnd().getServiceFormat());
         pceNode.initFrequenciesBitSet();
 
index 7fd8976c484a0b1cd9306476a16440ca6ce34ad2..0592bbcab8f5888a8a3b09d6b0dd0efc42243244 100644 (file)
@@ -20,6 +20,7 @@ import java.util.TreeMap;
 import org.opendaylight.transportpce.common.fixedflex.GridConstant;
 import org.opendaylight.transportpce.common.mapping.PortMapping;
 import org.opendaylight.transportpce.pce.SortPortsByName;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.reroute.request.input.Endpoints;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220316.mapping.Mapping;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.network.rev211210.TerminationPoint1;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.state.types.rev191129.State;
@@ -63,6 +64,7 @@ public class PceOpticalNode implements PceNode {
     private String version;
     private BigDecimal slotWidthGranularity;
     private BigDecimal centralFreqGranularity;
+    private Endpoints endpoints;
 
     public PceOpticalNode(String deviceNodeId, String serviceType, PortMapping portMapping, Node node,
         OpenroadmNodeType nodeType, String version, BigDecimal slotWidthGranularity,
@@ -276,13 +278,17 @@ public class PceOpticalNode implements PceNode {
                 LOG.warn("initXndrTps: XPONDER tp = {} is OOS/degraded", tp.getTpId().getValue());
                 continue;
             }
-            org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev211210.TerminationPoint1 nttp1 = tp
-                .augmentation(org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev211210
-                .TerminationPoint1.class);
-            if (nttp1 != null && nttp1.getXpdrNetworkAttributes().getWavelength() != null) {
-                this.usedXpndrNWTps.add(tp.getTpId().getValue());
-                LOG.debug("initXndrTps: XPONDER tp = {} is used", tp.getTpId().getValue());
-                continue;
+            if (endpoints == null
+                    || (!endpoints.getAEndTp().equals(tp.getTpId().getValue())
+                        && !endpoints.getZEndTp().equals(tp.getTpId().getValue()))) {
+                org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev211210.TerminationPoint1 nttp1 =
+                        tp.augmentation(org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev211210
+                                .TerminationPoint1.class);
+                if (nttp1 != null && nttp1.getXpdrNetworkAttributes().getWavelength() != null) {
+                    this.usedXpndrNWTps.add(tp.getTpId().getValue());
+                    LOG.debug("initXndrTps: XPONDER tp = {} is used", tp.getTpId().getValue());
+                    continue;
+                }
             }
             // find Client of this network TP
             if (cntp1.getAssociatedConnectionMapTp() != null) {
@@ -483,4 +489,8 @@ public class PceOpticalNode implements PceNode {
         return centralFreqGranularity;
     }
 
+    public void setEndpoints(Endpoints endpoints) {
+        this.endpoints = endpoints;
+    }
+
 }
index e241dbf844b9e37e4059c8419ba3ae6eec7db251..cb6a4210703deb4c4d0f5b6fe1b226f5d366ef05 100644 (file)
@@ -12,6 +12,8 @@ import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev22
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.CancelResourceReserveOutput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestOutput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestInput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestOutput;
 
 /**
  * Path Computation Service.
@@ -34,4 +36,13 @@ public interface PathComputationService {
      */
     ListenableFuture<PathComputationRequestOutput> pathComputationRequest(PathComputationRequestInput input);
 
+    /**
+     * Requests a path computation in order to reroute a service.
+     *
+     * @param input PathComputationRerouteRequestInput data
+     * @return output PathComputationRerouteRequestOutput data
+     */
+    ListenableFuture<PathComputationRerouteRequestOutput> pathComputationRerouteRequest(
+            PathComputationRerouteRequestInput input);
+
 }
index a97934b04f9e4a26f57348b195c56a2563d0a7d2..3c521336384ca96f9f3137d6e653a2c0fbc121ea 100644 (file)
@@ -29,14 +29,20 @@ import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev22
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.CancelResourceReserveOutput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.CancelResourceReserveOutputBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInputBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestOutput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestOutputBuilder;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestInput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestOutput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestOutputBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.ServicePathRpcResult;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.ServicePathRpcResultBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.gnpy.GnpyResponse;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.gnpy.GnpyResponseBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.gnpy.gnpy.response.response.type.NoPathCaseBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.gnpy.gnpy.response.response.type.PathCaseBuilder;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.request.input.ServiceAEndBuilder;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.request.input.ServiceZEndBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.performance.PathPropertiesBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.performance.path.properties.PathMetric;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.performance.path.properties.PathMetricBuilder;
@@ -48,6 +54,7 @@ import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdes
 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.RpcStatusEx;
 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.ServicePathNotificationTypes;
 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.response.parameters.sp.ResponseParametersBuilder;
+import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.service.handler.header.ServiceHandlerHeaderBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -272,6 +279,52 @@ public class PathComputationServiceImpl implements PathComputationService {
         });
     }
 
+    @Override
+    public ListenableFuture<PathComputationRerouteRequestOutput> pathComputationRerouteRequest(
+            PathComputationRerouteRequestInput input) {
+        return executor.submit(() -> {
+            PathComputationRerouteRequestOutputBuilder output = new PathComputationRerouteRequestOutputBuilder();
+            ConfigurationResponseCommonBuilder configurationResponseCommon = new ConfigurationResponseCommonBuilder()
+                    .setRequestId("none");
+            PceComplianceCheckResult check = PceComplianceCheck.check(input);
+            if (!check.hasPassed()) {
+                LOG.error("Path not calculated, path computation reroute request not compliant : {}",
+                        check.getMessage());
+                configurationResponseCommon
+                        .setAckFinalIndicator("Yes")
+                        .setResponseCode("Path not calculated")
+                        .setResponseMessage(check.getMessage());
+                return output
+                        .setConfigurationResponseCommon(configurationResponseCommon.build())
+                        .build();
+            }
+            PathComputationRequestInput pathComputationInput = new PathComputationRequestInputBuilder()
+                    .setServiceName("no_name")
+                    .setServiceHandlerHeader(new ServiceHandlerHeaderBuilder().setRequestId("none").build())
+                    .setServiceAEnd(new ServiceAEndBuilder(input.getServiceAEnd()).build())
+                    .setServiceZEnd(new ServiceZEndBuilder(input.getServiceZEnd()).build())
+                    .setHardConstraints(input.getHardConstraints())
+                    .setPceRoutingMetric(input.getPceRoutingMetric())
+                    .setResourceReserve(false)
+                    .setSoftConstraints(input.getSoftConstraints())
+                    .setRoutingMetric(input.getRoutingMetric())
+                    .build();
+            PceSendingPceRPCs sendingPCE = new PceSendingPceRPCs(pathComputationInput, networkTransactionService,
+                    gnpyConsumer, portMapping, input.getEndpoints());
+            sendingPCE.pathComputation();
+            String message = sendingPCE.getMessage();
+            String responseCode = sendingPCE.getResponseCode();
+            LOG.info("PCE response: {} {}", message, responseCode);
+            return output.setConfigurationResponseCommon(
+                    configurationResponseCommon
+                            .setAckFinalIndicator("Yes")
+                            .setResponseCode(responseCode)
+                            .setResponseMessage(message)
+                            .build())
+                    .build();
+        });
+    }
+
     public GnpyResponse generateGnpyResponse(Response responseGnpy, String pathDir) {
         if (responseGnpy == null) {
             return new GnpyResponseBuilder()
index 20203edb24d1d974f2252d61dbc4cc7ee960b0d1..cd8ca876edd8c31dea19c8df008a3738a0824635 100644 (file)
@@ -259,6 +259,19 @@ public final class ModelMappingUtils {
             .build();
     }
 
+    public static org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808
+            .path.computation.reroute.request.input.ServiceAEnd createServiceAEndReroute(ServiceEndpoint serviceAEnd) {
+        return new org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808
+                .path.computation.reroute.request.input.ServiceAEndBuilder()
+                .setClli(serviceAEnd.getClli())
+                .setNodeId(serviceAEnd.getNodeId().getValue())
+                .setRxDirection(createRxDirection(serviceAEnd.getRxDirection().values().stream().findFirst().get()))
+                .setServiceFormat(serviceAEnd.getServiceFormat())
+                .setServiceRate(serviceAEnd.getServiceRate())
+                .setTxDirection(createTxDirection(serviceAEnd.getTxDirection().values().stream().findFirst().get()))
+                .build();
+    }
+
     public static ServiceZEnd createServiceZEnd(ServiceEndpoint serviceZEnd) {
         return new ServiceZEndBuilder()
             .setClli(serviceZEnd.getClli())
@@ -270,6 +283,19 @@ public final class ModelMappingUtils {
             .build();
     }
 
+    public static org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808
+            .path.computation.reroute.request.input.ServiceZEnd createServiceZEndReroute(ServiceEndpoint serviceZEnd) {
+        return new org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808
+                .path.computation.reroute.request.input.ServiceZEndBuilder()
+                .setClli(serviceZEnd.getClli())
+                .setNodeId(serviceZEnd.getNodeId().getValue())
+                .setRxDirection(createRxDirection(serviceZEnd.getRxDirection().values().stream().findFirst().get()))
+                .setServiceFormat(serviceZEnd.getServiceFormat())
+                .setServiceRate(serviceZEnd.getServiceRate())
+                .setTxDirection(createTxDirection(serviceZEnd.getTxDirection().values().stream().findFirst().get()))
+                .build();
+    }
+
     public static RxDirection createRxDirection(
             org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210
                 .service.endpoint.RxDirection rxDirection) {
index ed6a9a6be3bb1bc02d16cfde87ddf2cb25108b3a..434aa2cb31be964e6e9dcba2f5d47ad352bafbec 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.transportpce.servicehandler.listeners;
 
+import static org.opendaylight.transportpce.servicehandler.ModelMappingUtils.createServiceAEndReroute;
+import static org.opendaylight.transportpce.servicehandler.ModelMappingUtils.createServiceZEndReroute;
+
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.util.Collection;
@@ -22,9 +25,14 @@ import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
 import org.opendaylight.mdsal.binding.api.DataTreeModification;
 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
 import org.opendaylight.transportpce.common.ResponseCodes;
+import org.opendaylight.transportpce.pce.service.PathComputationService;
 import org.opendaylight.transportpce.servicehandler.ServiceInput;
 import org.opendaylight.transportpce.servicehandler.impl.ServicehandlerImpl;
 import org.opendaylight.transportpce.servicehandler.service.ServiceDataStoreOperations;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestInput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestInputBuilder;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestOutput;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.reroute.request.input.EndpointsBuilder;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.Restorable;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.RpcActions;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.sdnc.request.header.SdncRequestHeaderBuilder;
@@ -39,6 +47,11 @@ import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service
 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.delete.input.ServiceDeleteReqInfo;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.delete.input.ServiceDeleteReqInfoBuilder;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.list.Services;
+import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.path.description.atoz.direction.AToZ;
+import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.path.description.atoz.direction.AToZKey;
+import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.pce.resource.resource.resource.TerminationPoint;
+import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.PceMetric;
+import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePaths;
 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishNotificationAlarmService;
 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishNotificationAlarmServiceBuilder;
 import org.opendaylight.yangtools.yang.common.RpcResult;
@@ -52,14 +65,17 @@ public class ServiceListener implements DataTreeChangeListener<Services> {
     private ServicehandlerImpl servicehandlerImpl;
     private ServiceDataStoreOperations serviceDataStoreOperations;
     private NotificationPublishService notificationPublishService;
+    private PathComputationService pathComputationService;
     private Map<String, ServiceInput> mapServiceInputReroute;
     private final ScheduledExecutorService executor;
 
     public ServiceListener(ServicehandlerImpl servicehandlerImpl, ServiceDataStoreOperations serviceDataStoreOperations,
-                           NotificationPublishService notificationPublishService) {
+                           NotificationPublishService notificationPublishService,
+                           PathComputationService pathComputationService) {
         this.servicehandlerImpl = servicehandlerImpl;
         this.notificationPublishService = notificationPublishService;
         this.serviceDataStoreOperations = serviceDataStoreOperations;
+        this.pathComputationService = pathComputationService;
         this.executor = MoreExecutors.getExitingScheduledExecutorService(new ScheduledThreadPoolExecutor(4));
         mapServiceInputReroute = new HashMap<>();
     }
@@ -98,7 +114,11 @@ public class ServiceListener implements DataTreeChangeListener<Services> {
                                 && inputAfter.getServiceResiliency().getResiliency() != null
                                 && inputAfter.getServiceResiliency().getResiliency().equals(Restorable.VALUE)) {
                             LOG.info("Attempting to reroute the service '{}'...", serviceInputName);
-                            // It is used for hold off time purposes
+                            if (!serviceRerouteCheck(inputBefore)) {
+                                LOG.info("No other path available, cancelling reroute process of service '{}'...",
+                                        serviceInputName);
+                                continue;
+                            }
                             mapServiceInputReroute.put(serviceInputName, null);
                             if (inputAfter.getServiceResiliency().getHoldoffTime() != null) {
                                 LOG.info("Waiting hold off time before rerouting...");
@@ -108,7 +128,8 @@ public class ServiceListener implements DataTreeChangeListener<Services> {
                                                     && mapServiceInputReroute.get(serviceInputName) == null) {
                                                 serviceRerouteStep1(serviceInputName);
                                             } else {
-                                                LOG.info("Cancelling rerouting for service '{}'...", serviceInputName);
+                                                LOG.info("Cancelling reroute process of service '{}'...",
+                                                        serviceInputName);
                                             }
                                         },
                                         Long.parseLong(String.valueOf(inputAfter.getServiceResiliency()
@@ -215,6 +236,44 @@ public class ServiceListener implements DataTreeChangeListener<Services> {
         mapServiceInputReroute.remove(serviceNameToReroute);
     }
 
+    /**
+     * Call the PCE RPC path-computation-reroute-request to check if any other path exists.
+     *
+     * @param input Service to be rerouted
+     */
+    protected boolean serviceRerouteCheck(Services input) {
+        Optional<ServicePaths> servicePaths = serviceDataStoreOperations.getServicePath(input.getServiceName());
+        if (servicePaths.isEmpty()) {
+            LOG.warn("Service path of '{}' does not exist in datastore", input.getServiceName());
+            return false;
+        }
+        // Get the network xpdr termination points
+        Map<AToZKey, AToZ> mapaToz = servicePaths.get().getPathDescription().getAToZDirection().getAToZ();
+        String aendtp = ((TerminationPoint) mapaToz.get(new AToZKey(String.valueOf(mapaToz.size() - 3)))
+                .getResource().getResource()).getTpId();
+        String zendtp = ((TerminationPoint) mapaToz.get(new AToZKey("2")).getResource()
+                .getResource()).getTpId();
+        PathComputationRerouteRequestInput inputPC = new PathComputationRerouteRequestInputBuilder()
+                .setHardConstraints(input.getHardConstraints())
+                .setSoftConstraints(input.getSoftConstraints())
+                .setServiceAEnd(createServiceAEndReroute(input.getServiceAEnd()))
+                .setServiceZEnd(createServiceZEndReroute(input.getServiceZEnd()))
+                .setPceRoutingMetric(PceMetric.TEMetric)
+                .setEndpoints(new EndpointsBuilder()
+                        .setAEndTp(aendtp)
+                        .setZEndTp(zendtp)
+                        .build())
+                .build();
+        ListenableFuture<PathComputationRerouteRequestOutput> res =
+                pathComputationService.pathComputationRerouteRequest(inputPC);
+        try {
+            return res.get().getConfigurationResponseCommon().getResponseCode().equals(ResponseCodes.RESPONSE_OK);
+        } catch (ExecutionException | InterruptedException e) {
+            LOG.warn("ServiceRerouteCheck FAILED ! ", e);
+            return false;
+        }
+    }
+
     /**
      * Send notification to NBI notification in order to publish message.
      *
index affeddf0dd85cd0e7f949feb9c499fb93a02e69c..a84205cbcf0614cd32b2c2b27bf978cc0ea209aa 100644 (file)
@@ -47,6 +47,7 @@ Author: Martial Coulibaly <martial.coulibaly@gfi.com> on behalf of Orange
         <argument ref="serviceHandlerImpl" />
         <argument ref="serviceDatastoreOperation" />
         <argument ref="notificationPublishService" />
+        <argument ref="pathComputationService" />
     </bean>
 
     <bean id="rendererListener" class="org.opendaylight.transportpce.servicehandler.listeners.RendererListenerImpl">
index f94d94b3e770187e95231cb537b89452769c9254..d6163b21b696895e7e71310ee22ded2dd5cd8647 100755 (executable)
@@ -24,11 +24,13 @@ import java.util.Optional;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.mdsal.binding.api.DataObjectModification;
 import org.opendaylight.mdsal.binding.api.DataTreeModification;
 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
 import org.opendaylight.transportpce.common.ResponseCodes;
+import org.opendaylight.transportpce.pce.service.PathComputationService;
 import org.opendaylight.transportpce.servicehandler.impl.ServicehandlerImpl;
 import org.opendaylight.transportpce.servicehandler.service.ServiceDataStoreOperations;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.node.types.rev210528.NodeIdType;
@@ -72,6 +74,8 @@ public class ServiceListenerTest {
     private ServiceDataStoreOperations serviceDataStoreOperations;
     @Mock
     private NotificationPublishService notificationPublishService;
+    @Mock
+    private PathComputationService pathComputationService;
 
     @Test
     public void testOnDataTreeChangedWhenDeleteService() {
@@ -85,7 +89,7 @@ public class ServiceListenerTest {
         when(service.getModificationType()).thenReturn(DataObjectModification.ModificationType.DELETE);
         when(service.getDataBefore()).thenReturn(buildService(State.InService, AdminStates.InService));
         ServiceListener listener = new ServiceListener(servicehandler, serviceDataStoreOperations,
-                notificationPublishService);
+                notificationPublishService, pathComputationService);
         listener.onDataTreeChanged(changes);
         verify(ch, times(1)).getRootNode();
         verify(service, times(1)).getModificationType();
@@ -112,7 +116,7 @@ public class ServiceListenerTest {
         when(service.getDataBefore()).thenReturn(buildService(State.InService, AdminStates.InService));
         when(service.getDataAfter()).thenReturn(serviceDown);
         ServiceListener listener = new ServiceListener(servicehandler, serviceDataStoreOperations,
-                notificationPublishService);
+                notificationPublishService, pathComputationService);
         listener.onDataTreeChanged(changes);
         verify(ch, times(1)).getRootNode();
         verify(service, times(1)).getModificationType();
@@ -138,7 +142,7 @@ public class ServiceListenerTest {
         when(service.getModificationType()).thenReturn(DataObjectModification.ModificationType.SUBTREE_MODIFIED);
         when(service.getDataBefore()).thenReturn(buildService(State.InService, AdminStates.InService));
         ServiceListener listener = new ServiceListener(servicehandler, serviceDataStoreOperations,
-                notificationPublishService);
+                notificationPublishService, pathComputationService);
         listener.onDataTreeChanged(changes);
         verify(ch, times(1)).getRootNode();
         verify(service, times(2)).getModificationType();
@@ -189,8 +193,10 @@ public class ServiceListenerTest {
                                                 .build())
                                 .build())
                         .buildFuture());
-        ServiceListener listener = new ServiceListener(servicehandler, serviceDataStoreOperations,
-                notificationPublishService);
+        ServiceListener listener = Mockito.spy(new ServiceListener(servicehandler, serviceDataStoreOperations,
+                notificationPublishService, pathComputationService));
+        Mockito.doReturn(true).when(listener).serviceRerouteCheck(any());
+
         listener.onDataTreeChanged(changes);
         verify(ch, times(1)).getRootNode();
         verify(service, times(1)).getModificationType();
@@ -217,7 +223,7 @@ public class ServiceListenerTest {
         when(service.getDataBefore()).thenReturn(buildService(State.InService, AdminStates.InService));
         when(service.getDataAfter()).thenReturn(serviceAfter);
         ServiceListener listener = new ServiceListener(servicehandler, serviceDataStoreOperations,
-                notificationPublishService);
+                notificationPublishService, pathComputationService);
         listener.onDataTreeChanged(changes);
         verify(ch, times(1)).getRootNode();
         verify(service, times(1)).getModificationType();