a65d94fcf33b23131ac7dfa60385153a0c92f8fb
[bgpcep.git] / algo / algo-impl / src / main / java / org / opendaylight / algo / impl / ShortestPathFirst.java
1 /*
2  * Copyright (c) 2016 Orange. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.algo.impl;
9
10 import java.util.HashMap;
11 import java.util.List;
12
13 import org.opendaylight.graph.ConnectedEdge;
14 import org.opendaylight.graph.ConnectedGraph;
15 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev191125.graph.topology.graph.VertexKey;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.ComputationStatus;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.ConstrainedPath;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.ConstrainedPathBuilder;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev200120.PathConstraints;
20 import org.opendaylight.yangtools.yang.common.Uint32;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24
25 /**
26  * This Class implements a simple Shortest Path First path computation algorithm based on standard IGP Metric.
27  *
28  * @author Olivier Dugeon
29  * @author Philippe Niger
30  * @author Philippe Cadro
31  *
32  */
33 public class ShortestPathFirst extends AbstractPathComputation {
34
35     private static final Logger LOG = LoggerFactory.getLogger(ShortestPathFirst.class);
36
37     private HashMap<Long, CspfPath> visitedVertices;
38
39     public ShortestPathFirst(ConnectedGraph graph) {
40         super(graph);
41         visitedVertices = new HashMap<Long, CspfPath>();
42     }
43
44     public ConstrainedPath computeP2pPath(VertexKey src, VertexKey dst, PathConstraints cts) {
45         ConstrainedPathBuilder cpathBuilder;
46         List<ConnectedEdge> edges;
47         CspfPath currentPath;
48         int currentCost = Integer.MAX_VALUE;
49
50         LOG.info("Start SPF Path Computation from {} to {} with constraints {}", src, dst, cts);
51
52         /* Initialize algorithm */
53         this.constraints = cts;
54         cpathBuilder = initializePathComputation(src, dst);
55         if (cpathBuilder.getStatus() == ComputationStatus.Failed) {
56             LOG.warn("Initial configurations are not met. Abort!");
57             return cpathBuilder.build();
58         }
59
60         visitedVertices.clear();
61
62         while (priorityQueue.size() != 0) {
63             currentPath = priorityQueue.poll();
64             visitedVertices.put(currentPath.getVertexKey(), currentPath);
65             LOG.debug("Process path to Vertex {} from Priority Queue", currentPath.getVertex().toString());
66             edges = currentPath.getVertex().getOutputConnectedEdges();
67
68             for (ConnectedEdge edge : edges) {
69                 /* Check that Edge point to a valid Vertex and is suitable for the Constraint Address Family */
70                 if (pruneEdge(edge, currentPath)) {
71                     LOG.trace("  Prune Edge {}", edge.toString());
72                     continue;
73                 }
74                 if ((relax(edge, currentPath)) && (pathDestination.getCost() < currentCost)) {
75                     currentCost = pathDestination.getCost();
76                     cpathBuilder.setPathDescription(getPathDescription(pathDestination.getPath()))
77                             .setMetric(Uint32.valueOf(pathDestination.getCost()))
78                             .setStatus(ComputationStatus.Active);
79                     LOG.debug("  Found a valid path up to destination {}", cpathBuilder.getPathDescription());
80                 }
81             }
82         }
83         /* The priority queue is empty => all the possible (vertex, path) elements have been explored
84          * The "ConstrainedPathBuilder" object contains the optimal path if it exists
85          * Otherwise an empty path with status failed is returned
86          */
87         if ((cpathBuilder.getStatus() == ComputationStatus.InProgress)
88                 || (cpathBuilder.getPathDescription().size() == 0)) {
89             cpathBuilder.setStatus(ComputationStatus.Failed);
90         } else {
91             cpathBuilder.setStatus(ComputationStatus.Completed);
92         }
93         return cpathBuilder.build();
94     }
95
96     private boolean relax(ConnectedEdge edge, CspfPath currentPath) {
97         LOG.debug("    Start relaxing Edge {} to Vertex {}", edge.toString(), edge.getDestination().toString());
98         final Long nextVertexKey = edge.getDestination().getKey();
99
100         /* Verify if we have not visited this Vertex to avoid loop */
101         if (visitedVertices.containsKey(nextVertexKey)) {
102             return false;
103         }
104
105         /* Get Next Vertex from processedPath or create a new one if it has not yet processed */
106         CspfPath nextPath = processedPath.get(nextVertexKey);
107         if (nextPath == null) {
108             nextPath = new CspfPath(edge.getDestination());
109             processedPath.put(nextPath.getVertexKey(), nextPath);
110         }
111
112         /* Compute Cost from source to this next Vertex and add or update it in the Priority Queue
113          * if total path Cost is lower than cost associated to this next Vertex.
114          * This could occurs if we process a Vertex that as not yet been visited in the Graph
115          * or if we found a shortest path up to this Vertex. */
116         int totalCost = edge.getEdge().getEdgeAttributes().getMetric().intValue() + currentPath.getCost();
117         if (nextPath.getCost() > totalCost) {
118             nextPath.setCost(totalCost)
119                     .replacePath(currentPath.getPath())
120                     .addConnectedEdge(edge);
121             /* It is not possible to directly update the CspfPath in the Priority Queue. Indeed, if we modify the path
122              * weight, the Priority Queue must be re-ordered. So, we need fist to remove the CspfPath if it is present
123              * in the Priority Queue, then, update the Path Weight, and finally (re-)insert it in the Priority Queue.
124              */
125             priorityQueue.removeIf((path) -> path.getVertexKey().equals(nextVertexKey));
126             nextPath.setKey(totalCost);
127             priorityQueue.add(nextPath);
128             LOG.debug("    Added path to Vertex {} in the Priority Queue with weight {}",
129                     nextPath.getVertex().toString(), nextPath.getKey());
130         }
131         /* Return True if we reach the destination, false otherwise */
132         return pathDestination.equals(nextPath);
133     }
134 }