/* 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>();
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 + "}";
+ }
+
}
/* 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);
*/
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();
* 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);
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;
}
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();
/*
* 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)
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);
}
* 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());
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.
*/
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;
}
/* 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();
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;
}
* 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);
*/
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)
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 */
*/
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;
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()) {
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());
}
}
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;
.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;
}
}
--- /dev/null
+.. _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.*
+
--- /dev/null
+.. _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.
--- /dev/null
+.. _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
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
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:
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
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.
**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:
**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
''''''''''''
**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.
**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
'''''''''''''
**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
'''''''''''
**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
'''''''''''''
=============
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
bmp/index
pcep/index
graph/index
+ algo/index
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
--- /dev/null
+.. _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.
+
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.