Path Computation & Server Improvement 23/88223/5
authorOlivier Dugeon <olivier.dugeon@orange.com>
Mon, 2 Mar 2020 18:28:35 +0000 (19:28 +0100)
committerRobert Varga <nite@hq.sk>
Fri, 6 Mar 2020 13:03:17 +0000 (13:03 +0000)
 - Correct a bug in SAMCRA algorithm when delay is not specified
 - Improve debug message for SAMCRA algorithm
 - Add documentation for Path Computation Algorithm bundle
 - Update documentation for PCEP with the Path Computation Server

JIRA: BGPCEP-897
JIRA: BGPCEP-896

Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
Change-Id: I3243c6c1f52f3960c01c64069a075c76ed2fa829

12 files changed:
algo/algo-impl/src/main/java/org/opendaylight/algo/impl/CspfPath.java
algo/algo-impl/src/main/java/org/opendaylight/algo/impl/Samcra.java
docs/algo/algo-user-guide-algo-model.rst [new file with mode: 0644]
docs/algo/algo-user-guide-running-algo.rst [new file with mode: 0644]
docs/algo/index.rst [new file with mode: 0644]
docs/graph/graph-user-guide-graph-model.rst
docs/graph/graph-user-guide-manage-graph.rst
docs/graph/graph-user-guide-running-graph.rst
docs/index.rst
docs/pcep/index.rst
docs/pcep/pcep-user-guide-path-computation.rst [new file with mode: 0644]
docs/pcep/pcep-user-guide-troubleshooting.rst

index c3b562d8dac5e6599cdb4191980aa372767cdd24..8d9d7ea921dcbf4f382544d970fce3aa39e11166 100644 (file)
@@ -36,7 +36,8 @@ public class CspfPath implements Comparable<CspfPath> {
     /* Path Length and associated cost and delay */
     private float pathLength = 0;
     private int cost = Integer.MAX_VALUE;
-    private int delay = Integer.MAX_VALUE;
+    /* Uint24 Max value */
+    private int delay = 16777215;
 
     /* Path as Connected Edge list from the source up to the Connected Vertex */
     private ArrayList<ConnectedEdge> currentPath = new ArrayList<ConnectedEdge>();
@@ -177,4 +178,15 @@ public class CspfPath implements Comparable<CspfPath> {
         return this.cvertex.getKey().hashCode();
     }
 
+    @Override
+    public String toString() {
+        String output = "_path={";
+        for (ConnectedEdge edge : currentPath) {
+            if ((edge.getEdge() != null) && (edge.getEdge().getEdgeAttributes() != null)) {
+                output = output + edge.getEdge().getEdgeAttributes().getRemoteAddress().toString() + ", ";
+            }
+        }
+        return output + "}";
+    }
+
 }
