2 * Copyright (c) 2020 Orange. All rights reserved.
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
8 package org.opendaylight.algo.impl;
10 import java.util.ArrayList;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.PriorityQueue;
14 import org.eclipse.jdt.annotation.Nullable;
15 import org.opendaylight.algo.PathComputationAlgorithm;
16 import org.opendaylight.graph.ConnectedEdge;
17 import org.opendaylight.graph.ConnectedGraph;
18 import org.opendaylight.graph.ConnectedVertex;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.edge.EdgeAttributes;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.edge.attributes.UnreservedBandwidth;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.graph.topology.graph.Prefix;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.graph.topology.graph.VertexKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.network.concepts.rev131125.MplsLabel;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.AddressFamily;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.ComputationStatus;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.ConstrainedPath;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.ConstrainedPathBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.PathConstraints;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.path.constraints.ExcludeRoute;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.path.constraints.IncludeRoute;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.path.descriptions.PathDescription;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.path.descriptions.PathDescriptionBuilder;
36 import org.opendaylight.yangtools.yang.common.Uint32;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 public abstract class AbstractPathComputation implements PathComputationAlgorithm {
42 private static final Logger LOG = LoggerFactory.getLogger(AbstractPathComputation.class);
44 protected final ConnectedGraph graph;
46 /* Source and Destination of the path */
47 protected CspfPath pathSource = null;
48 protected CspfPath pathDestination = null;
50 /* Constraints that the path must respect */
51 protected PathConstraints constraints = null;
53 /* Priority Queue and HashMap to manage path computation */
54 protected final PriorityQueue<CspfPath> priorityQueue = new PriorityQueue<>();
55 protected final HashMap<Long, CspfPath> processedPath = new HashMap<>();
57 protected AbstractPathComputation(final ConnectedGraph graph) {
62 * Initialize the various parameters for Path Computation, in particular the
63 * Source and Destination CspfPath.
66 * Source Vertex Identifier in the Connected Graph
68 * Destination Vertex Identifier in the Connected Graph
70 * @return Constrained Path Builder with status set to 'OnGoing' if
71 * initialization success, 'Failed' otherwise
73 protected ConstrainedPathBuilder initializePathComputation(final VertexKey src, final VertexKey dst) {
74 ConstrainedPathBuilder cpathBuilder = new ConstrainedPathBuilder().setStatus(ComputationStatus.InProgress);
76 /* Check that source and destination vertexKey are not identical */
77 if (src.equals(dst)) {
78 LOG.warn("Source and Destination are equal: Abort!");
79 return cpathBuilder.setStatus(ComputationStatus.EqualEndpoints);
83 * Get the Connected Vertex from the Graph to initialize the source of
86 ConnectedVertex vertex = graph.getConnectedVertex(src.getVertexId().longValue());
88 LOG.warn("Found no source for Vertex Key {}", src);
89 return cpathBuilder.setStatus(ComputationStatus.NoSource);
91 LOG.debug("Create Path Source with Vertex {}", vertex);
92 pathSource = new CspfPath(vertex).setCost(0).setDelay(0);
93 cpathBuilder.setSource(vertex.getVertex().getVertexId());
96 * Get the Connected Vertex from the Graph to initialize the destination
99 vertex = graph.getConnectedVertex(dst.getVertexId().longValue());
100 if (vertex == null) {
101 LOG.warn("Found no destination for Vertex Key {}", src);
102 return cpathBuilder.setStatus(ComputationStatus.NoDestination);
104 LOG.debug("Create Path Destination with Vertex {}", vertex);
105 pathDestination = new CspfPath(vertex);
106 cpathBuilder.setDestination(vertex.getVertex().getVertexId());
108 /* Initialize the Priority Queue, HashMap */
109 priorityQueue.clear();
110 priorityQueue.add(pathSource);
111 processedPath.clear();
112 processedPath.put(pathSource.getVertexKey(), pathSource);
113 processedPath.put(pathDestination.getVertexKey(), pathDestination);
118 private boolean verifyAddressFamily(final ConnectedEdge edge, final EdgeAttributes attributes) {
119 /* Check that Edge belongs to the Address Family of the requested path */
120 switch (constraints.getAddressFamily()) {
122 if (attributes.getRemoteAddress() == null) {
123 LOG.debug("No Ipv4 address");
128 if (attributes.getRemoteAddress6() == null) {
129 LOG.debug("No Ipv6 address");
134 if (getIpv4NodeSid(edge.getDestination()) == null) {
135 LOG.debug("No Node-SID for IPv4");
138 if (attributes.getAdjSid() == null) {
139 LOG.debug("No SR Adjacency-SID for IPv4");
144 if (getIpv6NodeSid(edge.getDestination()) == null) {
145 LOG.debug("No Node-SID for IPv6");
148 if (attributes.getAdjSid6() == null) {
149 LOG.debug("No SR Adjacency-SID for IPv6");
160 private boolean verifyMetrics(final EdgeAttributes attributes, final CspfPath path) {
161 /* If specified, check that total TE Metric up to this edge respects the initial constraints */
162 if (constraints.getTeMetric() != null) {
163 if (attributes.getTeMetric() == null) {
166 int totalCost = attributes.getTeMetric().intValue() + path.getCost();
167 if (totalCost > constraints.getTeMetric().intValue()) {
168 LOG.debug("TeMetric {} exceed constraint {}", totalCost, constraints.getTeMetric().intValue());
174 /* If specified, check that total Delay up to this edge respects the initial constraints */
175 if (constraints.getDelay() != null) {
176 if (attributes.getDelay() == null) {
179 int totalDelay = attributes.getDelay().getValue().intValue() + path.getDelay();
180 if (totalDelay > constraints.getDelay().getValue().intValue()) {
181 LOG.debug("Delay {} exceed constraint {}", totalDelay,
182 constraints.getDelay().getValue().intValue());
188 /* Check that Edge respect Loss constraint */
189 if (constraints.getLoss() != null) {
190 if (attributes.getLoss() == null
191 || attributes.getLoss().getValue().intValue() > constraints.getLoss().getValue().intValue()) {
199 private boolean verifyBandwidth(final ConnectedEdge edge, final EdgeAttributes attributes) {
200 if (constraints.getBandwidth() == null) {
204 int cos = constraints.getClassType() != null ? constraints.getClassType().intValue() : 0;
205 if (attributes.getMaxLinkBandwidth() == null
206 || attributes.getMaxResvLinkBandwidth() == null
207 || attributes.getUnreservedBandwidth() == null
208 || attributes.getUnreservedBandwidth().get(cos) == null) {
212 /* Get Unreserved Bandwidth for the given Class of Service / Priority */
213 Long bandwidth = constraints.getBandwidth().getValue().longValue();
215 for (UnreservedBandwidth unResBw : attributes.getUnreservedBandwidth()) {
216 if (unResBw.getClassType().intValue() == cos) {
217 unrsv = unResBw.getBandwidth().getValue().longValue();
221 Long maxBW = attributes.getMaxLinkBandwidth().getValue().longValue();
222 if (bandwidth > List.of(unrsv,
223 // maxBW might be on the list but will always be greater
224 // than the next items
225 maxBW - edge.getCosResvBandwidth(cos), maxBW - edge.getGlobalResvBandwidth(),
226 attributes.getMaxResvLinkBandwidth().getValue().longValue()).stream().mapToLong(v -> v).min()
228 LOG.debug("Bandwidth constraint is not met");
235 private boolean verifyExcludeRoute(final ConnectedEdge edge, final EdgeAttributes attributes) {
236 if (constraints.getExcludeRoute() == null || constraints.getExcludeRoute().isEmpty()) {
240 final List<ExcludeRoute> xro = constraints.getExcludeRoute();
241 switch (constraints.getAddressFamily()) {
244 for (int i = 0; i < xro.size(); i++) {
245 final Ipv4Address address = xro.get(i).getIpv4();
246 if (address.equals(attributes.getRemoteAddress())
247 || address.equals(attributes.getLocalAddress())
248 || address.equals(edge.getSource().getVertex().getRouterId())
249 || address.equals(edge.getDestination().getVertex().getRouterId())) {
256 for (int i = 0; i < xro.size(); i++) {
257 final Ipv6Address address = xro.get(i).getIpv6();
258 if (address.equals(attributes.getRemoteAddress6())
259 || address.equals(attributes.getLocalAddress6())
260 || address.equals(edge.getSource().getVertex().getRouterId6())
261 || address.equals(edge.getDestination().getVertex().getRouterId6())) {
274 * Check if Edge need to be prune regarding all constraints including
277 * @return True if Edge must be prune, False if Edge must be keep
279 protected boolean pruneEdge(final ConnectedEdge edge, final CspfPath path) {
280 /* Check that Constraints are initialized */
281 if (constraints == null) {
282 LOG.warn("Constraints not set");
286 /* Edge could point to an unknown Vertex e.g. with inter-domain link */
287 if (edge.getDestination() == null || edge.getDestination().getVertex() == null) {
288 LOG.debug("No Destination");
292 /* Check that Edge have attributes */
293 EdgeAttributes attributes = edge.getEdge() != null ? edge.getEdge().getEdgeAttributes() : null;
294 if (attributes == null) {
295 LOG.debug("No attributes");
299 /* Check that Edge belongs to the requested address family */
300 if (verifyAddressFamily(edge, attributes)) {
304 /* Check only IGP Metric, if specified, for simple SPF algorithm */
305 if (this instanceof ShortestPathFirst) {
306 if (constraints.getMetric() != null) {
307 if (attributes.getMetric() == null) {
310 int totalCost = attributes.getMetric().intValue() + path.getCost();
311 if (totalCost > constraints.getMetric().intValue()) {
312 LOG.debug("Metric {} exceed constraint {}", totalCost, constraints.getMetric().intValue());
316 LOG.trace("Edge {} is valid for Simple Path Computation", edge);
320 /* Check that Edge respect Metric constraints */
321 if (verifyMetrics(attributes, path)) {
325 /* Check that Edge meet Bandwidth constraint */
326 if (verifyBandwidth(edge, attributes)) {
330 /* Check that Edge belongs to admin group */
331 if (constraints.getAdminGroup() != null
332 && !constraints.getAdminGroup().equals(attributes.getAdminGroup())) {
333 LOG.debug("Not in the requested admin-group");
337 /* Check that Edge is not part of Exclude Route */
338 if (verifyExcludeRoute(edge, attributes)) {
343 * OK. All is fine. We can consider this Edge valid, so not to be prune
345 LOG.trace("Edge {} is valid for Constrained Path Computation", edge);
350 * Return the MPLS Label corresponding to the Node SID for IPv4 when the
351 * Connected Vertex is Segment Routing aware.
353 * @return MPLS Label if Connected Vertex is Segment Routing aware, Null
356 protected @Nullable MplsLabel getIpv4NodeSid(final ConnectedVertex cvertex) {
358 * Check that Vertex is Segment Routing aware
360 if (cvertex.getVertex() == null || cvertex.getVertex().getSrgb() == null) {
364 * Find in Prefix List Node SID attached to the IPv4 of the next Vertex
365 * and return the MPLS Label that corresponds to the index in the SRGB
367 if (cvertex.getPrefixes() == null) {
370 for (Prefix prefix : cvertex.getPrefixes()) {
371 if (prefix.getPrefixSid() == null || prefix.getNodeSid() == null) {
374 if (prefix.getNodeSid() && prefix.getPrefix().getIpv4Prefix() != null) {
375 return new MplsLabel(Uint32.valueOf(prefix.getPrefixSid().intValue()));
382 * Return the MPLS Label corresponding to the Node SID for IPv6 when the
383 * Connected Vertex is Segment Routing aware.
385 * @return MPLS Label if Connected Vertex is Segment Routing aware, Null
388 protected @Nullable MplsLabel getIpv6NodeSid(final ConnectedVertex cvertex) {
390 * Check that Vertex is Segment Routing aware
392 if (cvertex.getVertex() == null || cvertex.getVertex().getSrgb() == null) {
396 * Find in Prefix List Node SID attached to the IPv6 of the next Vertex
397 * and return the MPLS Label that corresponds to the index in the SRGB
399 if (cvertex.getPrefixes() == null) {
402 for (Prefix prefix : cvertex.getPrefixes()) {
403 if (prefix.getPrefixSid() == null || prefix.getNodeSid() == null) {
406 if (prefix.getNodeSid() && prefix.getPrefix().getIpv6Prefix() != null) {
407 return new MplsLabel(Uint32.valueOf(prefix.getPrefixSid().intValue()));
414 * Convert List of Connected Edges into a Path Description as a List of
415 * IPv4, IPv6 or MPLS Label depending of the requested Address Family.
418 * List of Connected Edges
420 * @return Path Description
422 protected List<PathDescription> getPathDescription(final List<ConnectedEdge> edges) {
423 ArrayList<PathDescription> list = new ArrayList<>();
425 for (ConnectedEdge edge : edges) {
426 PathDescription pathDesc = null;
427 switch (constraints.getAddressFamily()) {
429 pathDesc = new PathDescriptionBuilder()
430 .setIpv4(edge.getEdge().getEdgeAttributes().getLocalAddress())
431 .setRemoteIpv4(edge.getEdge().getEdgeAttributes().getRemoteAddress())
435 pathDesc = new PathDescriptionBuilder()
436 .setIpv6(edge.getEdge().getEdgeAttributes().getLocalAddress6())
437 .setRemoteIpv6(edge.getEdge().getEdgeAttributes().getRemoteAddress6())
441 pathDesc = new PathDescriptionBuilder()
442 .setIpv4(edge.getEdge().getEdgeAttributes().getLocalAddress())
443 .setRemoteIpv4(edge.getEdge().getEdgeAttributes().getRemoteAddress())
444 .setSid(edge.getEdge().getEdgeAttributes().getAdjSid())
448 pathDesc = new PathDescriptionBuilder()
449 .setIpv6(edge.getEdge().getEdgeAttributes().getLocalAddress6())
450 .setRemoteIpv6(edge.getEdge().getEdgeAttributes().getRemoteAddress6())
451 .setSid(edge.getEdge().getEdgeAttributes().getAdjSid6())
462 private VertexKey getVertexKey(final IncludeRoute iro, AddressFamily af) {
463 IpAddress address = null;
468 address = new IpAddress(iro.getIpv4());
472 address = new IpAddress(iro.getIpv6());
478 ConnectedVertex vertex = graph.getConnectedVertex(address);
479 return vertex != null ? vertex.getVertex().key() : null;
482 private ConstrainedPath mergePath(ConstrainedPath cp1, ConstrainedPath cp2) {
483 ArrayList<PathDescription> mergePathDesc = new ArrayList<PathDescription>(cp1.getPathDescription());
484 mergePathDesc.addAll(cp2.getPathDescription());
485 return new ConstrainedPathBuilder(cp1)
486 .setPathDescription(mergePathDesc)
487 .setStatus(cp1.getStatus().equals(cp2.getStatus()) ? cp1.getStatus() : cp2.getStatus())
492 public ConstrainedPath computeP2pPath(VertexKey source, VertexKey destination, PathConstraints cts) {
493 this.constraints = cts;
495 if (constraints.getIncludeRoute() == null || constraints.getIncludeRoute().isEmpty()) {
496 return computeSimplePath(source, destination);
499 /* Start by computing Path from source to the first Include Route address */
500 VertexKey key = getVertexKey(constraints.getIncludeRoute().get(0), constraints.getAddressFamily());
501 ConstrainedPath ctsPath = computeSimplePath(source, key);
502 if (ctsPath.getStatus() != ComputationStatus.Completed) {
506 /* Then, loop other subsequent Include Route address */
507 for (int i = 1; i < constraints.getIncludeRoute().size() - 1; i++) {
508 VertexKey next = getVertexKey(constraints.getIncludeRoute().get(i), constraints.getAddressFamily());
509 ctsPath = mergePath(ctsPath, computeSimplePath(key, next));
510 if (ctsPath.getStatus() != ComputationStatus.Completed) {
516 /* Finish path up to the destination */
517 return mergePath(ctsPath, computeSimplePath(key, destination));
520 protected abstract ConstrainedPath computeSimplePath(VertexKey source, VertexKey destination);