Bump upstreams
[bgpcep.git] / pcep / server / server-provider / src / main / java / org / opendaylight / bgpcep / pcep / server / provider / PcepTopologyListener.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 java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.Iterables;
13 import java.nio.ByteBuffer;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.stream.Collectors;
17 import org.opendaylight.mdsal.binding.api.DataBroker;
18 import org.opendaylight.mdsal.binding.api.DataObjectModification;
19 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
20 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
21 import org.opendaylight.mdsal.binding.api.DataTreeModification;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.DecimalBandwidth;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.Delay;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.AddressFamily;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.ComputationStatus;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.path.descriptions.PathDescription;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.path.computation.rev220324.path.descriptions.PathDescriptionBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.Lsp1;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.OperationalStatus;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Path1;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.identifiers.tlv.lsp.identifiers.address.family.Ipv4Case;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.identifiers.tlv.lsp.identifiers.address.family.Ipv6Case;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.Lsp;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.segment.routing.rev200720.SrSubobject;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.segment.routing.rev200720.sr.subobject.nai.IpAdjacency;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.segment.routing.rev200720.sr.subobject.nai.IpNodeId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.PathStatus;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.PathType;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.ConfiguredLsp;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.ConfiguredLspBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.ConfiguredLspKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.configured.lsp.ComputedPathBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.configured.lsp.IntendedPathBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.server.rev220321.pcc.configured.lsp.configured.lsp.intended.path.ConstraintsBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.explicit.route.object.Ero;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.reported.route.object.Rro;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820.basic.explicit.route.subobjects.subobject.type.IpPrefixCase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.Node1;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.PccSyncState;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.pcep.client.attributes.PathComputationClient;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.pcep.client.attributes.path.computation.client.ReportedLsp;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.pcep.client.attributes.path.computation.client.reported.lsp.Path;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
61 import org.opendaylight.yangtools.concepts.Registration;
62 import org.opendaylight.yangtools.yang.binding.DataObject;
63 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
64 import org.opendaylight.yangtools.yang.common.Decimal64;
65 import org.opendaylight.yangtools.yang.common.Uint32;
66 import org.opendaylight.yangtools.yang.common.Uint8;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 /**
71  * This Class listen to the PCEP Topology in order to trigger Path Manager methods to control Managed TE Path.
72  *
73  * @author Olivier Dugeon
74  */
75
76 public final class PcepTopologyListener implements DataTreeChangeListener<Node>, AutoCloseable {
77     private static final Logger LOG = LoggerFactory.getLogger(PcepTopologyListener.class);
78
79     private final PathManagerProvider pathManager;
80
81     private Registration listenerRegistration;
82
83     public PcepTopologyListener(final DataBroker dataBroker,
84             final KeyedInstanceIdentifier<Topology, TopologyKey> topology, final PathManagerProvider pathManager) {
85         this.pathManager = requireNonNull(pathManager);
86         listenerRegistration = dataBroker.registerLegacyTreeChangeListener(
87                 DataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, topology.child(Node.class)), this);
88         LOG.info("Registered PCE Server listener {} for Operational PCEP Topology {}",
89                 listenerRegistration, topology.getKey().getTopologyId().getValue());
90     }
91
92     /**
93      * Close this Listener.
94      */
95     @Override
96     public void close() {
97         if (listenerRegistration != null) {
98             LOG.info("Unregistered PCE Server listener {} for Operational PCEP Topology", listenerRegistration);
99             listenerRegistration.close();
100             listenerRegistration = null;
101         }
102     }
103
104     /**
105      * Handle reported LSP modifications.
106      *
107      * @param nodeId    Node Identifier to which the modified children belongs to.
108      * @param lspMod    List of Reported LSP modifications.
109      */
110     private void handleLspChange(final NodeId nodeId, final List<DataObjectModification<?>> lspMod) {
111         for (DataObjectModification<?> lsp : lspMod) {
112             ReportedLsp rptLsp;
113
114             switch (lsp.modificationType()) {
115                 case DELETE:
116                     rptLsp = (ReportedLsp) lsp.dataBefore();
117                     LOG.debug("Un-Register Managed TE Path: {}", rptLsp.getName());
118                     pathManager.unregisterTePath(nodeId, new ConfiguredLspKey(rptLsp.getName()));
119                     break;
120                 case SUBTREE_MODIFIED:
121                 case WRITE:
122                     rptLsp = (ReportedLsp) lsp.dataAfter();
123                     LOG.debug("Register Managed TE Path {}", rptLsp.getName());
124                     pathManager.registerTePath(nodeId,  getConfiguredLsp(rptLsp), getPathType(rptLsp));
125                     break;
126                 default:
127                     break;
128             }
129         }
130     }
131
132     /**
133      * Parse Sub Tree modification. Given list has been filtered to get only Path Computation Client modifications.
134      * This function first create, update or delete Managed TE Node that corresponds to the given NodeId. Then, it
135      * filter the children to retain only the reported LSP modifications.
136      *
137      * @param nodeId    Node Identifier to which the modified children belongs to.
138      * @param pccMod    List of Path Computation Client modifications.
139      */
140     private void handlePccChange(final NodeId nodeId, final List<DataObjectModification<?>> pccMod) {
141         for (DataObjectModification<?> node : pccMod) {
142             /* First, process PCC modification */
143             switch (node.modificationType()) {
144                 case DELETE:
145                     LOG.debug("Un-Register Managed TE Node: {}", nodeId);
146                     pathManager.disableManagedTeNode(nodeId);
147                     /* Should stop here to avoid deleting later the associated Managed TE Path */
148                     return;
149                 case SUBTREE_MODIFIED:
150                 case WRITE:
151                     /* First look if the PCC was already created or not yet */
152                     if (pathManager.checkManagedTeNode(nodeId)) {
153                         /* Check if PCC State is Synchronized */
154                         if (node.modifiedChildren() == null || node.modifiedChildren().isEmpty()) {
155                             PathComputationClient pcc = (PathComputationClient) node.dataAfter();
156                             if (pcc.getStateSync() == PccSyncState.Synchronized) {
157                                 LOG.debug("Synchronize Managed TE Node {}", nodeId);
158                                 pathManager.syncManagedTeNode(nodeId);
159                             }
160                             return;
161                         }
162                     } else {
163                         LOG.debug("Register new Managed TE Node {}", nodeId);
164                         pathManager.registerManagedTeNode(nodeId);
165                     }
166                     break;
167                 default:
168                     break;
169             }
170
171             /* Then, look to reported LSP modification */
172             final List<DataObjectModification<? extends DataObject>> lspMod = node.modifiedChildren()
173                     .stream().filter(mod -> mod.dataType().equals(ReportedLsp.class))
174                     .collect(Collectors.toList());
175             if (!lspMod.isEmpty()) {
176                 handleLspChange(nodeId, lspMod);
177             }
178         }
179     }
180
181     /**
182      * Parse Sub Tree modification. Given children list has been filtered to get only Node1 modifications.
183      * This function filter again this given list to retain only PathComputationClient modifications.
184      *
185      * @param nodeId    Node Identifier to which the modified children belongs to.
186      * @param node1Mod  List of Node1 modifications.
187      */
188     private void handleNode1Change(final NodeId nodeId, final List<DataObjectModification<?>> node1Mod) {
189         for (DataObjectModification<?> child : node1Mod) {
190             /* Then, look only to PathComputationClient.class modification */
191             final List<DataObjectModification<?>> pccMod = child.modifiedChildren()
192                     .stream().filter(mod -> mod.dataType().equals(PathComputationClient.class))
193                     .collect(Collectors.toList());
194             if (!pccMod.isEmpty()) {
195                 handlePccChange(nodeId, pccMod);
196             }
197         }
198     }
199
200     @Override
201     public void onDataTreeChanged(final List<DataTreeModification<Node>> changes) {
202         for (var change : changes) {
203             final var root = change.getRootNode();
204
205             final NodeId nodeId =
206                 root.modificationType() == DataObjectModification.ModificationType.DELETE
207                     ? root.dataBefore().getNodeId() : root.dataAfter().getNodeId();
208
209             /* Look only to Node1.class modification */
210             final List<DataObjectModification<?>> node1Mod = root.modifiedChildren().stream()
211                     .filter(mod -> mod.dataType().equals(Node1.class))
212                     .collect(Collectors.toList());
213             if (!node1Mod.isEmpty()) {
214                 handleNode1Change(nodeId, node1Mod);
215             }
216         }
217     }
218
219     /**
220      * Translate ERO Segment Routing SubOject i.e. NaiType into Path Description.
221      *
222      * @param srObj Segment Routing SubObject.
223      * @param af    Address Family, SR-IPv4 or SR-IPv6.
224      *
225      * @return      Path Description of the corresponding ERO SubObject.
226      */
227     private static PathDescription getSrPath(final SrSubobject srObj, final AddressFamily af) {
228         return switch (af) {
229             case SrIpv4 -> switch (srObj.getNaiType()) {
230                 case Ipv4Adjacency -> new PathDescriptionBuilder()
231                     .setSid(srObj.getSid())
232                     .setIpv4(((IpAdjacency)srObj.getNai()).getLocalIpAddress().getIpv4AddressNoZone())
233                     .setRemoteIpv4(((IpAdjacency)srObj.getNai()).getRemoteIpAddress().getIpv4AddressNoZone())
234                     .build();
235                 case Ipv4NodeId -> new PathDescriptionBuilder()
236                     .setSid(srObj.getSid())
237                     .setRemoteIpv4(((IpNodeId)srObj.getNai()).getIpAddress().getIpv4AddressNoZone())
238                     .build();
239                 default -> null;
240             };
241             case SrIpv6 -> switch (srObj.getNaiType()) {
242                 case Ipv6Adjacency -> new PathDescriptionBuilder()
243                     .setSid(srObj.getSid())
244                     .setIpv6(((IpAdjacency)srObj.getNai()).getLocalIpAddress().getIpv6AddressNoZone())
245                     .setRemoteIpv6(((IpAdjacency)srObj.getNai()).getRemoteIpAddress().getIpv6AddressNoZone())
246                     .build();
247                 case Ipv6NodeId -> new PathDescriptionBuilder()
248                     .setSid(srObj.getSid())
249                     .setRemoteIpv6(((IpNodeId)srObj.getNai()).getIpAddress().getIpv6AddressNoZone())
250                     .build();
251                 default -> null;
252             };
253             default -> null;
254         };
255     }
256
257     /**
258      * Translate ERO RSVP-TE SubObject i.e. IpPrefixCase into Path Description.
259      *
260      * @param srObj Segment Routing SubObject.
261      * @param af    Address Family, SR-IPv4 or SR-IPv6.
262      *
263      * @return      Path Description of the corresponding ERO SubObject.
264      */
265     private static PathDescription getIpPath(final IpPrefixCase ipc, final AddressFamily af) {
266         return switch (af) {
267             case Ipv4 -> new PathDescriptionBuilder()
268                 .setRemoteIpv4(
269                     new Ipv4Address(ipc.getIpPrefix().getIpPrefix().getIpv4Prefix().getValue().split("/")[0]))
270                 .build();
271             case Ipv6 -> new PathDescriptionBuilder()
272                 .setRemoteIpv6(
273                     new Ipv6Address(ipc.getIpPrefix().getIpPrefix().getIpv6Prefix().getValue().split("/")[0]))
274                 .build();
275             default -> null;
276         };
277     }
278
279     /**
280      * Translate RRO RSVP-TE SubObject i.e. IpPrefixCase into Path Description.
281      *
282      * @param srObj Segment Routing SubObject.
283      * @param af    Address Family, SR-IPv4 or SR-IPv6.
284      *
285      * @return      Path Description of the corresponding RRO SubObject.
286      */
287     private static PathDescription getIpPath(final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang
288             .rsvp.rev150820._record.route.subobjects.subobject.type.IpPrefixCase ipc, final AddressFamily af) {
289         return switch (af) {
290             case Ipv4 -> new PathDescriptionBuilder()
291                 .setRemoteIpv4(
292                     new Ipv4Address(ipc.getIpPrefix().getIpPrefix().getIpv4Prefix().getValue().split("/")[0]))
293                 .build();
294             case Ipv6 -> new PathDescriptionBuilder()
295                 .setRemoteIpv6(
296                     new Ipv6Address(ipc.getIpPrefix().getIpPrefix().getIpv6Prefix().getValue().split("/")[0]))
297                 .build();
298             default -> null;
299         };
300     }
301
302     /**
303      * Translate Explicit Route Object (ERO) of the LSP into Path Description.
304      *
305      * @param ero   Explicit Route Object.
306      * @param af    Address Family, IPv4 or IPv6.
307      *
308      * @return      Path Description of the corresponding TE Path.
309      */
310     private static List<PathDescription> getPathDescription(final Ero ero, final AddressFamily af) {
311         final var pathDesc = new ArrayList<PathDescription>();
312         for (var element : ero.nonnullSubobject()) {
313             final var sbt = element.getSubobjectType();
314             if (sbt instanceof SrSubobject sr) {
315                 pathDesc.add(getSrPath(sr, af));
316             } else if (sbt instanceof IpPrefixCase ip) {
317                 pathDesc.add(getIpPath(ip, af));
318             }
319         }
320         return pathDesc.isEmpty() ? null : pathDesc;
321     }
322
323     /**
324      * Translate Record Route Object (RRO) of the LSP into a Path Description.
325      *
326      * @param rro   Record Route Object of the reported LSP.
327      * @param af    Address Family, IPv4, IPv6, SR-IPv4 or SR-IPv6
328      *
329      * @return      Path Description of the corresponding TE Path.
330      */
331     private static List<PathDescription> getPathDescription(final Rro rro, final AddressFamily af) {
332         final var pathDesc = new ArrayList<PathDescription>();
333         for (var element : rro.nonnullSubobject()) {
334             final var sbt = element.getSubobjectType();
335             if (sbt instanceof SrSubobject sr) {
336                 pathDesc.add(getSrPath(sr, af));
337             } else if (sbt
338                     instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820._record
339                         .route.subobjects.subobject.type.IpPrefixCase ip) {
340                 pathDesc.add(getIpPath(ip, af));
341             }
342         }
343         return pathDesc.isEmpty() ? null : pathDesc;
344     }
345
346     /**
347      * Build a new TE Path from a reported LSP.
348      *
349      * @param rl    Reported LSP.
350      *
351      * @return      new TE Path.
352      */
353     private static ConfiguredLsp getConfiguredLsp(final ReportedLsp rl) {
354         /* New reported LSP is always the last Path in the List i.e. old Paths are place before */
355         Path path = Iterables.getLast(rl.getPath().values());
356         Float convert;
357         ConstraintsBuilder cb = new ConstraintsBuilder();
358
359         /* Set Constraints */
360         if (path.getClassType() != null) {
361             cb.setClassType(path.getClassType().getClassType().getValue());
362         }
363         if (path.getBandwidth() != null) {
364             convert = ByteBuffer.wrap(path.getBandwidth().getBandwidth().getValue()).getFloat();
365             cb.setBandwidth(new DecimalBandwidth(Decimal64.valueOf(2, convert.longValue())));
366         }
367         if ((cb.getBandwidth() == null || cb.getBandwidth().getValue().equals(Decimal64.valueOf(2, 0)))
368                 && path.getReoptimizationBandwidth() != null) {
369             convert = ByteBuffer.wrap(path.getReoptimizationBandwidth().getBandwidth().getValue()).getFloat();
370             cb.setBandwidth(new DecimalBandwidth(Decimal64.valueOf(2, convert.longValue())));
371         }
372         if (path.getMetrics() != null) {
373             for (var metric : path.getMetrics()) {
374                 convert = ByteBuffer.wrap(metric.getMetric().getValue().getValue()).getFloat();
375                 switch (metric.getMetric().getMetricType().intValue()) {
376                     case MessagesUtil.IGP_METRIC:
377                         cb.setMetric(Uint32.valueOf(convert.longValue()));
378                         break;
379                     case MessagesUtil.TE_METRIC:
380                         cb.setTeMetric(Uint32.valueOf(convert.longValue()));
381                         break;
382                     case MessagesUtil.PATH_DELAY:
383                         cb.setDelay(new Delay(Uint32.valueOf(convert.longValue())));
384                         break;
385                     default:
386                         break;
387                 }
388             }
389         }
390
391         /* Get Source and Destination addresses and family */
392         if (path.augmentations() == null) {
393             return null;
394         }
395         final Path1 p1 = path.augmentation(Path1.class);
396         final Uint8 pst = p1.getPathSetupType() != null ? p1.getPathSetupType().getPst() : Uint8.ZERO;
397         final Lsp lsp = p1.getLsp();
398         final var af = lsp.getTlvs().getLspIdentifiers().getAddressFamily();
399         IpAddress source = null;
400         IpAddress destination = null;
401         if (af instanceof Ipv4Case v4) {
402             final var ipv4 = v4.getIpv4();
403             source = new IpAddress(ipv4.getIpv4TunnelSenderAddress());
404             destination = new IpAddress(ipv4.getIpv4TunnelEndpointAddress());
405             cb.setAddressFamily(pst == Uint8.ZERO ? AddressFamily.Ipv4 : AddressFamily.SrIpv4);
406         } else if (af instanceof Ipv6Case v6) {
407             final var ipv6 = v6.getIpv6();
408             source = new IpAddress(ipv6.getIpv6TunnelSenderAddress());
409             destination = new IpAddress(ipv6.getIpv6TunnelSenderAddress());
410             cb.setAddressFamily(pst == Uint8.ZERO ? AddressFamily.Ipv6 : AddressFamily.SrIpv6);
411         } else {
412             return null;
413         }
414
415         /* Build Intended Path */
416         final IntendedPathBuilder ipb = new IntendedPathBuilder()
417                 .setSource(source)
418                 .setDestination(destination)
419                 .setConstraints(cb.build());
420
421         /* Get a Valid Path Description for this TePath if any */
422         List<PathDescription> pathDesc = null;
423         if (path.getEro() != null
424                 && path.getEro().getSubobject() != null
425                 && path.getEro().getSubobject().size() > 0) {
426             pathDesc = getPathDescription(path.getEro(), cb.getAddressFamily());
427         }
428         if (pathDesc == null
429                 && path.getRro() != null
430                 && path.getRro().getSubobject() != null
431                 && path.getRro().getSubobject().size() > 0) {
432             pathDesc = getPathDescription(path.getRro(), cb.getAddressFamily());
433         }
434
435         ConfiguredLspBuilder clb =
436             new ConfiguredLspBuilder()
437                 .setName(rl.getName())
438                 .setPathStatus(PathStatus.Reported)
439                 .setIntendedPath(ipb.build());
440
441         /* Finally Build Actual Path and TE Path */
442         if (pathDesc == null) {
443             return clb.setComputedPath(
444                     new ComputedPathBuilder().setComputationStatus(ComputationStatus.Failed).build())
445                 .build();
446         }
447         return clb.setComputedPath(
448                 new ComputedPathBuilder()
449                     .setPathDescription(pathDesc)
450                     .setComputationStatus(
451                         lsp.getOperational() == OperationalStatus.Down
452                             ? ComputationStatus.Failed
453                             : ComputationStatus.Completed)
454                     .build())
455             .build();
456     }
457
458     /**
459      * get Path Type from a reported LSP.
460      *
461      * @param rl    Reported LSP.
462      *
463      * @return      Path Type.
464      */
465     private static PathType getPathType(final ReportedLsp rl) {
466         /* New reported LSP is always the last Path in the List i.e. old Paths are place before */
467         final Path1 p1 = Iterables.getLast(rl.getPath().values()).augmentation(Path1.class);
468         if (!p1.getLsp().getDelegate()) {
469             return PathType.Pcc;
470         }
471         final Lsp1 lspCreateFlag = p1.getLsp().augmentation(Lsp1.class);
472         if (lspCreateFlag == null || !lspCreateFlag.getCreate()) {
473             return PathType.Delegated;
474         }
475         return PathType.Initiated;
476     }
477 }
478