2 * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.tunnel.provider;
10 import java.util.HashSet;
14 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
15 import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
16 import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
17 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
18 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.ReportedLsp1;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.SymbolicPathName;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.identifiers.tlv.lsp.identifiers.AddressFamily;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.endpoints.address.family.Ipv4Case;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.endpoints.address.family.Ipv6Case;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.endpoints.address.family.ipv4._case.Ipv4;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.endpoints.address.family.ipv6._case.Ipv6;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.PathComputationClient;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.path.computation.client.ReportedLsp;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.AdministrativeStatus;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.Link1;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.Link1Builder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.SupportingNode1;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.SupportingNode1Builder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.tunnel.pcep.supporting.node.attributes.PathComputationClientBuilder;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
39 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder;
40 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNode;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeBuilder;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeKey;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1Builder;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.IgpTerminationPointAttributesBuilder;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.igp.termination.point.attributes.TerminationPointType;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.igp.termination.point.attributes.termination.point.type.Ip;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.igp.termination.point.attributes.termination.point.type.IpBuilder;
60 import org.opendaylight.yangtools.yang.binding.DataObject;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.opendaylight.yangtools.yang.common.RpcResult;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
66 import com.google.common.base.Charsets;
67 import com.google.common.base.Preconditions;
68 import com.google.common.collect.Lists;
69 import com.google.common.util.concurrent.FutureCallback;
70 import com.google.common.util.concurrent.Futures;
71 import com.google.common.util.concurrent.JdkFutureAdapters;
73 public final class NodeChangedListener implements DataChangeListener {
74 public static final Logger LOG = LoggerFactory.getLogger(NodeChangedListener.class);
75 private final InstanceIdentifier<Topology> target;
76 private final DataProviderService dataProvider;
78 NodeChangedListener(final DataProviderService dataProvider, final InstanceIdentifier<Topology> target) {
79 this.dataProvider = Preconditions.checkNotNull(dataProvider);
80 this.target = Preconditions.checkNotNull(target);
83 private static void categorizeIdentifier(final InstanceIdentifier<?> i, final Set<InstanceIdentifier<ReportedLsp>> changedLsps,
84 final Set<InstanceIdentifier<Node>> changedNodes) {
85 final InstanceIdentifier<ReportedLsp> li = i.firstIdentifierOf(ReportedLsp.class);
87 final InstanceIdentifier<Node> ni = i.firstIdentifierOf(Node.class);
89 LOG.warn("Ignoring uncategorized identifier {}", i);
98 private static void enumerateLsps(final InstanceIdentifier<Node> id, final Node node, final Set<InstanceIdentifier<ReportedLsp>> lsps) {
100 LOG.trace("Skipping null node", id);
103 final Node1 pccnode = node.getAugmentation(Node1.class);
104 if (pccnode == null) {
105 LOG.trace("Skipping non-PCEP-enabled node {}", id);
109 for (final ReportedLsp l : pccnode.getPathComputationClient().getReportedLsp()) {
110 lsps.add(InstanceIdentifier.builder(id).augmentation(Node1.class).child(PathComputationClient.class).child(ReportedLsp.class,
111 l.getKey()).toInstance());
115 private static LinkId linkIdForLsp(final InstanceIdentifier<ReportedLsp> i, final ReportedLsp lsp) {
116 return new LinkId(i.firstKeyOf(Node.class, NodeKey.class).getNodeId().getValue() + "/lsps/" + lsp.getName());
119 private InstanceIdentifier<Link> linkForLsp(final LinkId linkId) {
120 return InstanceIdentifier.builder(this.target).child(Link.class, new LinkKey(linkId)).toInstance();
123 private SupportingNode createSupportingNode(final NodeId sni, final Boolean inControl) {
124 final SupportingNodeKey sk = new SupportingNodeKey(sni);
125 final SupportingNodeBuilder snb = new SupportingNodeBuilder();
129 SupportingNode1.class,
130 new SupportingNode1Builder().setPathComputationClient(new PathComputationClientBuilder().setControlling(inControl).build()).build());
135 private InstanceIdentifier<TerminationPoint> getIpTerminationPoint(final DataModificationTransaction trans, final IpAddress addr,
136 final InstanceIdentifier<Node> sni, final Boolean inControl) {
137 for (final Node n : ((Topology) trans.readOperationalData(this.target)).getNode()) {
138 for (final TerminationPoint tp : n.getTerminationPoint()) {
139 final TerminationPoint1 tpa = tp.getAugmentation(TerminationPoint1.class);
142 final TerminationPointType tpt = tpa.getIgpTerminationPointAttributes().getTerminationPointType();
144 if (tpt instanceof Ip) {
145 for (final IpAddress a : ((Ip) tpt).getIpAddress()) {
146 if (addr.equals(a.getIpv6Address())) {
148 final NodeKey k = InstanceIdentifier.keyOf(sni);
149 boolean have = false;
152 * We may have found a termination point which has been created as a destination,
153 * so it does not have a supporting node pointer. Since we now know what it is,
156 for (final SupportingNode sn : n.getSupportingNode()) {
157 if (sn.getNodeRef().equals(k.getNodeId())) {
164 final SupportingNode sn = createSupportingNode(k.getNodeId(), inControl);
166 trans.putOperationalData(
167 InstanceIdentifier.builder(this.target).child(Node.class, n.getKey()).child(
168 SupportingNode.class, sn.getKey()).toInstance(), sn);
171 return InstanceIdentifier.builder(this.target).child(Node.class, n.getKey()).child(TerminationPoint.class,
172 tp.getKey()).toInstance();
176 LOG.debug("Ignoring termination point type {}", tpt);
182 LOG.debug("Termination point for {} not found, creating a new one", addr);
184 final String url = "ip://" + addr.toString();
185 final TerminationPointKey tpk = new TerminationPointKey(new TpId(url));
186 final TerminationPointBuilder tpb = new TerminationPointBuilder();
187 tpb.setKey(tpk).setTpId(tpk.getTpId());
189 TerminationPoint1.class,
190 new TerminationPoint1Builder().setIgpTerminationPointAttributes(
191 new IgpTerminationPointAttributesBuilder().setTerminationPointType(
192 new IpBuilder().setIpAddress(Lists.newArrayList(addr)).build()).build()).build());
194 final NodeKey nk = new NodeKey(new NodeId(url));
195 final NodeBuilder nb = new NodeBuilder();
196 nb.setKey(nk).setNodeId(nk.getNodeId());
197 nb.setTerminationPoint(Lists.newArrayList(tpb.build()));
199 nb.setSupportingNode(Lists.newArrayList(createSupportingNode(InstanceIdentifier.keyOf(sni).getNodeId(), inControl)));
202 trans.putOperationalData(InstanceIdentifier.builder(this.target).child(Node.class, nb.getKey()).toInstance(), nb.build());
203 return InstanceIdentifier.builder(this.target).child(Node.class, nb.getKey()).child(TerminationPoint.class, tpb.getKey()).toInstance();
206 private void create(final DataModificationTransaction trans, final InstanceIdentifier<ReportedLsp> i, final ReportedLsp value) {
207 final InstanceIdentifier<Node> ni = i.firstIdentifierOf(Node.class);
208 final ReportedLsp1 rl = value.getAugmentation(ReportedLsp1.class);
210 final AddressFamily af = rl.getLsp().getTlvs().getLspIdentifiers().getAddressFamily();
213 * We are trying to ensure we have source and destination nodes.
215 final IpAddress srcIp, dstIp;
216 if (af instanceof Ipv4Case) {
217 final Ipv4 ipv4 = ((Ipv4Case) af).getIpv4();
218 srcIp = new IpAddress(ipv4.getSourceIpv4Address());
219 dstIp = new IpAddress(ipv4.getDestinationIpv4Address());
220 } else if (af instanceof Ipv6Case) {
221 final Ipv6 ipv6 = ((Ipv6Case) af).getIpv6();
222 srcIp = new IpAddress(ipv6.getSourceIpv6Address());
223 dstIp = new IpAddress(ipv6.getDestinationIpv6Address());
225 throw new IllegalArgumentException("Unsupported address family: " + af.getImplementedInterface());
228 final Link1Builder lab = new Link1Builder(value.getPath().getLspa());
229 lab.setBandwidth(value.getPath().getBandwidth().getBandwidth());
230 lab.setClassType(value.getPath().getClassType().getClassType());
231 lab.setSymbolicPathName(new SymbolicPathName(value.getName().getBytes(Charsets.UTF_8)));
233 final InstanceIdentifier<TerminationPoint> dst = getIpTerminationPoint(trans, dstIp, null, Boolean.FALSE);
234 final InstanceIdentifier<TerminationPoint> src = getIpTerminationPoint(trans, srcIp, ni, rl.getLsp().isDelegate());;
235 lab.setOperationalStatus(rl.getLsp().getOperational());
236 lab.setAdministrativeStatus(rl.getLsp().isAdministrative() ? AdministrativeStatus.Active : AdministrativeStatus.Inactive);
238 final LinkId id = linkIdForLsp(i, value);
239 final LinkBuilder lb = new LinkBuilder();
242 lb.setSource(new SourceBuilder().setSourceNode(src.firstKeyOf(Node.class, NodeKey.class).getNodeId()).setSourceTp(
243 src.firstKeyOf(TerminationPoint.class, TerminationPointKey.class).getTpId()).build());
244 lb.setDestination(new DestinationBuilder().setDestNode(dst.firstKeyOf(Node.class, NodeKey.class).getNodeId()).setDestTp(
245 dst.firstKeyOf(TerminationPoint.class, TerminationPointKey.class).getTpId()).build());
246 lb.addAugmentation(Link1.class, lab.build());
248 trans.putOperationalData(linkForLsp(id), lb.build());
251 private InstanceIdentifier<TerminationPoint> tpIdentifier(final NodeId node, final TpId tp) {
252 return InstanceIdentifier.builder(this.target).child(Node.class, new NodeKey(node)).child(TerminationPoint.class, new TerminationPointKey(tp)).toInstance();
255 private InstanceIdentifier<Node> nodeIdentifier(final NodeId node) {
256 return InstanceIdentifier.builder(this.target).child(Node.class, new NodeKey(node)).toInstance();
259 private void remove(final DataModificationTransaction trans, final InstanceIdentifier<ReportedLsp> i, final ReportedLsp value) {
260 final InstanceIdentifier<Link> li = linkForLsp(linkIdForLsp(i, value));
262 final Link l = (Link) trans.readOperationalData(li);
264 LOG.debug("Removing link {} (was {})", li, l);
265 trans.removeOperationalData(li);
267 LOG.debug("Searching for orphan links/nodes");
268 final Topology t = (Topology) trans.readOperationalData(InstanceIdentifier.builder(this.target).toInstance());
270 NodeId srcNode = l.getSource().getSourceNode();
271 NodeId dstNode = l.getDestination().getDestNode();
272 TpId srcTp = l.getSource().getSourceTp();
273 TpId dstTp = l.getDestination().getDestTp();
275 boolean orphSrcNode = true, orphDstNode = true, orphDstTp = true, orphSrcTp = true;
276 for (final Link lw : t.getLink()) {
277 LOG.trace("Checking link {}", lw);
279 final NodeId sn = lw.getSource().getSourceNode();
280 final NodeId dn = lw.getDestination().getDestNode();
281 final TpId st = lw.getSource().getSourceTp();
282 final TpId dt = lw.getDestination().getDestTp();
284 // Source node checks
285 if (srcNode.equals(sn)) {
287 LOG.debug("Node {} held by source of link {}", srcNode, lw);
290 if (orphSrcTp && srcTp.equals(st)) {
291 LOG.debug("TP {} held by source of link {}", srcTp, lw);
295 if (srcNode.equals(dn)) {
297 LOG.debug("Node {} held by destination of link {}", srcNode, lw);
300 if (orphSrcTp && srcTp.equals(dt)) {
301 LOG.debug("TP {} held by destination of link {}", srcTp, lw);
306 // Destination node checks
307 if (dstNode.equals(sn)) {
309 LOG.debug("Node {} held by source of link {}", dstNode, lw);
312 if (orphDstTp && dstTp.equals(st)) {
313 LOG.debug("TP {} held by source of link {}", dstTp, lw);
317 if (dstNode.equals(dn)) {
319 LOG.debug("Node {} held by destination of link {}", dstNode, lw);
322 if (orphDstTp && dstTp.equals(dt)) {
323 LOG.debug("TP {} held by destination of link {}", dstTp, lw);
329 if (orphSrcNode && !orphSrcTp) {
330 LOG.warn("Orphan source node {} but not TP {}, retaining the node", srcNode, srcTp);
333 if (orphDstNode && !orphDstTp) {
334 LOG.warn("Orphan destination node {} but not TP {}, retaining the node", dstNode, dstTp);
339 LOG.debug("Removing orphan node {}", srcNode);
340 trans.removeOperationalData(nodeIdentifier(srcNode));
341 } else if (orphSrcTp) {
342 LOG.debug("Removing orphan TP {} on node {}", srcTp, srcNode);
343 trans.removeOperationalData(tpIdentifier(srcNode, srcTp));
346 LOG.debug("Removing orphan node {}", dstNode);
347 trans.removeOperationalData(nodeIdentifier(dstNode));
348 } else if (orphDstTp) {
349 LOG.debug("Removing orphan TP {} on node {}", dstTp, dstNode);
350 trans.removeOperationalData(tpIdentifier(dstNode, dstTp));
356 public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
357 final DataModificationTransaction trans = this.dataProvider.beginTransaction();
359 final Set<InstanceIdentifier<ReportedLsp>> lsps = new HashSet<>();
360 final Set<InstanceIdentifier<Node>> nodes = new HashSet<>();
362 // Categorize reported identifiers
363 for (final InstanceIdentifier<?> i : change.getRemovedOperationalData()) {
364 categorizeIdentifier(i, lsps, nodes);
366 for (final InstanceIdentifier<?> i : change.getUpdatedOperationalData().keySet()) {
367 categorizeIdentifier(i, lsps, nodes);
369 for (final InstanceIdentifier<?> i : change.getCreatedOperationalData().keySet()) {
370 categorizeIdentifier(i, lsps, nodes);
374 final Map<InstanceIdentifier<?>, DataObject> o = change.getOriginalOperationalData();
375 final Map<InstanceIdentifier<?>, DataObject> n = change.getUpdatedOperationalData();
377 // Now walk all nodes, check for removals/additions and cascade them to LSPs
378 for (final InstanceIdentifier<Node> i : nodes) {
379 enumerateLsps(i, (Node) o.get(i), lsps);
380 enumerateLsps(i, (Node) n.get(i), lsps);
383 // We now have list of all affected LSPs. Walk them create/remove them
384 for (final InstanceIdentifier<ReportedLsp> i : lsps) {
385 final ReportedLsp oldValue = (ReportedLsp) o.get(i);
386 final ReportedLsp newValue = (ReportedLsp) n.get(i);
388 LOG.debug("Updating lsp {} value {} -> {}", i, oldValue, newValue);
389 if (oldValue != null) {
390 remove(trans, i, oldValue);
392 if (newValue != null) {
393 create(trans, i, newValue);
397 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(trans.commit()), new FutureCallback<RpcResult<TransactionStatus>>() {
399 public void onSuccess(final RpcResult<TransactionStatus> result) {
400 LOG.trace("Topology change committed successfully");
404 public void onFailure(final Throwable t) {
405 LOG.error("Failed to propagate a topology change, target topology became inconsistent", t);
410 public static InstanceIdentifier<Link> linkIdentifier(final InstanceIdentifier<Topology> topology, final NodeId node,
412 return InstanceIdentifier.builder(topology).child(Link.class, new LinkKey(new LinkId(node.getValue() + "/lsp/" + name))).toInstance();