index dd92cb368a85c198a23595a3e7bd9d99b56ba5aa..2aa124f66c8d19d57db11073e76da7fd41b6ddf0 100644 (file)
@@ -101,7 +101,8 @@ public class Samcra extends AbstractPathComputation {
 
     /* TE Metric cost and Delay cost for the current selected Path */
     int teCost = Integer.MAX_VALUE;
-    int delayCost = Integer.MAX_VALUE;
+    /* Uint24 Max value */
+    int delayCost = 16777215;
 
     public Samcra(ConnectedGraph graph) {
         super(graph);
@@ -156,15 +157,15 @@ public class Samcra extends AbstractPathComputation {
          */
         while (priorityQueue.size() != 0) {
             currentPath = priorityQueue.poll();
-            LOG.debug("Process path to Vertex {} from Priority Queue", currentPath.getVertex().toString());
+            LOG.debug(" - Process path up to Vertex {} from Priority Queue", currentPath.getVertex().toString());
 
             /* Prepare Samcra Path from current CSP Path except for the source */
             if (!(currentPath.equals(pathSource))) {
                 SamcraPath currentSamcraPath = samcraPaths.get(currentPath.getVertexKey());
                 CspfPath currentCspfPath = currentSamcraPath.getCurrentPath();
                 float queuePathLength = currentCspfPath.getPathLength();
-                LOG.trace("Samcra: priority Queue output SamcraPaths: {} CurrentPath: {} PathLength: {}",
-                        currentSamcraPath.currentPath.getPath(), currentCspfPath.getPath(), queuePathLength);
+                LOG.trace(" - Priority Queue output SamcraPaths {} CurrentPath {} with PathLength {}",
+                        currentSamcraPath.currentPath.toString(), currentCspfPath.toString(), queuePathLength);
             }
 
             edges = currentPath.getVertex().getOutputConnectedEdges();
@@ -180,7 +181,7 @@ public class Samcra extends AbstractPathComputation {
                  * the minimal path length) info obtained from the destination vertex
                  */
                 if (pruneEdge(edge, currentPath)) {
-                    LOG.trace("  Prune Edge {}", edge.toString());
+                    LOG.trace(" - Prune Edge {}", edge.toString());
                     continue;
                 }
                 float pathLength = relaxSamcra(edge, currentPath, pathSource);
@@ -189,10 +190,10 @@ public class Samcra extends AbstractPathComputation {
                 if ((pathLength > 0F) && (pathLength <= currentPathLength)) {
                     final SamcraPath finalPath = samcraPaths.get(pathDestination.getVertexKey());
                     cpathBuilder.setPathDescription(getPathDescription(finalPath.getCurrentPath().getPath()))
-                            .setMetric(Uint32.valueOf(pathDestination.getCost()))
-                            .setDelay(new Delay(Uint32.valueOf(pathDestination.getDelay())))
+                            .setMetric(Uint32.valueOf(finalPath.getCurrentPath().getCost()))
+                            .setDelay(new Delay(Uint32.valueOf(finalPath.getCurrentPath().getDelay())))
                             .setStatus(ComputationStatus.Active);
-                    LOG.debug("Samcra: path to destination found and registered: {}",
+                    LOG.debug(" - Path to destination found and registered {}",
                             cpathBuilder.getPathDescription());
                     currentPathLength = pathLength;
                 }
@@ -208,8 +209,8 @@ public class Samcra extends AbstractPathComputation {
             CspfPath selectedPath = null;
 
             if (!(currentPath.equals(pathSource))) {
-                LOG.debug("Samcra: priority queue output processing for connected vertex:  {}",
-                        currentPath.getVertexKey());
+                LOG.debug(" - Processing current path {} up to {} from Priority Queue", currentPath.toString(),
+                        currentPath.getVertex().toString());
                 SamcraPath currentSamcraPath = samcraPaths.get(currentPath.getVertexKey());
                 currentSamcraPath.decrementPathCount();
                 /*
@@ -219,8 +220,8 @@ public class Samcra extends AbstractPathComputation {
                  * If it is the case the shortest length is used to re-inject the connected vertex in the Priority Queue
                  */
                 for (CspfPath testedPath : currentSamcraPath.getPathList()) {
-                    LOG.debug("Samcra: priority queue output: testedPath: {} status: {} ", testedPath,
-                            testedPath.getPathStatus());
+                    LOG.debug(" - Testing path {} with status {} ",
+                            testedPath.toString(), testedPath.getPathStatus());
                     if (testedPath.getPathStatus() == CspfPath.SELECTED) {
                         testedPath.setPathStatus(CspfPath.PROCESSED);
                     } else if ((testedPath.getPathStatus() == CspfPath.ACTIVE)
@@ -236,8 +237,8 @@ public class Samcra extends AbstractPathComputation {
                     selectedPath.setPathStatus(CspfPath.SELECTED);
                     currentSamcraPath.setCurrentPath(selectedPath);
                     priorityQueue.add(selectedPath);
-                    LOG.debug("Samcra priority queue output: add path to the priority queue: {} path count: {} ",
-                            selectedPath.getPath(), currentSamcraPath.getPathCount());
+                    LOG.debug(" - Add path {} to Priority Queue. New path count {} ",
+                            selectedPath.toString(), currentSamcraPath.getPathCount());
                 } else {
                     currentSamcraPath.setCurrentPath(null);
                 }
@@ -264,7 +265,7 @@ public class Samcra extends AbstractPathComputation {
      * If the connected vertex has not already been processed, the corresponding CspfPath object is created.
      */
     private float relaxSamcra(ConnectedEdge edge, CspfPath currentPath, CspfPath source) {
-        LOG.debug("    Start SAMCRA relaxing Edge {} to Vertex {}", edge.toString(), edge.getDestination().toString());
+        LOG.debug("   - Start SAMCRA relaxing Edge {} to Vertex {}", edge.toString(), edge.getDestination().toString());
 
         /* Process CspfPath including the next Vertex */
         CspfPath nextVertexPath = processedPath.get(edge.getDestination().getKey());
@@ -273,8 +274,8 @@ public class Samcra extends AbstractPathComputation {
             processedPath.put(nextVertexPath.getVertexKey(), nextVertexPath);
             SamcraPath nextSamcraPath = new SamcraPath(edge.getDestination());
             samcraPaths.put(nextVertexPath.getVertexKey(), nextSamcraPath);
-            LOG.debug("relaxSamcra: next connected vertex does not exists, create it: {} with new Samcra Path: {}",
-                    nextVertexPath.toString(), nextSamcraPath);
+            LOG.debug("     - Next connected vertex {} does not exist, create it with new Samcra Path {}",
+                    nextSamcraPath.getVertex().toString(), nextVertexPath.toString());
         }
 
         /* Connected Vertex's paths management using SamcraPath object.
@@ -282,13 +283,13 @@ public class Samcra extends AbstractPathComputation {
          */
         Long predecessorId = 0L;
         if (!(currentPath.equals(source))) {
-            LOG.debug("relaxSamcra: check predecessor");
+            LOG.debug("     - Check predecessor");
             SamcraPath currentSamcraPath = samcraPaths.get(currentPath.getVertexKey());
             CspfPath currentVertexPath = currentSamcraPath.getCurrentPath();
             predecessorId = currentVertexPath.getPredecessor();
         }
         if (predecessorId.equals(nextVertexPath.getVertexKey())) {
-            LOG.debug("relaxSamcra: Skip Edge because next vertex: {} is predecessor of: {}",
+            LOG.debug("     - Skip Edge because next vertex {} is predecessor of {}",
                     nextVertexPath.getVertexKey(), predecessorId);
             return 0F;
         }
@@ -296,6 +297,9 @@ public class Samcra extends AbstractPathComputation {
         /* Connected Vertex's paths management using CspfPath object.
          * The paths list is explored and the paths dominated by the new path are marked as dominated.
          * The new path is also check and if it is dominated by an existing path it is omitted.
+         * Even if call to pruneEdge() method has removed edges that do not meet constraints, the method keep edges
+         * that have no Delay or TE Metric if the Delay, respectively the TE Metric are not specified in constraints.
+         * So, Delay and TE Metric presence in edge attributes must be checked again.
          */
         if (edge.getEdge().getEdgeAttributes().getTeMetric() != null) {
             teCost = edge.getEdge().getEdgeAttributes().getTeMetric().intValue() + currentPath.getCost();
@@ -310,7 +314,7 @@ public class Samcra extends AbstractPathComputation {
 
         SamcraPath samcraPath = samcraPaths.get(nextVertexPath.getVertexKey());
         if (isPathDominated(samcraPath)) {
-            LOG.debug("relaxSamcra: Skip Edge because new path is dominated");
+            LOG.debug("     - Skip Edge because new path is dominated");
             return 0F;
         }
 
@@ -318,7 +322,7 @@ public class Samcra extends AbstractPathComputation {
          * is created with predecessor set to connected vertex, path length and path status information,
          * marked as "active" and added to the connected vertex's path list.
          * The weight attribute, used as classification key by the priority queue, is an integer value computed
-         * from the TE and delay length.
+         * from the TE Metric and Delay length.
          */
         CspfPath newPath = createNonDominatedPath(edge, nextVertexPath.getVertex(), currentPath);
 
@@ -332,14 +336,15 @@ public class Samcra extends AbstractPathComputation {
          */
         CspfPath currentSamcraPath = samcraPath.getCurrentPath();
         if (currentSamcraPath == null) {
-            LOG.debug("relaxSamcra: add new Path: {}", newPath);
+            LOG.debug("     - Add new Path {}", newPath.toString());
             if (!(newPath.equals(pathDestination))) {
                 priorityQueue.add(newPath);
             }
             newPath.setPathStatus(CspfPath.SELECTED);
             samcraPath.setCurrentPath(newPath);
         } else if (newPath.getPathLength() < currentSamcraPath.getPathLength()) {
-            LOG.debug("relaxSamcra: update current Path: {} with new Path: {}", currentSamcraPath, newPath);
+            LOG.debug("     - Update current path up to {} with new path {}",
+                    currentSamcraPath.getVertex().toString(), newPath.toString());
             samcraPath.getPathList()
                 .stream()
                 .filter(path -> path.getPathStatus() == CspfPath.SELECTED)
@@ -362,11 +367,9 @@ public class Samcra extends AbstractPathComputation {
         samcraPath.addPath(newPath);
         samcraPath.incrementPathCount();
 
-        LOG.debug("relaxSamcra: number of paths  {} ", samcraPath.getPathCount());
-        LOG.debug("relaxSamcra: add vertex Paths to samcraPath {} with index {}", samcraPath,
-                samcraPath.getVertex().getKey());
-        LOG.debug("relaxSamcra: current Path {}  predecessor {}",  samcraPath.getCurrentPath(),
-                samcraPath.getCurrentPath().getPredecessor());
+        LOG.debug("     - Add path {} to {} with index {}/{}", samcraPath.getCurrentPath().toString(),
+                samcraPath.getCurrentPath().getVertex().toString(), samcraPath.getVertex().getKey(),
+                samcraPath.getPathCount());
         samcraPaths.put(samcraPath.getVertex().getKey(), samcraPath);
 
         /* If the destination is reached, return the computed path length 0 otherwise */
@@ -386,7 +389,7 @@ public class Samcra extends AbstractPathComputation {
      */
     private boolean isPathDominated(SamcraPath samcraPath) {
         /* Evaluate Path Domination */
-        LOG.debug("Check path domination");
+        LOG.debug("       - Check path domination");
         Uint32 teMetric = constraints.getTeMetric();
         Uint32 delay = (constraints.getDelay() != null) ? constraints.getDelay().getValue() : null;
 
@@ -396,7 +399,7 @@ public class Samcra extends AbstractPathComputation {
             boolean testedPathCostDominated = false;
             boolean testedPathDelayDominated = false;
 
-            LOG.debug("Check if path {} is dominated or dominates", testedPath);
+            LOG.debug("       - Check if path {} is dominated or dominates", testedPath.toString());
             if (testedPath.getPathStatus() != CspfPath.DOMINATED) {
                 if (teMetric != null) {
                     if (teCost >= testedPath.getCost()) {
@@ -415,16 +418,17 @@ public class Samcra extends AbstractPathComputation {
 
                 if ((((teMetric != null) && (pathCostDominated)) && ((pathDelayDominated) || (delay == null)))
                         || ((teMetric == null) && ((delay != null) && (pathDelayDominated)))) {
-                    LOG.debug("New path is dominated with teCost: {} delayCost: {}", teCost, delayCost);
+                    LOG.debug("       - New path is dominated by teCost {} and/or delayCost {}", teCost, delayCost);
                     /* A path that dominates the current path has been found */
                     return true;
                 } else if ((((teMetric != null) && (testedPathCostDominated))
                         && ((testedPathDelayDominated) || (delay == null)))
                         || ((teMetric == null) && ((delay != null) && (testedPathDelayDominated)))) {
-                    /* Old Path is dominated by the new path. Mark it as Dominated and decrement number of valid Path */
+                    /* Old Path is dominated by the new path. Mark it as Dominated and decrement
+                     * the number of valid Paths */
                     testedPath.setPathStatus(CspfPath.DOMINATED);
                     samcraPath.decrementPathCount();
-                    LOG.debug("New path dominates existing path: {} teCost: {} delayCost:  {}", testedPath,
+                    LOG.debug("       - New path dominates existing path with teCost {} and/or delayCost {}",
                             testedPath.getCost(), testedPath.getDelay());
                 }
             }
@@ -437,7 +441,7 @@ public class Samcra extends AbstractPathComputation {
         Uint32 metric = constraints.getTeMetric();
         Uint32 delay = (constraints.getDelay() != null) ? constraints.getDelay().getValue() : null;
 
-        LOG.debug("Create new non dominated path");
+        LOG.debug("       - Create new non dominated path");
 
         /* Compute Path length as key for the path Weight */
         float teLength = 0.0F;
@@ -464,9 +468,9 @@ public class Samcra extends AbstractPathComputation {
                 .replacePath(cspfPath.getPath())
                 .addConnectedEdge(edge);
 
-        LOG.debug("Creation of a new Path: {} path length: {} predecessor connected vertex {}",
-                newPath, pathLength, newPath.getPredecessor());
+        LOG.debug("       - Created new Path {} with length {}, cost {} and delay {}",
+                newPath.toString(), pathLength, teCost, delayCost);
 
-        return cspfPath;
+        return newPath;
     }
 }
diff --git a/docs/algo/algo-user-guide-algo-model.rst b/docs/algo/algo-user-guide-algo-model.rst
new file mode 100644 (file)
index 0000000..da36c4c
--- /dev/null
@@ -0,0 +1,141 @@
+.. _algo-user-guide-algo-model:
+
+Path Computation Algorithms Overview
+====================================
+
+This section provides a high-level overview about Path Computation Algorithms.
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+
+Path Computation Theory
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Path computation in network has for objective to find a path between two end
+points (p2p) or between a point and a multiple destination points (p2mp). The
+well known algorithm is the Djikstra one whch aims to find the shortest path
+by taking into account the number of hop as key objective.
+
+In addition, path computation aims also to take into account various constraints
+to find optimal path in a network. The constraints are various and may include
+standard routing protcol metric (IGP metric), Traffic Enginnering metic (TE
+metric), end to end delay (Delay), end to end delay variation (Jitter) ...
+all referenced as *Additive Metric* because the metric carried by each link is
+added together to check that the end-to-end constraint is respected. The second
+category of metric is named *Concave Metric* and concerns the Bandwidth and
+Loss. Indeed, these metrics are not added together but checked over each link
+to verify that the constraints are met.
+
+For more information, reader could refer to Path Computation algorithms
+e.g. Shortest Path First (SPF) https://en.wikipedia.org/wiki/Shortest_path_problem
+and Constrainted Shortest Path First (CSPF) https://en.wikipedia.org/wiki/Constrained_Shortest_Path_First.
+
+Path Computation Overview
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This features provides three Path Computation algorithms:
+
+* Shortest Path First (SPF) a.k.a. Djikstra
+* Constrainted Shortest Path First (CSPF)
+* Self Adaptive Multiple Constraints Routing Algorithm (SAMCRA)
+
+All of these algorithms use the same principles:
+
+* A priority Queue where all potential paths are stored
+* A pruning function that validate / invalidate edge to next vertex regarding
+  the constraints
+
+The priority queue sort elements based on a key and outpout the element that
+presents the smallest key value. Here, depedning of the algorithm, the key will
+represents the standard metric (SPF), the Traffic Engineering Metric (CSPF) or
+the TE Metric and Delay as composite metric. The key represents only *Additive
+Metrics* to be optimized.
+
+The pruning function will check if edge to the next vertex respects the given
+constraints. This concerns both *Concave Metrics*, bandwidth and loss, and
+*Additive Metrics*. For the latter, current metrics value are added to the
+edge metrics value and check against given constraints. In addition, address
+family (IPv4, IPv6, Segment Routing for IPv4 or IPv6) is checked to avoid
+validate a path that is not capable of the given requested address family
+(e.g. an IPv4 vertex / edge for an IPv6 path).
+
+The pseudo code below shows how the various algorithms are working:
+
+.. code-block:: java
+
+    /* Initialize the algorithms */
+    initialize pathSource and pathDestination with vertex source and Destination;
+    visitedVertexList.clear();
+    processedPathList.clear();
+    priorityQueue.clear();
+    priorityQueue.add(pathSource);
+    currentMetric = Integer.MAX_VALUE;
+    computedPath = null;
+
+    /* Loop until Priority Queue becomes empty */
+    while (!priorityQueue.empty()) {
+        /* Get currentPath with lowest accumulated metric from the Priority Queue */
+        currentPath = priorityQueue.poll();
+
+        /* For all Edges from the current vertex, check if next Vertex is acceptable or not */
+        for (edge : currentPath.getvertex().getAllEdges()) {
+            if (pruneEdge(edge, currentPath)) {
+                continue;
+            }
+            /* If we reach destination with a better Metric, store the path */
+            if (relax(edge, currentPath) && (pathDestination.getMetric() < currentMetric))
+                computedPath = pathDestination;
+            }
+        }
+    }
+
+    /* Example of relax function that checks the standard routing Metric */
+    private boolean relax(edge, currentPath) {
+        /* Verify if we have not visited this Vertex to avoid loop */
+        if (visitedVerticeList.contains(edge.getDestination())) {
+            return false;
+        }
+
+        /* Get Next Vertex from processedPathList or create a new one if it has not yet processed */
+        nextPath = processedPathList.get(edge.getDestination());
+        if (nextPath == null) {
+            nextPath = new (edge.getDestination());
+            processedPathList.add(nextPath);
+        }
+
+        /* Compute Metric from source to this next Vertex and add or update it in the Priority Queue
+         * if total path Metric is lower than metric associated to this next Vertex.
+         * This could occurs if we process a Vertex that as not yet been visited in the Graph
+         * or if we found a shortest path up to this Vertex. */
+        int totalMetric = edge.getMetric() + currentPath.getMetric();
+        if (nextPath.getMetric() > totalMetric) {
+            nextPath = currentPath;
+            nextPath.setMetric(totalMetric);
+            nextPath.addEdgeToPath(edge);
+            /* Here, we set the path key with the total Metric for the  Priority Queue
+             * At next iteration, Priority Queue will consider this new Path in the collection
+             * to provide the path with the lowest total Metric */
+            nextPath.setKey(totalMetric);
+            priorityQueue.add(nextPath);
+        }
+        /* Return True if we reach the destination, false otherwise */
+        return pathDestination.equals(nextPath);
+    }
+
+    /* Example of prune function that checks bandwidth and standard metric */
+    boolean pruneEdge(edge, currentPath) {
+        if (edge.getBandwidth() < constraints.getBandwidth()) {
+            return true;
+        }
+        if (edge.getMetric() + currentPath.getMetric() > constraints.getMetric()) {
+            return true;
+        }
+    }
+
+This pseudo code corresponds to the ShortestPathFist.java class.
+
+Note: Details of SAMCRA algorithm could be found in the article **Concepts of
+Exact QoS Routing Algorithms**, *Piet Van Mieghem and Fernando A. Kuipers,
+IEEE/ACM Transactions on Networking, Volume 12, Number 5, October 2004.*
+
diff --git a/docs/algo/algo-user-guide-running-algo.rst b/docs/algo/algo-user-guide-running-algo.rst
new file mode 100644 (file)
index 0000000..36faa1f
--- /dev/null
@@ -0,0 +1,181 @@
+.. _algo-user-guide-running-algo:
+
+Running Path Computation
+========================
+
+This section details how to install and use the Path Computation Algorithm
+feature.
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+
+Installation
+^^^^^^^^^^^^
+
+Install feature - ``features-algo``. Also, for the sake of this sample, it is
+required to install RESTCONF in order to use the Path Computation service.
+
+In the Karaf console, type command:
+
+.. code-block:: console
+
+    feature:install features-restconf features-algo
+
+
+Yang Model
+^^^^^^^^^^
+
+Path Computation algorithm used Graph plugin, thus graph API and yang models.
+A new yang model has been introduced in order to define constrained path model
+as well as a new RPC service to call Path Computation Algorithm plugin.
+The model is given below:
+
+.. code-block:: console
+
+      module: path-computation
+      +--rw constrained-path
+         +--rw metric?             uint32
+         +--rw te-metric?          uint32
+         +--rw delay?              gr:delay
+         +--rw jitter?             gr:delay
+         +--rw loss?               gr:loss
+         +--rw admin-group?        uint32
+         +--rw address-family?     enumeration
+         +--rw class-type?         uint8
+         +--rw bandwidth?          gr:decimal-bandwidth
+         +--rw source?             uint64
+         +--rw destination?        uint64
+         +--rw path-description* []
+         |  +--rw ipv4?    inet:ipv4-address
+         |  +--rw ipv6?    inet:ipv6-address
+         |  +--rw label?   netc:mpls-label
+         +--rw status?             computation-status
+
+      rpcs:
+         +---x get-constrained-path
+            +---w input
+            |  +---w graph-name     string
+            |  +---w source?        uint64
+            |  +---w destination?   uint64
+            |  +---w constraints
+            |  |  +---w metric?           uint32
+            |  |  +---w te-metric?        uint32
+            |  |  +---w delay?            gr:delay
+            |  |  +---w jitter?           gr:delay
+            |  |  +---w loss?             gr:loss
+            |  |  +---w admin-group?      uint32
+            |  |  +---w address-family?   enumeration
+            |  |  +---w class-type?       uint8
+            |  |  +---w bandwidth?        gr:decimal-bandwidth
+            |  +---w algorithm?     algorithm-type
+            +--ro output
+               +--ro path-description* []
+               |  +--ro ipv4?    inet:ipv4-address
+               |  +--ro ipv6?    inet:ipv6-address
+               |  +--ro label?   netc:mpls-label
+               +--ro status?               computation-status
+               +--ro computed-metric?      uint32
+               +--ro computed-te-metric?   uint32
+               +--ro computed-delay?       gr:delay
+
+
+REST API
+^^^^^^^^
+
+This section details how to use the Path Computation Service RPC that could be
+used to request a path computation from a source to a destination with given
+constraints over a given graph.
+
+Get Constrained Path
+''''''''''''''''''''
+
+Path Computation algorithms are accessible through the RPC described below:
+
+-----
+
+**URL:** ``restconf/operations/path-computation:get-constrained-path``
+
+**Method:** ``POST``
+
+**Content-Type:** ``application/json``
+
+**Request Body:**
+
+.. code-block:: json
+   :linenos:
+   :emphasize-lines: 3,4,5,6,12
+
+      {
+         "input": {
+            "graph-name": "example",
+            "source": 9,
+            "destination": 4,
+            "constraints": {
+               "address-family": "ipv4",
+               "te-metric": 250,
+               "bandwidth": 100000000,
+               "class-type": 0
+            },
+            "algorithm": "cspf"
+         }
+      }
+
+@line 3: **graph-name** The *name* of the graph that must exist.
+
+@line 4: **source** The *source* as vertex ID in the graph.
+
+@line 5: **destination** - The *destination* as vertex ID in the graph.
+
+@line 6: **constraints** - List of *Constraints*. Possible values are:
+
+* *address-family* (ipv4, ipv6, sr-ipv4 and sr-ipv6) - default ipv4
+* *te-metric* as integer value
+* *bandwidth* (byte/sec) as integer value
+* *class-type* for the bandwidth - default 0
+* *delay* (micro-second) as integer value
+
+@line 12: **algorithm** - *Type of Path Computation Algorithm* Valid options
+are ``spf``, ``cspf`` and ``samcra`` - default ``spf``.
+
+**Response Body:**
+
+.. code-block:: json
+   :linenos:
+
+      {
+         "output": {
+            "computed-metric": 210,
+            "status": "completed",
+            "path-description": [
+                  {
+                     "ipv4": "10.194.77.143"
+                  },
+                  {
+                     "ipv4": "10.194.77.155"
+                  },
+                  {
+                     "ipv4": "10.194.77.161"
+                  }
+            ]
+         }
+      }
+
+
+Troubleshooting
+^^^^^^^^^^^^^^^
+
+Debug message could be activated with:
+
+.. code-block:: console
+
+    log:set DEBUG org.opendaylight.algo
+
+Then check log with ``log:tail`` command.
+
+In particular, if answer is ``failed`` check that source and destination
+vertices are known in the graph and that constraints are not too huge.
+A good advice is to start first by relaxing some constraints to see if
+algorithm could find a valid path or not, and then re-enable constraints
+one by one to find which one could not be met. Logs will also provide
+information about constraints that are not met during the path computation.
diff --git a/docs/algo/index.rst b/docs/algo/index.rst
new file mode 100644 (file)
index 0000000..5c1536e
--- /dev/null
@@ -0,0 +1,20 @@
+.. _algo-user-guide:
+
+Path Computation Algorithms User Guide
+======================================
+
+This guide contains information on how to use the OpenDaylight Path Computation
+Algorithms plugin. They are used in the PCE Server part to compute path in
+order to fulfil Explicit Route Object (ERO) for PcResponse message in turn of a
+PcRequest message.
+
+Note: As Path Computation Algorithms use the Graph plugin, user should read the
+previous chapter about the OpenDaylight Graph plugin as well as learn about
+(Constrained) Shortest Path First algorithms.
+
+
+.. toctree::
+   :maxdepth: 1
+
+   algo-user-guide-algo-model
+   algo-user-guide-running-algo
index 43563cf4c7b9abee07d8e10dc1c3a4a51b871a29..8930d9bd8fe2a671e987b3f88ec88e6d9f5a32fa 100644 (file)
@@ -66,17 +66,17 @@ Segment Routing TE setup.
 A new yang model is provided as an alternative to the IETF yang-te-topo model
 and IETF RFC8345 for several reasons:
 
- * Some link and node parameters (e.g. Segment Routing, TE Metric extensions)
-   which are available in IP/MPLS networks, through the IGP-TE or BGP-LS
-   protocols (see Linkstate Routes in BGP RIB) are not present in the IETF
-   ted model
- * Node and link identifier are represented as string in the IETF ted model
-   which it is not efficient when looking into a HashMap() to find node or
-   link by its identifier
- * Even if LinkstateTopologyBuilder() provided mechanism to fulfil IETF ted
-   model in the datastore, the NodeHolder an TpHolder classes have been
-   defined as private, and thus could not be used outside the
-   LinkstateTopologyBuilder() class
+* Some link and node parameters (e.g. Segment Routing, TE Metric extensions)
+  which are available in IP/MPLS networks, through the IGP-TE or BGP-LS
+  protocols (see Linkstate Routes in BGP RIB) are not present in the IETF
+  ted model
+* Node and link identifiers are represented as strings in the IETF ted model,
+  which it is not efficient when looking into a HashMap() to find a node or
+  a link by its identifier
+* Even if LinkstateTopologyBuilder() provided mechanism to fulfil IETF ted
+  model in the datastore, the NodeHolder an TpHolder classes have been
+  defined as private, and thus could not be used outside the
+  LinkstateTopologyBuilder() class
 
 Graph and Algorithm have been also designed to be used by other projects
 (e.g. openflow) which not control IP/MPLS network. Thus, even if the graph
@@ -160,8 +160,8 @@ it is need to access directly at the source and destination Vertex.
 So, to overcome this limitation, the implemented Graph is composed of two
 pieces:
 
- * A standard Graph modeled in yang and stored in the Data Store
- * A Connected Graph version based on the yang model but stored in memory only
+* A standard Graph modeled in yang and stored in the Data Store
+* A Connected Graph version based on the yang model but stored in memory only
 
 
 The connected version of Vertex is composed of:
index c4aed50e373c3aaf1814c70c051859750859f3e9..a03231a96eb24092739659a84dfa502b0bbd8cfd 100644 (file)
@@ -17,10 +17,10 @@ Graph Overview). Thus, Graph feature provides a new service named
 ConnectedGraphProvider published in Karaf. This service maintains an
 up-to-date list of Connected Graph stored in memory and allows to:
 
-  * Get Connected Graph by name or GraphKey
-  * Create Connected Graph
-  * Add existing graph to create associated Connected Graph
-  * Delete existing Graph identify by its GraphKey
+* Get Connected Graph by name or GraphKey
+* Create Connected Graph
+* Add existing graph to create associated Connected Graph
+* Delete existing Graph identify by its GraphKey
 
 Then, Connected Graph provides method to manage Vertices, Edges and Prefix.
 The ConnectedGraphProvider is also in charge to maintain up to date the Graph
@@ -28,11 +28,11 @@ associated to the Connected Graph in the OpenDaylight operational Data Store.
 
 In fact, two graphs are stored in the Data Store:
 
- * Operational Graph in ``restconf/operational`` which is the graph
-   associated with the Connected Graph stored in memory
- * Configuration Graph in ``restconf/config`` which is the graph that
-   could be create / modify / delete in order to produce the Connected
-   Graph and thus, the associated Graph stored in operational Data Store
+* Operational Graph in ``restconf/operational`` which is the graph
+  associated with the Connected Graph stored in memory
+* Configuration Graph in ``restconf/config`` which is the graph that
+  could be create / modify / delete in order to produce the Connected
+  Graph and thus, the associated Graph stored in operational Data Store
 
 It is also possible to add / delete Vertices, Edges and Prefix on an existing
 Graph through the REST API.
@@ -96,29 +96,30 @@ through the ``graph:graph-topology`` namespace as follow:
 **Response Body:**
 
 .. code-block:: json
-
-  {
-      "graph-topology": {
-          "graph": [
-              {
-                  "name": "example",
-                  "vertex": [
-                      {
-                          "vertex-id": 2,
-                          "name": "r2",
-                          "vertex-type": "standard"
-                      },
-                      {
-                          "vertex-id": 1,
-                          "name": "r1",
-                          "vertex-type": "standard"
-                      }
-                  ],
-                  "graph-type": "intra-domain"
-              }
-          ]
-      }
-  }
+   :linenos:
+
+    {
+        "graph-topology": {
+            "graph": [
+                {
+                    "name": "example",
+                    "vertex": [
+                        {
+                            "vertex-id": 2,
+                            "name": "r2",
+                            "vertex-type": "standard"
+                        },
+                        {
+                            "vertex-id": 1,
+                            "name": "r1",
+                            "vertex-type": "standard"
+                        }
+                    ],
+                    "graph-type": "intra-domain"
+                }
+            ]
+        }
+    }
 
 Graphs publish in the configuration Data Store are also accessible through REST
 API with the same namespace as follow:
@@ -132,29 +133,30 @@ API with the same namespace as follow:
 **Response Body:**
 
 .. code-block:: json
-
-  {
-      "graph-topology": {
-          "graph": [
-              {
-                  "name": "example",
-                  "vertex": [
-                      {
-                          "vertex-id": 2,
-                          "name": "r2",
-                          "vertex-type": "standard"
-                      },
-                      {
-                          "vertex-id": 1,
-                          "name": "r1",
-                          "vertex-type": "standard"
-                      }
-                  ],
-                  "graph-type": "intra-domain"
-              }
-          ]
-      }
-  }
+   :linenos:
+
+    {
+        "graph-topology": {
+            "graph": [
+                {
+                    "name": "example",
+                    "vertex": [
+                        {
+                            "vertex-id": 2,
+                            "name": "r2",
+                            "vertex-type": "standard"
+                        },
+                        {
+                            "vertex-id": 1,
+                            "name": "r1",
+                            "vertex-type": "standard"
+                        }
+                    ],
+                    "graph-type": "intra-domain"
+                }
+            ]
+        }
+    }
 
 Create Graph
 ''''''''''''
@@ -174,41 +176,43 @@ Data Store. This includes all modification and associated Connected Graphs.
 **Request Body:**
 
 .. code-block:: json
-
-  {
-      "graph-topology": {
-          "graph": [
-              {
-                  "name": "example",
-                  "graph-type": "intra-domain",
-                  "vertex": [
-                      {
-                          "vertex-id": 1,
-                          "name": "r1"
-                      },
-                      {
-                          "vertex-id": 2,
-                          "name": "r2"
-                      }
-                  ],
-                  "edge": [
-                      {
-                          "edge-id": 1,
-                          "name": "r1 - r2",
-                          "local-vertex-id": 1,
-                          "remote-vertex-id": 2
-                      },
-                      {
-                          "edge-id": 2,
-                          "name": "r2 - r1",
-                          "local-vertex-id": 2,
-                          "remote-vertex-id": 1
-                      }
-                  ]
-              }
-          ]
-      }
-  }
+   :linenos:
+   :emphasize-lines: 5,6,7,17,21,22
+
+    {
+        "graph-topology": {
+            "graph": [
+                {
+                    "name": "example",
+                    "graph-type": "intra-domain",
+                    "vertex": [
+                        {
+                            "vertex-id": 1,
+                            "name": "r1"
+                        },
+                        {
+                            "vertex-id": 2,
+                            "name": "r2"
+                        }
+                    ],
+                    "edge": [
+                        {
+                            "edge-id": 1,
+                            "name": "r1 - r2",
+                            "local-vertex-id": 1,
+                            "remote-vertex-id": 2
+                        },
+                        {
+                            "edge-id": 2,
+                            "name": "r2 - r1",
+                            "local-vertex-id": 2,
+                            "remote-vertex-id": 1
+                        }
+                    ]
+                }
+            ]
+        }
+    }
 
 @line 5: **name** The Graph identifier. Must be unique.
 
@@ -263,16 +267,17 @@ it will be automatically created. Only POST method must be used.
 **Request Body:**
 
 .. code-block:: json
-
-  {
-      "vertex": [
-          {
-              "vertex-id": 100,
-              "name": "r100",
-              "router-id": "192.168.1.100"
-          }
-      ]
-  }
+   :linenos:
+
+    {
+        "vertex": [
+            {
+                "vertex-id": 100,
+                "name": "r100",
+                "router-id": "192.168.1.100"
+            }
+        ]
+    }
 
 Delete Vertex
 '''''''''''''
@@ -305,23 +310,24 @@ it will be automatically created. Only POST method must be used.
 **Request Body:**
 
 .. code-block:: json
-
-  {
-      "edge": [
-          {
-              "edge-id": 10,
-              "name": "r1 - r2",
-              "local-vertex-id": 1,
-              "remote-vertex-id": 2
-          },
-          {
-              "edge-id": 20,
-              "name": "r2 - r1",
-              "local-vertex-id": 2,
-              "remote-vertex-id": 1
-          }
-      ]
-  }
+   :linenos:
+
+    {
+        "edge": [
+            {
+                "edge-id": 10,
+                "name": "r1 - r2",
+                "local-vertex-id": 1,
+                "remote-vertex-id": 2
+            },
+            {
+                "edge-id": 20,
+                "name": "r2 - r1",
+                "local-vertex-id": 2,
+                "remote-vertex-id": 1
+            }
+        ]
+    }
 
 Delete Edge
 '''''''''''
@@ -354,15 +360,16 @@ it will be automatically created. Only POST method must be used.
 **Request Body:**
 
 .. code-block:: json
-
-  {
-      "prefix": [
-          {
-              "prefix": "192.168.1.0/24",
-              "vertex-id": 1
-          }
-      ]
-  }
+   :linenos:
+
+    {
+        "prefix": [
+            {
+                "prefix": "192.168.1.0/24",
+                "vertex-id": 1
+            }
+        ]
+    }
 
 Delete Prefix
 '''''''''''''
index 6b05567d921d916c13bbadfbf51ba4abebfc8b8b..bf78bfb7b3237813793953c2e0d4d5ea7920b159 100644 (file)
@@ -4,13 +4,13 @@ Running Graph
 =============
 This section explains how to install Graph plugin.
 
-1. Install Graph feature - ``feature-graph``.
-   Also, for sake of this sample, it is required to install RESTCONF.
+1. Install Graph feature - ``features-graph``.
+   Also, for the sake of this sample, it is required to install RESTCONF.
    In the Karaf console, type command:
 
    .. code-block:: console
 
-      feature:install feature-restconf feature-graph
+      feature:install features-restconf features-graph
 
 2. The Graph plugin contains a default empty configuration, which is applied
    after the feature starts up. One instance of Graph plugin is created
index 234a4e2c30d5e9d847a77e31f9ea93ce1a443162..b9acf9660795bbc7ac90061ce19f55052b0577a8 100644 (file)
@@ -28,3 +28,4 @@ User Guides
    bmp/index
    pcep/index
    graph/index
+   algo/index
index 59bf603c7fde804ac4bb15ce915f763c6de38cef..109ce2e5f7d8cda962b796c1db59c7ca0277aaee 100644 (file)
@@ -13,6 +13,7 @@ The user should learn about PCEP basic concepts, supported capabilities, configu
    pcep-user-guide-supported-capabilities
    pcep-user-guide-running-pcep
    pcep-user-guide-active-stateful-pce
+   pcep-user-guide-path-computation
    pcep-user-guide-session-statistics
    pcep-user-guide-cli
    pcep-user-guide-test-tools
diff --git a/docs/pcep/pcep-user-guide-path-computation.rst b/docs/pcep/pcep-user-guide-path-computation.rst
new file mode 100644 (file)
index 0000000..02006e3
--- /dev/null
@@ -0,0 +1,174 @@
+.. _pcep-user-guide-path-computation:
+
+Path Computation Server
+=======================
+
+This section describes how to use Path Computation Server bundle in
+conjunction with the Path Computation Algorithm and Graph plugins. This Server
+provides a full PCE component that fully supports RFC5440 including PcRequest
+PcResponse messages exchanges from a PCC requesting a valid path to the PCE.
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+
+Installation
+^^^^^^^^^^^^
+
+Check that the feature ``features-algo`` is installed. Normally it will be
+installed with the pcep feature ``features-pcep``. Otherwise, install it
+with the following command:
+
+.. code-block:: console
+
+    feature:install features-algo
+
+Graph Setup
+^^^^^^^^^^^
+
+The Path Computation Server uses the Path Computation Algorithm plugin which
+needs a graph to be able to compute constrained paths. Thus, a valid graph must
+be provided manually or automatically.
+
+Manual activation
+'''''''''''''''''
+
+Create a new graph with the Rest API ``Create Graph``:
+
+.. code-block:: console
+
+    PUT: restconf/config/graph:graph-topology
+
+Refer to Graph documentation for details.
+
+There is a restriction on the name of the graph due to the Path Computation
+Algorithm integration. It must be started by **"ted://"** string in order to
+be learn automatically by the Path Computation Server bundle.
+
+Note that this kind of graph remains static. Thus, resources, mostly bandwidth,
+are not updated after deploying some RSVP-TE tunnels which consume bandwidth.
+
+BGP-LS activation
+'''''''''''''''''
+
+To achieve better experience, notably in conjunction with RSVP-TE and in order
+to work on an up-to-date graph, an integration is provided with the BGP Link
+State protocol. This allows to automatically fulfil a graph with network
+traffic engineering information conveyed by the BGP-LS protocol. The resources,
+mostly bandwidth, are automatically updated in the graph after deploying
+an RSVP-TE tunnel. Note that this is not the case with Segment Routing.
+
+For that purpose, just setup a BGP peering with a router that is BGP-LS
+speaker and report traffic engineering network topology from IS-IS-TE or
+OSPF-TE routing protocol. Refer to BGP documentation for the detail about
+how to setup a BGP peering with Link-State family.
+
+Once done, verify that the graph is correctly fulfil with the Rest API:
+
+.. code-block:: console
+
+    GET: restconf/operational/graph:graph-topology
+
+Usage
+^^^^^
+
+There is two ways to use the Path Computation Server: through PcRequest and
+with PcInitiate.
+
+With PcRequest, just create a new tunnel on the router with an external PCE
+for path computation. Once PcRequest received, the Path Computation Server
+launches the path computation algorithm with requested parameters and in turn
+sends back to the PCC a PcResponse message with the computed path in the ERO.
+A NO-PATH object is returned in case of failure with the reason (e.g. source
+or destination unknown, constraints not met ...). Check on the router that
+the tunnel is up and running. Wireshark capture will help to determine
+if the exchanges between the PCC and the PCE went well. Setting log debug for
+algo and pcep plugins and looking to the log will also ease debugging.
+
+With PcInitiate message, just use the PCEP Rest API to setup an LSP
+
+.. code-block:: console
+
+    POST: /restconf/operations/network-topology-pcep:add-lsp
+
+by omitting the ERO Object. Indeed, an automatic call to the Path Computation
+Algorithm will be triggered when the ERO is absent or empty with the given
+end-points and metrics objects as input paramters. Address family is
+automatically deduced from the IP address family of the end-points object.
+The same behaviour applies for Segment Routing: just add the *PST=1* indication
+in the json or xml payload will force the address family of path computation
+to Segment Routing.
+
+To verify the result, just check the LSP-Database. The new LSP must have an
+ERO automatically computed as well as an RRO. Again, setting log debug for algo
+and pcep plugins and looking to the log will also help to verify that all is
+conform as expected.
+
+Known limitations
+^^^^^^^^^^^^^^^^^
+
+As the Path Computation Server is in its initial release, there are some
+limitations mentioned hereinafter:
+
+* Following PCEP Objects that may be present in the PcRequest message are not
+  yet supported, and right now, ignored:
+
+  * Include Route Object (IRO)
+  * Exclude Route Object (XRO)
+  * Objective Function (OF)
+
+* LSP-Update Rest API with an empty ERO will not trigger Path Computation
+  Algorithm. Use Path Computation Algorithm Rest API to get a new path, and
+  then use the LSP-Update Rest API as usual with the computed ERO.
+
+* For Segment Routing, ERO is provided with Node SID for NAI and SID index.
+
+* Due to the integration with BGP-LS, the graph name must start with *ted://*
+  tag in order to be automatically used by the pcep plugin.
+
+* For Segment Routing, as network resources are not updated due to the lack
+  of signaling, the resources consumed by the new segment path are not updated
+  in the graph.
+
+All these limitations will be solved in future releases.
+
+Known Bug
+^^^^^^^^^
+
+When using BGP-LS for automatic Graph topology acquisition, for an undetermined
+reason, karaf is unable to start properly the *bgp-topology-provider* bundle.
+This is due to karaf that doesn't properly manage blueprint dependencies. Thus,
+BGP Topology Provider class is initialized with a wrong reference to the Graph
+Topology Service: a null pointer is provided instead. However, it is easy to
+overcome this issue by simply restarting the *bgp-topology-provider* bundle.
+
+First identify the bundle number of *bgp-topology-provider* and check the
+status.
+
+.. code-block:: console
+
+    opendaylight-user@karaf>bundle:list | grep bgp-topology-provider
+    232 │ Failure  │  80 │ 0.14.0          │ bgp-topology-provider
+
+
+Then restart the bundle if status is *Failure*
+
+.. code-block:: console
+
+    opendaylight-user@karaf>bundle:restart 232
+
+And finaly, verify that the bundle is active
+
+.. code-block:: console
+
+    opendaylight-user@root>bundle:list 232
+    START LEVEL 100 , List Threshold: 50
+     ID │ State  │ Lvl │ Version         │ Name
+    ────┼────────┼─────┼─────────────────┼───────────────────────
+    232 │ Active │  80 │ 0.14.0          │ bgp-topology-provider
+
+
+Looking to the log, you will normally see that a new Graph has been created and
+fulfil with your network topology element. Using Graph Rest API *Get Operational
+Graph* will also validate that all is running correctly.
+
index 6da8747f03ec79b56955e33f5983670d09dd3bc0..80782fe307c3aa7386871c5cfb03c254c2c83397 100644 (file)
@@ -42,6 +42,12 @@ PCEP is not working...
 
      log:set DEBUG org.opendaylight.bgpcep.pcep
 
+and for Path Computation Algorithm
+
+  .. code-block:: console
+
+      log:set DEBUG org.opendaylight.algo
+
 Bug reporting
 ^^^^^^^^^^^^^
 Before you report a bug, check `BGPCEP Jira <https://jira.opendaylight.org/projects/BGPCEP/issues/BGPCEP-589?filter=allopenissues>`_ to ensure same/similar bug is not already filed there.