Bump upstreams
[bgpcep.git] / pcep / server / server-provider / src / main / java / org / opendaylight / bgpcep / pcep / server / provider / ManagedTePath.java
1 /*
2  * Copyright (c) 2021 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.bgpcep.pcep.server.provider;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import java.nio.ByteBuffer;
19 import java.util.Collections;
20 import java.util.List;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.opendaylight.graph.ConnectedEdge;
23 import org.opendaylight.graph.ConnectedEdgeTrigger;
24 import org.opendaylight.graph.ConnectedGraph;
25 import org.opendaylight.graph.ConnectedVertex;
26 import org.opendaylight.graph.ConnectedVertexTrigger;
27 import org.opendaylight.mdsal.binding.api.WriteTransaction;
28 import org.opendaylight.mdsal.common.api.CommitInfo;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6AddressNoZone;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.Edge;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.Vertex;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.edge.attributes.UnreservedBandwidth;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ieee754.rev130819.Float32;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.network.concepts.rev131125.Bandwidth;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.network.topology.rev140113.NetworkTopologyRef;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.AddressFamily;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.ComputationStatus;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.path.descriptions.PathDescription;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Arguments2Builder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Arguments3Builder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.LspBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.PathComputationClient1;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.PathStatus;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.PathType;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.ConfiguredLsp;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.ConfiguredLspBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.configured.lsp.ComputedPath;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.configured.lsp.IntendedPath;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.configured.lsp.intended.path.Constraints;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.bandwidth.object.BandwidthBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.Ipv4CaseBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.Ipv6CaseBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.ipv4._case.Ipv4Builder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.ipv6._case.Ipv6Builder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.object.EndpointsObjBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.lsp.attributes.MetricsBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.metric.object.MetricBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.path.setup.type.tlv.PathSetupTypeBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.AddLsp;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.AddLspInput;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.AddLspInputBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.AddLspOutput;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.Node1;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.RemoveLsp;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.RemoveLspInput;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.RemoveLspInputBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.RemoveLspOutput;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.UpdateLsp;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.UpdateLspInput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.UpdateLspInputBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.UpdateLspOutput;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.add.lsp.args.ArgumentsBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.pcep.client.attributes.PathComputationClient;
77 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
78 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
79 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
80 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
81 import org.opendaylight.yangtools.yang.binding.CodeHelpers;
82 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
83 import org.opendaylight.yangtools.yang.common.RpcResult;
84 import org.opendaylight.yangtools.yang.common.Uint8;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
87
88 public class ManagedTePath implements ConnectedEdgeTrigger, ConnectedVertexTrigger {
89
90     private ConfiguredLsp cfgLsp = null;
91     private ConfiguredLsp prevLsp = null;
92     private final ManagedTeNode teNode;
93     private boolean sent = false;
94     private boolean triggerFlag = false;
95     private PathType type = PathType.Pcc;
96     private final InstanceIdentifier<Topology> pcepTopology;
97     private final InstanceIdentifier<PathComputationClient1> pccIdentifier;
98
99     private static final Logger LOG = LoggerFactory.getLogger(ManagedTePath.class);
100
101     public ManagedTePath(final ManagedTeNode teNode, final InstanceIdentifier<Topology> topology) {
102         this.teNode = requireNonNull(teNode);
103         pcepTopology = requireNonNull(topology);
104         pccIdentifier = pcepTopology.child(Node.class, new NodeKey(teNode.getId())).augmentation(Node1.class)
105                 .child(PathComputationClient.class).augmentation(PathComputationClient1.class);
106     }
107
108     public ManagedTePath(final ManagedTeNode teNode, final ConfiguredLsp lsp,
109             final InstanceIdentifier<Topology> topology) {
110         cfgLsp = requireNonNull(lsp);
111         this.teNode = requireNonNull(teNode);
112         pcepTopology = requireNonNull(topology);
113         pccIdentifier = pcepTopology.child(Node.class, new NodeKey(teNode.getId())).augmentation(Node1.class)
114                 .child(PathComputationClient.class).augmentation(PathComputationClient1.class);
115     }
116
117     public ManagedTePath(final ManagedTeNode teNode, final ManagedTePath mngPath) {
118         checkArgument(mngPath != null, "Managed TE Path is mandatory. Can't be null or empty!");
119         cfgLsp = mngPath.getLsp();
120         sent = mngPath.isSent();
121         type = mngPath.getType();
122         this.teNode = requireNonNull(teNode);
123         pcepTopology = mngPath.getTopology();
124         pccIdentifier = pcepTopology.child(Node.class, new NodeKey(teNode.getId())).augmentation(Node1.class)
125                 .child(PathComputationClient.class).augmentation(PathComputationClient1.class);
126     }
127
128     public ConfiguredLsp getLsp() {
129         return cfgLsp;
130     }
131
132     public ManagedTeNode getManagedTeNode() {
133         return teNode;
134     }
135
136     public PathType getType() {
137         return type;
138     }
139
140     public InstanceIdentifier<Topology> getTopology() {
141         return pcepTopology;
142     }
143
144     public ManagedTePath setConfiguredLsp(final ConfiguredLsp lsp) {
145         prevLsp = cfgLsp;
146         cfgLsp = lsp;
147         return this;
148     }
149
150     public ManagedTePath setType(final PathType type) {
151         this.type = type;
152         return this;
153     }
154
155     /**
156      * Mark this TE Path as synchronized and update the Data Store accordingly.
157      *
158      */
159     public void sync() {
160         cfgLsp = new ConfiguredLspBuilder(cfgLsp).setPathStatus(PathStatus.Sync).build();
161         updateToDataStore();
162     }
163
164     /**
165      * Disabling this TE Path by marking it as Configured. Do not update the Data Store.
166      */
167     public void disabled() {
168         cfgLsp = new ConfiguredLspBuilder(cfgLsp).setPathStatus(PathStatus.Configured).build();
169     }
170
171     /**
172      * Mark this TE Path as Failed.
173      */
174     public void failed() {
175         cfgLsp = new ConfiguredLspBuilder(cfgLsp).setPathStatus(PathStatus.Failed).build();
176         updateToDataStore();
177     }
178
179     public boolean isSent() {
180         return sent;
181     }
182
183     /**
184      * Compare the current TE Path against the reported LSP to determine if there are in Sync, need Update or
185      * considered as in Failure if already updated.
186      *
187      * @param lsp   LSP that corresponds to the reported LSP
188      *
189      * @return      new LSP status
190      */
191     public PathStatus checkReportedPath(final ConfiguredLsp lsp) {
192         /* Check if this path has not been already verified */
193         if (cfgLsp.getPathStatus() == PathStatus.Sync) {
194             return PathStatus.Sync;
195         }
196
197         /* Check Source, Destination and routing method which must be the same */
198         final IntendedPath iPath = lsp.getIntendedPath();
199         final IntendedPath rPath = lsp.getIntendedPath();
200         PathStatus newStatus = sent ? PathStatus.Failed : PathStatus.Updated;
201         if (!iPath.getSource().equals(rPath.getSource()) || !iPath.getDestination().equals(rPath.getDestination())) {
202             return PathStatus.Failed;
203         }
204
205         /* Check constraints ... */
206         final Constraints icts = iPath.getConstraints();
207         final Constraints rcts = rPath.getConstraints();
208         if (!icts.getAddressFamily().equals(rcts.getAddressFamily())) {
209             return newStatus;
210         }
211         if (icts.getBandwidth() != null && !icts.getBandwidth().equals(rcts.getBandwidth())) {
212             return newStatus;
213         }
214         /*
215          * ClassType, TE metric and Delay are not supported by all routers: need
216          * to check them only if there are all present
217          */
218         if (icts.getClassType() != null && rcts.getClassType() != null
219                         && !icts.getClassType().equals(rcts.getClassType())) {
220             return newStatus;
221         }
222         if (icts.getTeMetric() != null && rcts.getTeMetric() != null
223                 && !icts.getTeMetric().equals(rcts.getTeMetric())) {
224             return newStatus;
225         }
226         if (icts.getDelay() != null && rcts.getDelay() != null && !icts.getDelay().equals(rcts.getDelay())) {
227             return newStatus;
228         }
229
230         /* ... and Path to determine if an update is required */
231         if (lsp.getComputedPath().getComputationStatus() != ComputationStatus.Completed) {
232             return PathStatus.Failed;
233         }
234         if (!lsp.getComputedPath().getPathDescription().equals(lsp.getComputedPath().getPathDescription())) {
235             return newStatus;
236         }
237
238         /* All is conform. LSP is in sync with expected result. */
239         return PathStatus.Sync;
240     }
241
242     private void configureGraph(final ConnectedGraph graph, final ComputedPath cpath, final Constraints cts,
243             final boolean config) {
244         /* Check that Connected Graph is valid */
245         if (graph == null) {
246             return;
247         }
248
249         /* Verify that we have a valid Computed Path and that the LSP is in SYNC */
250         if (cpath.getComputationStatus() != ComputationStatus.Completed || cfgLsp.getPathStatus() != PathStatus.Sync) {
251             return;
252         }
253
254         /* Loop the path description to add reserved bandwidth and triggers for this LSP */
255         final Long bw = cts.getBandwidth() == null ? 0L : cts.getBandwidth().getValue().longValue();
256         int cos = cts.getClassType() == null ? 0 : cts.getClassType().intValue();
257         final AddressFamily af = cts.getAddressFamily();
258         final String lspId = teNode.getId().getValue() + "/" + cfgLsp.getName();
259         ConnectedEdge edge = null;
260         for (PathDescription path : cpath.getPathDescription()) {
261             edge = null;
262             switch (af) {
263                 case Ipv4:
264                 case SrIpv4:
265                     if (path.getIpv4() != null) {
266                         edge = graph.getConnectedEdge(new IpAddress(path.getIpv4()));
267                     } else if (path.getRemoteIpv4() != null) {
268                         edge = graph.getConnectedEdge(new IpAddress(path.getRemoteIpv4()));
269                         if (edgeAttrNotNull(edge)) {
270                             edge = graph.getConnectedEdge(edge.getEdge().getEdgeAttributes().getRemoteAddress());
271                         }
272                     }
273                     break;
274                 case Ipv6:
275                 case SrIpv6:
276                     if (path.getIpv6() != null) {
277                         edge = graph.getConnectedEdge(new IpAddress(path.getIpv6()));
278                     } else if (path.getRemoteIpv6() != null) {
279                         edge = graph.getConnectedEdge(new IpAddress(path.getRemoteIpv6()));
280                         if (edgeAttrNotNull(edge)) {
281                             /* Need to force using IPv6 address as Connected Edge is searched first on IPv4 address */
282                             edge = graph.getConnectedEdge(new IpAddress(
283                                     edge.getEdge().getEdgeAttributes().getRemoteAddress6()));
284                         }
285                     }
286                     break;
287                 default:
288                     break;
289             }
290
291             if (edge == null) {
292                 continue;
293             }
294
295             if (config) {
296                 if (bw != 0L) {
297                     edge.addBandwidth(bw, cos);
298                 }
299                 edge.registerTrigger(this, lspId);
300                 if (edge.getSource() != null) {
301                     edge.getSource().registerTrigger(this, lspId);
302                 }
303             } else {
304                 if (bw != 0L) {
305                     edge.delBandwidth(bw, cos);
306                 }
307                 edge.unRegisterTrigger(this, lspId);
308                 if (edge.getSource() != null) {
309                     edge.getSource().unRegisterTrigger(this, lspId);
310                 }
311             }
312         }
313         if (edge != null && edge.getDestination() != null) {
314             if (config) {
315                 edge.getDestination().registerTrigger(this, lspId);
316             } else {
317                 edge.getDestination().unRegisterTrigger(this, lspId);
318             }
319         }
320
321         /* Finally, reset Trigger Flag to activate them */
322         triggerFlag = false;
323     }
324
325     private static boolean edgeAttrNotNull(final ConnectedEdge edge) {
326         return edge != null && edge.getEdge() != null && edge.getEdge().getEdgeAttributes() != null;
327     }
328
329     public void setGraph(final ConnectedGraph graph) {
330         configureGraph(graph, cfgLsp.getComputedPath(), cfgLsp.getIntendedPath().getConstraints(), true);
331     }
332
333     public void unsetGraph(final ConnectedGraph graph) {
334         configureGraph(graph, cfgLsp.getComputedPath(), cfgLsp.getIntendedPath().getConstraints(), false);
335     }
336
337     public void updateGraph(final ConnectedGraph graph) {
338         /* First unset Bandwidth and Triggers for the old path if any */
339         if (prevLsp != null) {
340             configureGraph(graph, prevLsp.getComputedPath(), prevLsp.getIntendedPath().getConstraints(), false);
341         }
342
343         /* Then add Bandwidth and Triggers for the current path */
344         configureGraph(graph, cfgLsp.getComputedPath(), cfgLsp.getIntendedPath().getConstraints(), true);
345
346         /* And memorize current LSP for latter update */
347         prevLsp = cfgLsp;
348     }
349
350     /**
351      * Reset Triggered Flag.
352      */
353     public void unSetTriggerFlag() {
354         triggerFlag = false;
355     }
356
357     @Override
358     public boolean verifyVertex(final ConnectedVertex next, final Vertex current) {
359         /* Check if there is an on-going trigger */
360         if (triggerFlag) {
361             return false;
362         }
363
364         /* Check if Vertex has been removed */
365         Vertex vertex = next.getVertex();
366         if (vertex == null) {
367             triggerFlag = true;
368             return true;
369         }
370
371         /* Check if Vertex changed its Segment Routing Global Block */
372         final AddressFamily af = cfgLsp.getIntendedPath().getConstraints().getAddressFamily();
373         if ((af == AddressFamily.SrIpv4 || af == AddressFamily.SrIpv6) && !current.getSrgb().equals(vertex.getSrgb())) {
374             LOG.debug("Vertex {} modified its SRGB {} / {}", vertex.getName(), current.getSrgb(), vertex.getSrgb());
375             triggerFlag = true;
376             return true;
377         }
378
379         /* All is fine */
380         triggerFlag = false;
381         return false;
382     }
383
384     @Override
385     public boolean verifyEdge(final ConnectedEdge next, final Edge current) {
386         /* Check if there is an on-going trigger */
387         if (triggerFlag) {
388             return false;
389         }
390
391         /* Check if Edge or Attributes has been removed */
392         Edge edge = next.getEdge();
393         if (edge == null || edge.getEdgeAttributes() == null) {
394             triggerFlag = true;
395             return true;
396         }
397
398         /* Check that Configured LSP has valid constraints */
399         final Constraints constraints = cfgLsp.getIntendedPath().getConstraints();
400         if (constraints == null) {
401             return false;
402         }
403
404         /* Check if Metric is always met */
405         Long metric = 0L;
406         Long delta = 0L;
407         if (constraints.getDelay() != null) {
408             if (edge.getEdgeAttributes().getDelay() != null) {
409                 metric = constraints.getDelay().getValue().longValue();
410                 delta = edge.getEdgeAttributes().getDelay().getValue().longValue()
411                         - current.getEdgeAttributes().getDelay().getValue().longValue();
412             } else {
413                 triggerFlag = true;
414                 return true;
415             }
416         }
417         if (constraints.getTeMetric() != null) {
418             if (edge.getEdgeAttributes().getTeMetric() != null) {
419                 metric = constraints.getTeMetric().longValue();
420                 delta = edge.getEdgeAttributes().getTeMetric().longValue()
421                         - current.getEdgeAttributes().getTeMetric().longValue();
422             } else {
423                 triggerFlag = true;
424                 return true;
425             }
426         } else if (constraints.getMetric() != null) {
427             if (edge.getEdgeAttributes().getMetric() != null) {
428                 metric = constraints.getMetric().longValue();
429                 delta = edge.getEdgeAttributes().getMetric().longValue()
430                         - current.getEdgeAttributes().getMetric().longValue();
431             } else {
432                 triggerFlag = true;
433                 return true;
434             }
435         }
436         if (metric != 0L && cfgLsp.getComputedPath().getComputedMetric() != null
437                 && cfgLsp.getComputedPath().getComputedMetric().longValue() + delta > metric) {
438             LOG.debug("Following an update on Edge {} Metric is no longer guaranteed: {} / {}",
439                     edge.getName(),
440                     cfgLsp.getComputedPath().getComputedMetric().longValue() + delta,
441                     metric);
442             triggerFlag = true;
443             return true;
444         }
445
446         /* Check if Bandwidth is always met */
447         if (constraints.getBandwidth() != null) {
448             if (edge.getEdgeAttributes().getMaxLinkBandwidth() == null
449                     || edge.getEdgeAttributes().getMaxResvLinkBandwidth() == null
450                     || edge.getEdgeAttributes().getUnreservedBandwidth() == null) {
451                 triggerFlag = true;
452                 return true;
453             }
454             Long bandwidth = constraints.getBandwidth().getValue().longValue();
455             Long unrsv = 0L;
456             int cos = 0;
457             for (UnreservedBandwidth unResBw : edge.getEdgeAttributes().getUnreservedBandwidth()) {
458                 if (unResBw.getClassType().intValue() == cos) {
459                     unrsv = unResBw.getBandwidth().getValue().longValue();
460                     break;
461                 }
462             }
463             Long maxBW = edge.getEdgeAttributes().getMaxLinkBandwidth().getValue().longValue();
464             if (bandwidth > List.of(
465                     unrsv,
466                     /* maxBW might be on the list but will always be greater than the next items */
467                     maxBW - next.getCosResvBandwidth(cos),
468                     maxBW - next.getGlobalResvBandwidth(),
469                     edge.getEdgeAttributes().getMaxResvLinkBandwidth().getValue().longValue())
470                     .stream().mapToLong(v -> v)
471                     .min().getAsLong()
472             ) {
473                 LOG.debug("Following an update on Edge {}, Reserved bandwidth is no longer guaranteed", edge.getName());
474                 triggerFlag = true;
475                 return true;
476             }
477         }
478
479         /* Check if Edge changed its Adjacency SID */
480         final AddressFamily af = cfgLsp.getIntendedPath().getConstraints().getAddressFamily();
481         if ((af == AddressFamily.SrIpv4 || af == AddressFamily.SrIpv6)
482                 && !current.getEdgeAttributes().getAdjSid().equals(edge.getEdgeAttributes().getAdjSid())) {
483             LOG.debug("Edge {} has modified its Adjacency SID", edge.getName());
484             triggerFlag = true;
485             return true;
486         }
487
488         return false;
489     }
490
491     /**
492      * Convert LSP as Add LSP Input class to enforce it into the PCC through a call to add-lsp RPCs.
493      *
494      * @return  new Add LSP Input
495      */
496     private AddLspInput getAddLspInput() {
497         /* Create EndPoint Object */
498         final IntendedPath iPath = cfgLsp.getIntendedPath();
499         final EndpointsObjBuilder epb = new EndpointsObjBuilder()
500                 .setIgnore(false)
501                 .setProcessingRule(true);
502         if (iPath.getSource().getIpv4Address() != null) {
503             final Ipv4Builder ipBuilder = new Ipv4Builder()
504                     .setDestinationIpv4Address(new Ipv4AddressNoZone(iPath.getDestination().getIpv4Address()))
505                     .setSourceIpv4Address(new Ipv4AddressNoZone(iPath.getSource().getIpv4Address()));
506             epb.setAddressFamily(new Ipv4CaseBuilder().setIpv4(ipBuilder.build()).build());
507         } else if (cfgLsp.getIntendedPath().getSource().getIpv6Address() != null) {
508             final Ipv6Builder ipBuilder = new Ipv6Builder()
509                     .setDestinationIpv6Address(new Ipv6AddressNoZone(iPath.getDestination().getIpv6Address()))
510                     .setSourceIpv6Address(new Ipv6AddressNoZone(iPath.getSource().getIpv6Address()));
511             epb.setAddressFamily(new Ipv6CaseBuilder().setIpv6(ipBuilder.build()).build());
512         } else {
513             // In case of ...
514             return null;
515         }
516
517         /* Create Path Setup Type */
518         final PathSetupTypeBuilder pstBuilder = new PathSetupTypeBuilder();
519         switch (iPath.getConstraints().getAddressFamily()) {
520             case SrIpv4:
521             case SrIpv6:
522                 pstBuilder.setPst(Uint8.ONE);
523                 break;
524             default:
525                 pstBuilder.setPst(Uint8.ZERO);
526                 break;
527         }
528
529         /* Create LSP */
530         final LspBuilder lspBuilder = new LspBuilder()
531                 .setAdministrative(true)
532                 .setDelegate(true);
533
534         /* Build Arguments. */
535         final ArgumentsBuilder args = new ArgumentsBuilder()
536                 .setEndpointsObj(epb.build())
537                 .setEro(MessagesUtil.getEro(cfgLsp.getComputedPath().getPathDescription()))
538                 .addAugmentation(new Arguments2Builder()
539                         .setLsp(lspBuilder.build())
540                         .setPathSetupType(pstBuilder.build())
541                         .build());
542
543         /* with Bandwidth and Metric if defined */
544         if (iPath.getConstraints().getBandwidth() != null) {
545             final int ftoi = Float.floatToIntBits(iPath.getConstraints().getBandwidth().getValue().floatValue());
546             final byte[] itob = { (byte) (0xFF & ftoi >> 24), (byte) (0xFF & ftoi >> 16), (byte) (0xFF & ftoi >> 8),
547                 (byte) (0xFF & ftoi) };
548             args.setBandwidth(new BandwidthBuilder().setBandwidth(new Bandwidth(itob)).build());
549         }
550         /* Note that Delay are not set because, at least, Juniper Routers don't support them */
551         if (iPath.getConstraints().getTeMetric() != null) {
552             final MetricBuilder metricBuilder = new MetricBuilder()
553                     .setComputed(true)
554                     .setMetricType(Uint8.TWO)
555                     .setValue(new Float32(ByteBuffer.allocate(4)
556                             .putFloat(iPath.getConstraints().getTeMetric().floatValue()).array()));
557             args.setMetrics(Collections.singletonList(new MetricsBuilder().setMetric(metricBuilder.build()).build()));
558         } else if (iPath.getConstraints().getMetric() != null) {
559             final MetricBuilder metricBuilder = new MetricBuilder()
560                     .setComputed(true)
561                     .setMetricType(Uint8.ONE)
562                     .setValue(new Float32(
563                             ByteBuffer.allocate(4).putFloat(iPath.getConstraints().getMetric().floatValue()).array()));
564             args.setMetrics(Collections.singletonList(new MetricsBuilder().setMetric(metricBuilder.build()).build()));
565         }
566
567         /*
568          * NOTE: Seems that ClassType is not supported by some routers. Skip it for the moment.
569          */
570         /* With Class Type if defined
571         if (iPath.getConstraints().getClassType() != null) {
572             args.setClassType(
573                 new ClassTypeBuilder()
574                     .setClassType(new ClassType(iPath.getConstraints().getClassType()))
575                     .setIgnore(false)
576                     .setProcessingRule(true)
577                     .build());
578         }
579         */
580
581         /* Finally, build addLSP input */
582         return new AddLspInputBuilder()
583                 .setNode(teNode.getId())
584                 .setName(cfgLsp.getName())
585                 .setArguments(args.build())
586                 .setNetworkTopologyRef(new NetworkTopologyRef(pcepTopology))
587                 .build();
588     }
589
590
591     /**
592      * Call add-lsp RPC to enforce the LSP into the PCC. This action will trigger a PcInitiate message to the PCC.
593      *
594      * @param addLsp Add Lsp RPC
595      *
596      * @return      Add LSP Output to convey the RPC result
597      */
598     public ListenableFuture<RpcResult<AddLspOutput>> addPath(final AddLsp addLsp) {
599         /* Check if we could add this path */
600         if (type != PathType.Initiated || !teNode.isSync()) {
601             return null;
602         }
603
604         /* Check if we have a valid Path */
605         if (cfgLsp.getComputedPath().getComputationStatus() != ComputationStatus.Completed) {
606             return null;
607         }
608
609         sent = true;
610         final var enforce = addLsp.invoke(getAddLspInput());
611         LOG.info("Call Add LSP to {} with {}", addLsp, enforce);
612         Futures.addCallback(enforce, new FutureCallback<>() {
613             @Override
614             public void onSuccess(final RpcResult<AddLspOutput> result) {
615                 if (result.isSuccessful()) {
616                     LOG.debug("Enforce LSP success {}", result.getResult());
617                 } else {
618                     LOG.debug("Unable to enforce LSP {} on Node {}: Got error {}", cfgLsp.getName(), teNode.getId(),
619                             result.getErrors());
620                 }
621                 sent = false;
622             }
623
624             @Override
625             public void onFailure(final Throwable throwable) {
626                 LOG.warn("Failed enforce LSP {} on Node {}", cfgLsp.getName(), teNode.getId());
627                 sent = false;
628             }
629         }, MoreExecutors.directExecutor());
630
631         return enforce;
632     }
633
634     /**
635      * Convert LSP as Update LSP Input class to update it into the PCC through a call to update-lsp RPCs.
636      *
637      * @return  new Update LSP Input
638      */
639     private @NonNull UpdateLspInput getUpdateLspInput() {
640         /* Create Path Setup Type */
641         final IntendedPath iPath = cfgLsp.getIntendedPath();
642         final PathSetupTypeBuilder pstBuilder = new PathSetupTypeBuilder();
643         switch (iPath.getConstraints().getAddressFamily()) {
644             case SrIpv4:
645             case SrIpv6:
646                 pstBuilder.setPst(Uint8.ONE);
647                 break;
648             default:
649                 pstBuilder.setPst(Uint8.ZERO);
650                 break;
651         }
652
653         /* Create LSP */
654         final LspBuilder lspBuilder = new LspBuilder()
655                 .setAdministrative(true)
656                 .setDelegate(true);
657
658         /* Build Arguments */
659         final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730
660             .update.lsp.args.ArgumentsBuilder args;
661         args = new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730
662             .update.lsp.args.ArgumentsBuilder()
663                 .addAugmentation(new Arguments3Builder()
664                     .setLsp(lspBuilder.build())
665                     .setPathSetupType(pstBuilder.build())
666                     .build())
667                 .setEro(MessagesUtil.getEro(cfgLsp.getComputedPath().getPathDescription()));
668
669         /*  with Bandwidth and Metric if defined */
670         if (iPath.getConstraints().getBandwidth() != null) {
671             final int ftoi = Float.floatToIntBits(iPath.getConstraints().getBandwidth().getValue().floatValue());
672             final byte[] itob = { (byte) (0xFF & ftoi >> 24), (byte) (0xFF & ftoi >> 16), (byte) (0xFF & ftoi >> 8),
673                 (byte) (0xFF & ftoi) };
674             args.setBandwidth(new BandwidthBuilder().setBandwidth(new Bandwidth(itob)).build());
675         }
676         /* Note that Delay are not set because, at least, Juniper Routers don't support them */
677         if (iPath.getConstraints().getTeMetric() != null) {
678             final MetricBuilder metricBuilder = new MetricBuilder()
679                     .setComputed(true)
680                     .setMetricType(Uint8.TWO)
681                     .setValue(new Float32(ByteBuffer.allocate(4)
682                             .putFloat(iPath.getConstraints().getTeMetric().floatValue()).array()));
683             args.setMetrics(Collections.singletonList(new MetricsBuilder().setMetric(metricBuilder.build()).build()));
684         } else if (iPath.getConstraints().getMetric() != null) {
685             final MetricBuilder metricBuilder = new MetricBuilder()
686                     .setComputed(true)
687                     .setMetricType(Uint8.ONE)
688                     .setValue(new Float32(ByteBuffer.allocate(4)
689                             .putFloat(iPath.getConstraints().getMetric().floatValue()).array()));
690             args.setMetrics(Collections.singletonList(new MetricsBuilder().setMetric(metricBuilder.build()).build()));
691         }
692
693         /*
694          * NOTE: Seems that ClassType is not supported by some routers. Skip it for the moment.
695          */
696         /* With Class Type if defined
697         if (iPath.getConstraints().getClassType() != null) {
698             args.setClassType(
699                 new ClassTypeBuilder()
700                     .setClassType(new ClassType(iPath.getConstraints().getClassType()))
701                     .setIgnore(false)
702                     .setProcessingRule(true)
703                     .build());
704         }
705         */
706
707         /* Finally, build updateLSP input */
708         return new UpdateLspInputBuilder()
709                 .setNode(teNode.getId())
710                 .setName(cfgLsp.getName())
711                 .setArguments(args.build())
712                 .setNetworkTopologyRef(new NetworkTopologyRef(pcepTopology))
713                 .build();
714     }
715
716     /**
717      * Call update-lsp RPC to enforce the LSP into the PCC. This action will trigger a PcUpdate message to the PCC.
718      *
719      * @param updateLsp  Update LSP RPC
720      *
721      * @return      Update LSP Output to convey the RPC result
722      */
723     public ListenableFuture<RpcResult<UpdateLspOutput>> updatePath(final UpdateLsp updateLsp) {
724
725         /* Check if we could update this path */
726         if (type != PathType.Initiated && type != PathType.Delegated || !teNode.isSync()) {
727             return null;
728         }
729
730         /* Check if we have a valid ERO */
731         if (cfgLsp.getComputedPath().getComputationStatus() != ComputationStatus.Completed) {
732             return null;
733         }
734
735         sent = true;
736         final NodeId id = teNode.getId();
737         final var enforce = updateLsp.invoke(getUpdateLspInput());
738         LOG.info("Call Update LSP to {} with {}", updateLsp, enforce);
739         Futures.addCallback(enforce, new FutureCallback<>() {
740             @Override
741             public void onSuccess(final RpcResult<UpdateLspOutput> result) {
742                 if (result.isSuccessful()) {
743                     LOG.debug("Update LSP success {}", result.getResult());
744                 } else {
745                     LOG.debug("Unable to update LSP {} on Node {}: Got error {}", cfgLsp.getName(), id,
746                             result.getErrors());
747                 }
748                 sent = false;
749             }
750
751             @Override
752             public void onFailure(final Throwable throwable) {
753                 LOG.warn("Failed update LSP {} on Node {}", cfgLsp.getName(), id);
754                 sent = false;
755             }
756         }, MoreExecutors.directExecutor());
757
758         return enforce;
759     }
760
761     /**
762      * Call remove-lsp RPC to remove the LSP from the PCC. This action will trigger a PcInitiate message to the PCC
763      * with 'R' bit set.
764      *
765      * @param removeLsp Remove Lsp RPC
766      *
767      * @return      Remove LSP Output to convey the RPC result
768      */
769     public ListenableFuture<RpcResult<RemoveLspOutput>> removePath(final RemoveLsp removeLsp) {
770
771         /* Check if we could remove this path */
772         if (type != PathType.Initiated || !teNode.isSync() || cfgLsp.getPathStatus() != PathStatus.Sync) {
773             return null;
774         }
775
776         sent = true;
777         final NodeId id = teNode.getId();
778         final RemoveLspInput rli = new RemoveLspInputBuilder()
779                 .setNode(id)
780                 .setName(cfgLsp.getName())
781                 .setNetworkTopologyRef(new NetworkTopologyRef(pcepTopology))
782                 .build();
783         final ListenableFuture<RpcResult<RemoveLspOutput>> enforce = removeLsp.invoke(rli);
784         LOG.info("Call Remove LSP to {} with {}", removeLsp, enforce);
785         Futures.addCallback(enforce, new FutureCallback<RpcResult<RemoveLspOutput>>() {
786             @Override
787             public void onSuccess(final RpcResult<RemoveLspOutput> result) {
788                 if (result.isSuccessful()) {
789                     LOG.debug("Delete LSP success {}", result.getResult());
790                 } else {
791                     LOG.debug("Unable to delete LSP {} on Node {}: Got error {}", cfgLsp.getName(), id,
792                             result.getErrors());
793                 }
794                 sent = false;
795             }
796
797             @Override
798             public void onFailure(final Throwable throwable) {
799                 LOG.warn("Failed delete LSP {} on Node {}", cfgLsp.getName(), id);
800                 sent = false;
801             }
802         }, MoreExecutors.directExecutor());
803
804         return enforce;
805     }
806
807     /**
808      * Add LSP components to the Operational Data Store.
809      */
810     public synchronized void addToDataStore() {
811         /* Check if we could add this path */
812         if (!teNode.isSync()) {
813             return;
814         }
815
816         final WriteTransaction trans = teNode.getTransaction();
817         trans.put(LogicalDatastoreType.OPERATIONAL, pccIdentifier.child(ConfiguredLsp.class, cfgLsp.key()), cfgLsp);
818         trans.commit().addCallback(new FutureCallback<CommitInfo>() {
819             @Override
820             public void onSuccess(final CommitInfo result) {
821                 LOG.debug("Configured LSP {} has been published in operational datastore ", cfgLsp.getName());
822             }
823
824             @Override
825             public void onFailure(final Throwable throwable) {
826                 LOG.error("Cannot write Configured LSP {} to the operational datastore (transaction: {})",
827                         cfgLsp.getName(), trans.getIdentifier());
828             }
829         }, MoreExecutors.directExecutor());
830     }
831
832     /**
833      * Update LSP components to the Data Store.
834      */
835     public synchronized void updateToDataStore() {
836         /* Check if we could update this path */
837         if (!teNode.isSync()) {
838             return;
839         }
840
841         final WriteTransaction trans = teNode.getTransaction();
842         trans.merge(LogicalDatastoreType.OPERATIONAL, pccIdentifier.child(ConfiguredLsp.class, cfgLsp.key()), cfgLsp);
843         trans.commit().addCallback(new FutureCallback<CommitInfo>() {
844             @Override
845             public void onSuccess(final CommitInfo result) {
846                 LOG.debug("Configured LSP {} has been updated in operational datastore ", cfgLsp.getName());
847             }
848
849             @Override
850             public void onFailure(final Throwable throwable) {
851                 LOG.error("Cannot update Configured LSP {} to the operational datastore (transaction: {})",
852                         cfgLsp.getName(), trans.getIdentifier());
853             }
854         }, MoreExecutors.directExecutor());
855     }
856
857     /**
858      * Remove LSP components to the Data Store.
859      */
860     public synchronized void removeFromDataStore() {
861         /* Check if we could remove this path */
862         if (!teNode.isSync()) {
863             return;
864         }
865
866         final WriteTransaction trans = teNode.getTransaction();
867         trans.delete(LogicalDatastoreType.OPERATIONAL, pccIdentifier.child(ConfiguredLsp.class, cfgLsp.key()));
868         trans.commit().addCallback(new FutureCallback<CommitInfo>() {
869             @Override
870             public void onSuccess(final CommitInfo result) {
871                 LOG.debug("Configured LSP {} has been deleted in operational datastore ", cfgLsp.getName());
872             }
873
874             @Override
875             public void onFailure(final Throwable throwable) {
876                 LOG.error("Cannot delete Configured LSP {} from the operational datastore (transaction: {})",
877                         cfgLsp.getName(), trans.getIdentifier());
878             }
879         }, MoreExecutors.directExecutor());
880     }
881
882     @Override
883     public String toString() {
884         final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper("ManagedTePath");
885         CodeHelpers.appendValue(helper, "ConfiguredLsp", cfgLsp);
886         CodeHelpers.appendValue(helper, "PathType", type);
887         CodeHelpers.appendValue(helper, "Sent", sent);
888         return helper.toString();
889     }
890
891 }