2 * Copyright (c) 2021 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.bgpcep.pcep.server.provider;
10 import static java.util.Objects.requireNonNull;
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;
71 * This Class listen to the PCEP Topology in order to trigger Path Manager methods to control Managed TE Path.
73 * @author Olivier Dugeon
76 public final class PcepTopologyListener implements DataTreeChangeListener<Node>, AutoCloseable {
77 private static final Logger LOG = LoggerFactory.getLogger(PcepTopologyListener.class);
79 private final PathManagerProvider pathManager;
81 private Registration listenerRegistration;
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());
93 * Close this Listener.
97 if (listenerRegistration != null) {
98 LOG.info("Unregistered PCE Server listener {} for Operational PCEP Topology", listenerRegistration);
99 listenerRegistration.close();
100 listenerRegistration = null;
105 * Handle reported LSP modifications.
107 * @param nodeId Node Identifier to which the modified children belongs to.
108 * @param lspMod List of Reported LSP modifications.
110 private void handleLspChange(final NodeId nodeId, final List<DataObjectModification<?>> lspMod) {
111 for (DataObjectModification<?> lsp : lspMod) {
114 switch (lsp.modificationType()) {
116 rptLsp = (ReportedLsp) lsp.dataBefore();
117 LOG.debug("Un-Register Managed TE Path: {}", rptLsp.getName());
118 pathManager.unregisterTePath(nodeId, new ConfiguredLspKey(rptLsp.getName()));
120 case SUBTREE_MODIFIED:
122 rptLsp = (ReportedLsp) lsp.dataAfter();
123 LOG.debug("Register Managed TE Path {}", rptLsp.getName());
124 pathManager.registerTePath(nodeId, getConfiguredLsp(rptLsp), getPathType(rptLsp));
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.
137 * @param nodeId Node Identifier to which the modified children belongs to.
138 * @param pccMod List of Path Computation Client modifications.
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()) {
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 */
149 case SUBTREE_MODIFIED:
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);
163 LOG.debug("Register new Managed TE Node {}", nodeId);
164 pathManager.registerManagedTeNode(nodeId);
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);
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.
185 * @param nodeId Node Identifier to which the modified children belongs to.
186 * @param node1Mod List of Node1 modifications.
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);
201 public void onDataTreeChanged(final List<DataTreeModification<Node>> changes) {
202 for (var change : changes) {
203 final var root = change.getRootNode();
205 final NodeId nodeId =
206 root.modificationType() == DataObjectModification.ModificationType.DELETE
207 ? root.dataBefore().getNodeId() : root.dataAfter().getNodeId();
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);
220 * Translate ERO Segment Routing SubOject i.e. NaiType into Path Description.
222 * @param srObj Segment Routing SubObject.
223 * @param af Address Family, SR-IPv4 or SR-IPv6.
225 * @return Path Description of the corresponding ERO SubObject.
227 private static PathDescription getSrPath(final SrSubobject srObj, final AddressFamily 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())
235 case Ipv4NodeId -> new PathDescriptionBuilder()
236 .setSid(srObj.getSid())
237 .setRemoteIpv4(((IpNodeId)srObj.getNai()).getIpAddress().getIpv4AddressNoZone())
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())
247 case Ipv6NodeId -> new PathDescriptionBuilder()
248 .setSid(srObj.getSid())
249 .setRemoteIpv6(((IpNodeId)srObj.getNai()).getIpAddress().getIpv6AddressNoZone())
258 * Translate ERO RSVP-TE SubObject i.e. IpPrefixCase into Path Description.
260 * @param srObj Segment Routing SubObject.
261 * @param af Address Family, SR-IPv4 or SR-IPv6.
263 * @return Path Description of the corresponding ERO SubObject.
265 private static PathDescription getIpPath(final IpPrefixCase ipc, final AddressFamily af) {
267 case Ipv4 -> new PathDescriptionBuilder()
269 new Ipv4Address(ipc.getIpPrefix().getIpPrefix().getIpv4Prefix().getValue().split("/")[0]))
271 case Ipv6 -> new PathDescriptionBuilder()
273 new Ipv6Address(ipc.getIpPrefix().getIpPrefix().getIpv6Prefix().getValue().split("/")[0]))
280 * Translate RRO RSVP-TE SubObject i.e. IpPrefixCase into Path Description.
282 * @param srObj Segment Routing SubObject.
283 * @param af Address Family, SR-IPv4 or SR-IPv6.
285 * @return Path Description of the corresponding RRO SubObject.
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) {
290 case Ipv4 -> new PathDescriptionBuilder()
292 new Ipv4Address(ipc.getIpPrefix().getIpPrefix().getIpv4Prefix().getValue().split("/")[0]))
294 case Ipv6 -> new PathDescriptionBuilder()
296 new Ipv6Address(ipc.getIpPrefix().getIpPrefix().getIpv6Prefix().getValue().split("/")[0]))
303 * Translate Explicit Route Object (ERO) of the LSP into Path Description.
305 * @param ero Explicit Route Object.
306 * @param af Address Family, IPv4 or IPv6.
308 * @return Path Description of the corresponding TE Path.
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));
320 return pathDesc.isEmpty() ? null : pathDesc;
324 * Translate Record Route Object (RRO) of the LSP into a Path Description.
326 * @param rro Record Route Object of the reported LSP.
327 * @param af Address Family, IPv4, IPv6, SR-IPv4 or SR-IPv6
329 * @return Path Description of the corresponding TE Path.
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));
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));
343 return pathDesc.isEmpty() ? null : pathDesc;
347 * Build a new TE Path from a reported LSP.
349 * @param rl Reported LSP.
351 * @return new TE Path.
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());
357 ConstraintsBuilder cb = new ConstraintsBuilder();
359 /* Set Constraints */
360 if (path.getClassType() != null) {
361 cb.setClassType(path.getClassType().getClassType().getValue());
363 if (path.getBandwidth() != null) {
364 convert = ByteBuffer.wrap(path.getBandwidth().getBandwidth().getValue()).getFloat();
365 cb.setBandwidth(new DecimalBandwidth(Decimal64.valueOf(2, convert.longValue())));
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())));
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()));
379 case MessagesUtil.TE_METRIC:
380 cb.setTeMetric(Uint32.valueOf(convert.longValue()));
382 case MessagesUtil.PATH_DELAY:
383 cb.setDelay(new Delay(Uint32.valueOf(convert.longValue())));
391 /* Get Source and Destination addresses and family */
392 if (path.augmentations() == null) {
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);
415 /* Build Intended Path */
416 final IntendedPathBuilder ipb = new IntendedPathBuilder()
418 .setDestination(destination)
419 .setConstraints(cb.build());
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());
429 && path.getRro() != null
430 && path.getRro().getSubobject() != null
431 && path.getRro().getSubobject().size() > 0) {
432 pathDesc = getPathDescription(path.getRro(), cb.getAddressFamily());
435 ConfiguredLspBuilder clb =
436 new ConfiguredLspBuilder()
437 .setName(rl.getName())
438 .setPathStatus(PathStatus.Reported)
439 .setIntendedPath(ipb.build());
441 /* Finally Build Actual Path and TE Path */
442 if (pathDesc == null) {
443 return clb.setComputedPath(
444 new ComputedPathBuilder().setComputationStatus(ComputationStatus.Failed).build())
447 return clb.setComputedPath(
448 new ComputedPathBuilder()
449 .setPathDescription(pathDesc)
450 .setComputationStatus(
451 lsp.getOperational() == OperationalStatus.Down
452 ? ComputationStatus.Failed
453 : ComputationStatus.Completed)
459 * get Path Type from a reported LSP.
461 * @param rl Reported LSP.
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()) {
471 final Lsp1 lspCreateFlag = p1.getLsp().augmentation(Lsp1.class);
472 if (lspCreateFlag == null || !lspCreateFlag.getCreate()) {
473 return PathType.Delegated;
475 return PathType.Initiated;