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.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 public abstract class AbstractPathComputation implements PathComputationAlgorithm {
40 private static final Logger LOG = LoggerFactory.getLogger(AbstractPathComputation.class);
42 protected final ConnectedGraph graph;
44 /* Source and Destination of the path */
45 protected CspfPath pathSource = null;
46 protected CspfPath pathDestination = null;
48 /* Constraints that the path must respect */
49 protected PathConstraints constraints = null;
51 /* Priority Queue and HashMap to manage path computation */
52 protected final PriorityQueue<CspfPath> priorityQueue = new PriorityQueue<>();
53 protected final HashMap<Long, CspfPath> processedPath = new HashMap<>();
55 protected AbstractPathComputation(final ConnectedGraph graph) {
60 * Initialize the various parameters for Path Computation, in particular the
61 * Source and Destination CspfPath.
64 * Source Vertex Identifier in the Connected Graph
66 * Destination Vertex Identifier in the Connected Graph
68 * @return Constrained Path Builder with status set to 'OnGoing' if
69 * initialization success, 'Failed' otherwise
71 protected ConstrainedPathBuilder initializePathComputation(final VertexKey src, final VertexKey dst) {
72 ConstrainedPathBuilder cpathBuilder = new ConstrainedPathBuilder().setStatus(ComputationStatus.InProgress);
74 /* Check that source and destination vertexKey are not identical */
75 if (src.equals(dst)) {
76 LOG.warn("Source and Destination are equal: Abort!");
77 return cpathBuilder.setStatus(ComputationStatus.EqualEndpoints);
81 * Get the Connected Vertex from the Graph to initialize the source of
84 ConnectedVertex vertex = graph.getConnectedVertex(src.getVertexId().longValue());
86 LOG.warn("Found no source for Vertex Key {}", src);
87 return cpathBuilder.setStatus(ComputationStatus.NoSource);
89 LOG.debug("Create Path Source with Vertex {}", vertex);
90 pathSource = new CspfPath(vertex).setCost(0).setDelay(0);
91 cpathBuilder.setSource(vertex.getVertex().getVertexId());
94 * Get the Connected Vertex from the Graph to initialize the destination
97 vertex = graph.getConnectedVertex(dst.getVertexId().longValue());
99 LOG.warn("Found no destination for Vertex Key {}", src);
100 return cpathBuilder.setStatus(ComputationStatus.NoDestination);
102 LOG.debug("Create Path Destination with Vertex {}", vertex);
103 pathDestination = new CspfPath(vertex);
104 cpathBuilder.setDestination(vertex.getVertex().getVertexId());
106 /* Initialize the Priority Queue, HashMap */
107 priorityQueue.clear();
108 priorityQueue.add(pathSource);
109 processedPath.clear();
110 processedPath.put(pathSource.getVertexKey(), pathSource);
111 processedPath.put(pathDestination.getVertexKey(), pathDestination);
116 private boolean verifyAddressFamily(final ConnectedEdge edge, final EdgeAttributes attributes) {
117 /* Check that Edge belongs to the Address Family of the requested path */
118 switch (constraints.getAddressFamily()) {
120 if (attributes.getRemoteAddress() == null) {
121 LOG.debug("No Ipv4 address");
126 if (attributes.getRemoteAddress6() == null) {
127 LOG.debug("No Ipv6 address");
132 if (getIpv4NodeSid(edge.getDestination()) == null) {
133 LOG.debug("No Node-SID for IPv4");
136 if (attributes.getAdjSid() == null) {
137 LOG.debug("No SR Adjacency-SID for IPv4");
142 if (getIpv6NodeSid(edge.getDestination()) == null) {
143 LOG.debug("No Node-SID for IPv6");
146 if (attributes.getAdjSid6() == null) {
147 LOG.debug("No SR Adjacency-SID for IPv6");
158 private boolean verifyMetrics(final EdgeAttributes attributes, final CspfPath path) {
159 /* If specified, check that total TE Metric up to this edge respects the initial constraints */
160 if (constraints.getTeMetric() != null) {
161 if (attributes.getTeMetric() == null) {
164 int totalCost = attributes.getTeMetric().intValue() + path.getCost();
165 if (totalCost > constraints.getTeMetric().intValue()) {
166 LOG.debug("TeMetric {} exceed constraint {}", totalCost, constraints.getTeMetric().intValue());
172 /* If specified, check that total Delay up to this edge respects the initial constraints */
173 if (constraints.getDelay() != null) {
174 if (attributes.getDelay() == null) {
177 int totalDelay = attributes.getDelay().getValue().intValue() + path.getDelay();
178 if (totalDelay > constraints.getDelay().getValue().intValue()) {
179 LOG.debug("Delay {} exceed constraint {}", totalDelay,
180 constraints.getDelay().getValue().intValue());
186 /* Check that Edge respect Loss constraint */
187 if (constraints.getLoss() != null) {
188 if (attributes.getLoss() == null
189 || attributes.getLoss().getValue().intValue() > constraints.getLoss().getValue().intValue()) {
197 private boolean verifyBandwidth(final ConnectedEdge edge, final EdgeAttributes attributes) {
198 if (constraints.getBandwidth() == null) {
202 int cos = constraints.getClassType() != null ? constraints.getClassType().intValue() : 0;
203 if (attributes.getMaxLinkBandwidth() == null
204 || attributes.getMaxResvLinkBandwidth() == null
205 || attributes.getUnreservedBandwidth() == null
206 || attributes.getUnreservedBandwidth().get(cos) == null) {
210 /* Get Unreserved Bandwidth for the given Class of Service / Priority */
211 Long bandwidth = constraints.getBandwidth().getValue().longValue();
213 for (UnreservedBandwidth unResBw : attributes.getUnreservedBandwidth()) {
214 if (unResBw.getClassType().intValue() == cos) {
215 unrsv = unResBw.getBandwidth().getValue().longValue();
219 Long maxBW = attributes.getMaxLinkBandwidth().getValue().longValue();
220 if (bandwidth > List.of(unrsv,
221 // maxBW might be on the list but will always be greater
222 // than the next items
223 maxBW - edge.getCosResvBandwidth(cos), maxBW - edge.getGlobalResvBandwidth(),
224 attributes.getMaxResvLinkBandwidth().getValue().longValue()).stream().mapToLong(v -> v).min()
226 LOG.debug("Bandwidth constraint is not met");
233 private boolean verifyExcludeRoute(final ConnectedEdge edge, final EdgeAttributes attributes) {
234 final var xro = constraints.getExcludeRoute();
235 if (xro == null || xro.isEmpty()) {
239 switch (constraints.getAddressFamily()) {
241 for (ExcludeRoute element : xro) {
242 final Ipv4Address address = element.getIpv4();
243 if (address.equals(attributes.getRemoteAddress())
244 || address.equals(attributes.getLocalAddress())
245 || address.equals(edge.getSource().getVertex().getRouterId())
246 || address.equals(edge.getDestination().getVertex().getRouterId())) {
252 for (ExcludeRoute element : xro) {
253 final Ipv6Address address = element.getIpv6();
254 if (address.equals(attributes.getRemoteAddress6())
255 || address.equals(attributes.getLocalAddress6())
256 || address.equals(edge.getSource().getVertex().getRouterId6())
257 || address.equals(edge.getDestination().getVertex().getRouterId6())) {
270 * Check if Edge need to be prune regarding all constraints including
273 * @return True if Edge must be prune, False if Edge must be keep
275 protected boolean pruneEdge(final ConnectedEdge edge, final CspfPath path) {
276 /* Check that Constraints are initialized */
277 if (constraints == null) {
278 LOG.warn("Constraints not set");
282 /* Edge could point to an unknown Vertex e.g. with inter-domain link */
283 final var destination = edge.getDestination();
284 if (destination == null || destination.getVertex() == null) {
285 LOG.debug("No Destination");
289 /* Check that Edge have attributes */
290 final var graphEdge = edge.getEdge();
291 final var attributes = graphEdge != null ? graphEdge.getEdgeAttributes() : null;
292 if (attributes == null) {
293 LOG.debug("No attributes");
297 /* Check that Edge belongs to the requested address family */
298 if (verifyAddressFamily(edge, attributes)) {
302 /* Check only IGP Metric, if specified, for simple SPF algorithm */
303 if (this instanceof ShortestPathFirst) {
304 final var metricConstraint = constraints.getMetric();
305 if (metricConstraint != null) {
306 final var metric = attributes.getMetric();
307 if (metric == null) {
310 final int totalCost = metric.intValue() + path.getCost();
311 final var maxCost = metricConstraint.intValue();
312 if (totalCost > maxCost) {
313 LOG.debug("Metric {} exceed constraint {}", totalCost, maxCost);
317 LOG.trace("Edge {} is valid for Simple Path Computation", edge);
321 /* Check that Edge respect Metric constraints */
322 if (verifyMetrics(attributes, path)) {
326 /* Check that Edge meet Bandwidth constraint */
327 if (verifyBandwidth(edge, attributes)) {
331 /* Check that Edge belongs to admin group */
332 final var adminGroup = constraints.getAdminGroup();
333 if (adminGroup != null && !adminGroup.equals(attributes.getAdminGroup())) {
334 LOG.debug("Not in the requested admin-group");
338 /* Check that Edge is not part of Exclude Route */
339 if (verifyExcludeRoute(edge, attributes)) {
344 * OK. All is fine. We can consider this Edge valid, so not to be prune
346 LOG.trace("Edge {} is valid for Constrained Path Computation", edge);
351 * Return the MPLS Label corresponding to the Node SID for IPv4 when the
352 * Connected Vertex is Segment Routing aware.
354 * @return MPLS Label if Connected Vertex is Segment Routing aware, Null
357 protected @Nullable MplsLabel getIpv4NodeSid(final ConnectedVertex cvertex) {
359 * Check that Vertex is Segment Routing aware
361 final var vertex = cvertex.getVertex();
362 if (vertex == null || vertex.getSrgb() == null) {
366 * Find in Prefix List Node SID attached to the IPv4 of the next Vertex
367 * and return the MPLS Label that corresponds to the index in the SRGB
369 final var prefixes = cvertex.getPrefixes();
370 if (prefixes != null) {
371 for (Prefix prefix : prefixes) {
372 final var prefixSid = prefix.getPrefixSid();
373 final var nodeSid = prefix.getNodeSid();
374 if (prefixSid != null && nodeSid != null) {
375 if (nodeSid && prefix.getPrefix().getIpv4Prefix() != null) {
376 return new MplsLabel(prefixSid);
385 * Return the MPLS Label corresponding to the Node SID for IPv6 when the
386 * Connected Vertex is Segment Routing aware.
388 * @return MPLS Label if Connected Vertex is Segment Routing aware, Null
391 protected @Nullable MplsLabel getIpv6NodeSid(final ConnectedVertex cvertex) {
393 * Check that Vertex is Segment Routing aware
395 final var vertex = cvertex.getVertex();
396 if (cvertex == null || vertex.getSrgb() == null) {
400 * Find in Prefix List Node SID attached to the IPv6 of the next Vertex
401 * and return the MPLS Label that corresponds to the index in the SRGB
403 final var prefixes = cvertex.getPrefixes();
404 if (prefixes != null) {
405 for (var prefix : cvertex.getPrefixes()) {
406 final var prefixSid = prefix.getPrefixSid();
407 final var nodeSid = prefix.getNodeSid();
408 if (prefixSid != null && nodeSid != null) {
409 if (nodeSid && prefix.getPrefix().getIpv6Prefix() != null) {
410 return new MplsLabel(prefixSid);
419 * Convert List of Connected Edges into a Path Description as a List of
420 * IPv4, IPv6 or MPLS Label depending of the requested Address Family.
423 * List of Connected Edges
425 * @return Path Description
427 protected List<PathDescription> getPathDescription(final List<ConnectedEdge> edges) {
428 final var list = new ArrayList<PathDescription>(edges.size());
429 for (var edge : edges) {
430 final var pathDesc = switch (constraints.getAddressFamily()) {
431 case Ipv4 -> new PathDescriptionBuilder()
432 .setIpv4(edge.getEdge().getEdgeAttributes().getLocalAddress())
433 .setRemoteIpv4(edge.getEdge().getEdgeAttributes().getRemoteAddress())
435 case Ipv6 -> new PathDescriptionBuilder()
436 .setIpv6(edge.getEdge().getEdgeAttributes().getLocalAddress6())
437 .setRemoteIpv6(edge.getEdge().getEdgeAttributes().getRemoteAddress6())
439 case SrIpv4 -> new PathDescriptionBuilder()
440 .setIpv4(edge.getEdge().getEdgeAttributes().getLocalAddress())
441 .setRemoteIpv4(edge.getEdge().getEdgeAttributes().getRemoteAddress())
442 .setSid(edge.getEdge().getEdgeAttributes().getAdjSid())
444 case SrIpv6 -> new PathDescriptionBuilder()
445 .setIpv6(edge.getEdge().getEdgeAttributes().getLocalAddress6())
446 .setRemoteIpv6(edge.getEdge().getEdgeAttributes().getRemoteAddress6())
447 .setSid(edge.getEdge().getEdgeAttributes().getAdjSid6())
455 private VertexKey getVertexKey(final IncludeRoute iro, final AddressFamily af) {
456 final var address = switch (af) {
457 case Ipv4, SrIpv4 -> new IpAddress(iro.getIpv4());
458 case Ipv6, SrIpv6 -> new IpAddress(iro.getIpv6());
461 final var vertex = graph.getConnectedVertex(address);
462 return vertex != null ? vertex.getVertex().key() : null;
465 private static ConstrainedPath mergePath(final ConstrainedPath cp1, final ConstrainedPath cp2) {
466 final var mergePathDesc = new ArrayList<>(cp1.getPathDescription());
467 mergePathDesc.addAll(cp2.getPathDescription());
469 final var cp1status = cp1.getStatus();
470 final var cp2status = cp2.getStatus();
471 return new ConstrainedPathBuilder(cp1)
472 .setPathDescription(mergePathDesc)
473 .setStatus(cp1status.equals(cp2status) ? cp1status : cp2status)
478 public ConstrainedPath computeP2pPath(final VertexKey source, final VertexKey destination,
479 final PathConstraints cts) {
482 final var includeRoute = constraints.getIncludeRoute();
483 if (includeRoute == null || includeRoute.isEmpty()) {
484 return computeSimplePath(source, destination);
487 final var it = includeRoute.iterator();
488 /* Start by computing Path from source to the first Include Route address */
489 VertexKey key = getVertexKey(it.next(), constraints.getAddressFamily());
490 ConstrainedPath ctsPath = computeSimplePath(source, key);
491 if (ctsPath.getStatus() != ComputationStatus.Completed) {
495 /* Then, loop other subsequent Include Route address */
496 while (it.hasNext()) {
497 VertexKey next = getVertexKey(it.next(), constraints.getAddressFamily());
498 ctsPath = mergePath(ctsPath, computeSimplePath(key, next));
499 if (ctsPath.getStatus() != ComputationStatus.Completed) {
505 /* Finish path up to the destination */
506 return mergePath(ctsPath, computeSimplePath(key, destination));
509 protected abstract ConstrainedPath computeSimplePath(VertexKey source, VertexKey destination);