Bug-731: Fixed issue in NodeChangedListener
[bgpcep.git] / pcep / tunnel-provider / src / main / java / org / opendaylight / bgpcep / pcep / tunnel / provider / NodeChangedListener.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  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.tunnel.provider;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.Lists;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.JdkFutureAdapters;
16 import java.util.HashSet;
17 import java.util.Map;
18 import java.util.Set;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
21 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.AdministrativeStatus;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.Path1;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.identifiers.tlv.lsp.identifiers.AddressFamily;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.identifiers.tlv.lsp.identifiers.address.family.Ipv4Case;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.identifiers.tlv.lsp.identifiers.address.family.Ipv6Case;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.identifiers.tlv.lsp.identifiers.address.family.ipv4._case.Ipv4;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.identifiers.tlv.lsp.identifiers.address.family.ipv6._case.Ipv6;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.PathComputationClient;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.path.computation.client.ReportedLsp;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.path.computation.client.reported.lsp.Path;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.Link1;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.Link1Builder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.SupportingNode1;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.SupportingNode1Builder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.tunnel.pcep.supporting.node.attributes.PathComputationClientBuilder;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNode;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeBuilder;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeKey;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1Builder;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.IgpTerminationPointAttributesBuilder;
63 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;
64 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;
65 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;
66 import org.opendaylight.yangtools.yang.binding.DataObject;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71 public final class NodeChangedListener implements DataChangeListener {
72     private static final Logger LOG = LoggerFactory.getLogger(NodeChangedListener.class);
73     private final InstanceIdentifier<Topology> target;
74     private final DataBroker dataProvider;
75
76     NodeChangedListener(final DataBroker dataProvider, final InstanceIdentifier<Topology> target) {
77         this.dataProvider = Preconditions.checkNotNull(dataProvider);
78         this.target = Preconditions.checkNotNull(target);
79     }
80
81     private static void categorizeIdentifier(final InstanceIdentifier<?> i, final Set<InstanceIdentifier<ReportedLsp>> changedLsps,
82             final Set<InstanceIdentifier<Node>> changedNodes) {
83         final InstanceIdentifier<ReportedLsp> li = i.firstIdentifierOf(ReportedLsp.class);
84         if (li == null) {
85             final InstanceIdentifier<Node> ni = i.firstIdentifierOf(Node.class);
86             if (ni == null) {
87                 LOG.warn("Ignoring uncategorized identifier {}", i);
88             } else {
89                 changedNodes.add(ni);
90             }
91         } else {
92             changedLsps.add(li);
93         }
94     }
95
96     private static void enumerateLsps(final InstanceIdentifier<Node> id, final Node node, final Set<InstanceIdentifier<ReportedLsp>> lsps) {
97         if (node == null) {
98             LOG.trace("Skipping null node", id);
99             return;
100         }
101         final Node1 pccnode = node.getAugmentation(Node1.class);
102         if (pccnode == null) {
103             LOG.trace("Skipping non-PCEP-enabled node {}", id);
104             return;
105         }
106
107         for (final ReportedLsp l : pccnode.getPathComputationClient().getReportedLsp()) {
108             lsps.add(id.builder().augmentation(Node1.class).child(PathComputationClient.class).child(ReportedLsp.class, l.getKey()).toInstance());
109         }
110     }
111
112     private static LinkId linkIdForLsp(final InstanceIdentifier<ReportedLsp> i, final ReportedLsp lsp) {
113         return new LinkId(i.firstKeyOf(Node.class, NodeKey.class).getNodeId().getValue() + "/lsps/" + lsp.getName());
114     }
115
116     private InstanceIdentifier<Link> linkForLsp(final LinkId linkId) {
117         return this.target.child(Link.class, new LinkKey(linkId));
118     }
119
120     private SupportingNode createSupportingNode(final NodeId sni, final Boolean inControl) {
121         final SupportingNodeKey sk = new SupportingNodeKey(sni);
122         final SupportingNodeBuilder snb = new SupportingNodeBuilder();
123         snb.setNodeRef(sni);
124         snb.setKey(sk);
125         snb.addAugmentation(SupportingNode1.class, new SupportingNode1Builder().setPathComputationClient(
126                 new PathComputationClientBuilder().setControlling(inControl).build()).build());
127
128         return snb.build();
129     }
130
131     private InstanceIdentifier<TerminationPoint> getIpTerminationPoint(final ReadWriteTransaction trans, final IpAddress addr,
132             final InstanceIdentifier<Node> sni, final Boolean inControl) throws ReadFailedException {
133         final Topology topo = trans.read(LogicalDatastoreType.OPERATIONAL, this.target).checkedGet().get();
134         if (topo.getNode() != null && !topo.getNode().isEmpty()) {
135             for (final Node n : topo.getNode()) {
136                 if(n.getTerminationPoint() != null && !n.getTerminationPoint().isEmpty()) {
137                     for (final TerminationPoint tp : n.getTerminationPoint()) {
138                         final TerminationPoint1 tpa = tp.getAugmentation(TerminationPoint1.class);
139
140                         if (tpa != null) {
141                             final TerminationPointType tpt = tpa.getIgpTerminationPointAttributes().getTerminationPointType();
142
143                             if (tpt instanceof Ip) {
144                                 for (final IpAddress a : ((Ip) tpt).getIpAddress()) {
145                                     if (addr.equals(a)) {
146                                         if (sni != null) {
147                                             final NodeKey k = InstanceIdentifier.keyOf(sni);
148                                             boolean have = false;
149
150                                             /*
151                                              * We may have found a termination point which has been created as a destination,
152                                              * so it does not have a supporting node pointer. Since we now know what it is,
153                                              * fill it in.
154                                              */
155                                             if (n.getSupportingNode() != null) {
156                                                 for (final SupportingNode sn : n.getSupportingNode()) {
157                                                     if (sn.getNodeRef().equals(k.getNodeId())) {
158                                                         have = true;
159                                                         break;
160                                                     }
161                                                 }
162                                             }
163
164                                             if (!have) {
165                                                 final SupportingNode sn = createSupportingNode(k.getNodeId(), inControl);
166
167                                                 trans.put(LogicalDatastoreType.OPERATIONAL, this.target.child(Node.class, n.getKey()).child(
168                                                         SupportingNode.class, sn.getKey()), sn);
169                                             }
170                                         }
171                                         return this.target.builder().child(Node.class, n.getKey()).child(TerminationPoint.class, tp.getKey()).toInstance();
172                                     }
173                                 }
174                             } else {
175                                 LOG.debug("Ignoring termination point type {}", tpt);
176                             }
177                         }
178                     }
179                 }
180             }
181         }
182
183         LOG.debug("Termination point for {} not found, creating a new one", addr);
184
185         final String url = "ip://" + addr.toString();
186         final TerminationPointKey tpk = new TerminationPointKey(new TpId(url));
187         final TerminationPointBuilder tpb = new TerminationPointBuilder();
188         tpb.setKey(tpk).setTpId(tpk.getTpId());
189         tpb.addAugmentation(TerminationPoint1.class, new TerminationPoint1Builder().setIgpTerminationPointAttributes(
190                 new IgpTerminationPointAttributesBuilder().setTerminationPointType(
191                         new IpBuilder().setIpAddress(Lists.newArrayList(addr)).build()).build()).build());
192
193         final NodeKey nk = new NodeKey(new NodeId(url));
194         final NodeBuilder nb = new NodeBuilder();
195         nb.setKey(nk).setNodeId(nk.getNodeId());
196         nb.setTerminationPoint(Lists.newArrayList(tpb.build()));
197         if (sni != null) {
198             nb.setSupportingNode(Lists.newArrayList(createSupportingNode(InstanceIdentifier.keyOf(sni).getNodeId(), inControl)));
199         }
200
201         final InstanceIdentifier<Node> nid = this.target.child(Node.class, nb.getKey());
202         trans.put(LogicalDatastoreType.OPERATIONAL, nid, nb.build());
203         return nid.child(TerminationPoint.class, tpb.getKey());
204     }
205
206     private void create(final ReadWriteTransaction trans, final InstanceIdentifier<ReportedLsp> i, final ReportedLsp value) throws ReadFailedException {
207         final InstanceIdentifier<Node> ni = i.firstIdentifierOf(Node.class);
208
209         final Path1 rl = value.getPath().get(0).getAugmentation(Path1.class);
210
211         final AddressFamily af = rl.getLsp().getTlvs().getLspIdentifiers().getAddressFamily();
212
213         /*
214          * We are trying to ensure we have source and destination nodes.
215          */
216         final IpAddress srcIp, dstIp;
217         if (af instanceof Ipv4Case) {
218             final Ipv4 ipv4 = ((Ipv4Case) af).getIpv4();
219             srcIp = new IpAddress(ipv4.getIpv4TunnelSenderAddress());
220             dstIp = new IpAddress(ipv4.getIpv4TunnelEndpointAddress());
221         } else if (af instanceof Ipv6Case) {
222             final Ipv6 ipv6 = ((Ipv6Case) af).getIpv6();
223             srcIp = new IpAddress(ipv6.getIpv6TunnelSenderAddress());
224             dstIp = new IpAddress(ipv6.getIpv6TunnelSenderAddress());
225         } else {
226             throw new IllegalArgumentException("Unsupported address family: " + af.getImplementedInterface());
227         }
228
229         final Path path0 = value.getPath().get(0);
230         final Link1Builder lab = new Link1Builder();
231         if (path0.getBandwidth() != null) {
232             lab.setBandwidth(path0.getBandwidth().getBandwidth());
233         }
234         if (path0.getClassType() != null) {
235             lab.setClassType(path0.getClassType().getClassType());
236         }
237         lab.setSymbolicPathName(value.getName());
238
239         final InstanceIdentifier<TerminationPoint> dst = getIpTerminationPoint(trans, dstIp, null, Boolean.FALSE);
240         final InstanceIdentifier<TerminationPoint> src = getIpTerminationPoint(trans, srcIp, ni, rl.getLsp().isDelegate());
241
242         final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.Link1Builder slab = new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.Link1Builder();
243         slab.setOperationalStatus(rl.getLsp().getOperational());
244         slab.setAdministrativeStatus(rl.getLsp().isAdministrative() ? AdministrativeStatus.Active : AdministrativeStatus.Inactive);
245
246         final LinkId id = linkIdForLsp(i, value);
247         final LinkBuilder lb = new LinkBuilder();
248         lb.setLinkId(id);
249
250         lb.setSource(new SourceBuilder().setSourceNode(src.firstKeyOf(Node.class, NodeKey.class).getNodeId()).setSourceTp(
251                 src.firstKeyOf(TerminationPoint.class, TerminationPointKey.class).getTpId()).build());
252         lb.setDestination(new DestinationBuilder().setDestNode(dst.firstKeyOf(Node.class, NodeKey.class).getNodeId()).setDestTp(
253                 dst.firstKeyOf(TerminationPoint.class, TerminationPointKey.class).getTpId()).build());
254         lb.addAugmentation(Link1.class, lab.build());
255         lb.addAugmentation(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.Link1.class,
256                 slab.build());
257
258         trans.put(LogicalDatastoreType.OPERATIONAL, linkForLsp(id), lb.build());
259     }
260
261     private InstanceIdentifier<TerminationPoint> tpIdentifier(final NodeId node, final TpId tp) {
262         return this.target.builder().child(Node.class, new NodeKey(node)).child(TerminationPoint.class, new TerminationPointKey(tp)).toInstance();
263     }
264
265     private InstanceIdentifier<Node> nodeIdentifier(final NodeId node) {
266         return this.target.child(Node.class, new NodeKey(node));
267     }
268
269     private void remove(final ReadWriteTransaction trans, final InstanceIdentifier<ReportedLsp> i, final ReportedLsp value) throws ReadFailedException {
270         final InstanceIdentifier<Link> li = linkForLsp(linkIdForLsp(i, value));
271
272         final Optional<Link> ol = trans.read(LogicalDatastoreType.OPERATIONAL, li).checkedGet();
273         if (!ol.isPresent()) {
274             return;
275         }
276
277         final Link l = (Link) ol.get();
278         LOG.debug("Removing link {} (was {})", li, l);
279         trans.delete(LogicalDatastoreType.OPERATIONAL, li);
280
281         LOG.debug("Searching for orphan links/nodes");
282         final Optional<Topology> ot = trans.read(LogicalDatastoreType.OPERATIONAL, this.target).checkedGet();
283         Preconditions.checkState(ot.isPresent());
284
285         final Topology t = (Topology) ot.get();
286         NodeId srcNode = l.getSource().getSourceNode();
287         NodeId dstNode = l.getDestination().getDestNode();
288         TpId srcTp = l.getSource().getSourceTp();
289         TpId dstTp = l.getDestination().getDestTp();
290
291         boolean orphSrcNode = true, orphDstNode = true, orphDstTp = true, orphSrcTp = true;
292         for (final Link lw : t.getLink()) {
293             LOG.trace("Checking link {}", lw);
294
295             final NodeId sn = lw.getSource().getSourceNode();
296             final NodeId dn = lw.getDestination().getDestNode();
297             final TpId st = lw.getSource().getSourceTp();
298             final TpId dt = lw.getDestination().getDestTp();
299
300             // Source node checks
301             if (srcNode.equals(sn)) {
302                 if (orphSrcNode) {
303                     LOG.debug("Node {} held by source of link {}", srcNode, lw);
304                     orphSrcNode = false;
305                 }
306                 if (orphSrcTp && srcTp.equals(st)) {
307                     LOG.debug("TP {} held by source of link {}", srcTp, lw);
308                     orphSrcTp = false;
309                 }
310             }
311             if (srcNode.equals(dn)) {
312                 if (orphSrcNode) {
313                     LOG.debug("Node {} held by destination of link {}", srcNode, lw);
314                     orphSrcNode = false;
315                 }
316                 if (orphSrcTp && srcTp.equals(dt)) {
317                     LOG.debug("TP {} held by destination of link {}", srcTp, lw);
318                     orphSrcTp = false;
319                 }
320             }
321
322             // Destination node checks
323             if (dstNode.equals(sn)) {
324                 if (orphDstNode) {
325                     LOG.debug("Node {} held by source of link {}", dstNode, lw);
326                     orphDstNode = false;
327                 }
328                 if (orphDstTp && dstTp.equals(st)) {
329                     LOG.debug("TP {} held by source of link {}", dstTp, lw);
330                     orphDstTp = false;
331                 }
332             }
333             if (dstNode.equals(dn)) {
334                 if (orphDstNode) {
335                     LOG.debug("Node {} held by destination of link {}", dstNode, lw);
336                     orphDstNode = false;
337                 }
338                 if (orphDstTp && dstTp.equals(dt)) {
339                     LOG.debug("TP {} held by destination of link {}", dstTp, lw);
340                     orphDstTp = false;
341                 }
342             }
343         }
344
345         if (orphSrcNode && !orphSrcTp) {
346             LOG.warn("Orphan source node {} but not TP {}, retaining the node", srcNode, srcTp);
347             orphSrcNode = false;
348         }
349         if (orphDstNode && !orphDstTp) {
350             LOG.warn("Orphan destination node {} but not TP {}, retaining the node", dstNode, dstTp);
351             orphDstNode = false;
352         }
353
354         if (orphSrcNode) {
355             LOG.debug("Removing orphan node {}", srcNode);
356             trans.delete(LogicalDatastoreType.OPERATIONAL, nodeIdentifier(srcNode));
357         } else if (orphSrcTp) {
358             LOG.debug("Removing orphan TP {} on node {}", srcTp, srcNode);
359             trans.delete(LogicalDatastoreType.OPERATIONAL, tpIdentifier(srcNode, srcTp));
360         }
361         if (orphDstNode) {
362             LOG.debug("Removing orphan node {}", dstNode);
363             trans.delete(LogicalDatastoreType.OPERATIONAL, nodeIdentifier(dstNode));
364         } else if (orphDstTp) {
365             LOG.debug("Removing orphan TP {} on node {}", dstTp, dstNode);
366             trans.delete(LogicalDatastoreType.OPERATIONAL, tpIdentifier(dstNode, dstTp));
367         }
368     }
369
370     @Override
371     public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
372         final ReadWriteTransaction trans = this.dataProvider.newReadWriteTransaction();
373
374         final Set<InstanceIdentifier<ReportedLsp>> lsps = new HashSet<>();
375         final Set<InstanceIdentifier<Node>> nodes = new HashSet<>();
376
377         // Categorize reported identifiers
378         for (final InstanceIdentifier<?> i : change.getRemovedPaths()) {
379             categorizeIdentifier(i, lsps, nodes);
380         }
381         for (final InstanceIdentifier<?> i : change.getUpdatedData().keySet()) {
382             categorizeIdentifier(i, lsps, nodes);
383         }
384         for (final InstanceIdentifier<?> i : change.getCreatedData().keySet()) {
385             categorizeIdentifier(i, lsps, nodes);
386         }
387
388         // Get the subtrees
389         final Map<InstanceIdentifier<?>, ? extends DataObject> o = change.getOriginalData();
390         final Map<InstanceIdentifier<?>, DataObject> u = change.getUpdatedData();
391         final Map<InstanceIdentifier<?>, DataObject> c = change.getCreatedData();
392
393         // Now walk all nodes, check for removals/additions and cascade them to LSPs
394         for (final InstanceIdentifier<Node> i : nodes) {
395             enumerateLsps(i, (Node) o.get(i), lsps);
396             enumerateLsps(i, (Node) u.get(i), lsps);
397             enumerateLsps(i, (Node) c.get(i), lsps);
398         }
399
400         // We now have list of all affected LSPs. Walk them create/remove them
401         for (final InstanceIdentifier<ReportedLsp> i : lsps) {
402             final ReportedLsp oldValue = (ReportedLsp) o.get(i);
403             ReportedLsp newValue = (ReportedLsp) u.get(i);
404             if (newValue == null) {
405                 newValue = (ReportedLsp) c.get(i);
406             }
407
408             LOG.debug("Updating lsp {} value {} -> {}", i, oldValue, newValue);
409             if (oldValue != null) {
410                 try {
411                     remove(trans, i, oldValue);
412                 } catch (ReadFailedException e) {
413                     LOG.warn("Failed to remove LSP {}", i, e);
414                 }
415             }
416             if (newValue != null) {
417                 try {
418                     create(trans, i, newValue);
419                 } catch (ReadFailedException e) {
420                     LOG.warn("Failed to add LSP {}", i, e);
421                 }
422             }
423         }
424
425         Futures.addCallback(JdkFutureAdapters.listenInPoolThread(trans.submit()), new FutureCallback<Void>() {
426             @Override
427             public void onSuccess(final Void result) {
428                 LOG.trace("Topology change committed successfully");
429             }
430
431             @Override
432             public void onFailure(final Throwable t) {
433                 LOG.error("Failed to propagate a topology change, target topology became inconsistent", t);
434             }
435         });
436     }
437
438     public static InstanceIdentifier<Link> linkIdentifier(final InstanceIdentifier<Topology> topology, final NodeId node, final String name) {
439         return topology.child(Link.class, new LinkKey(new LinkId(node.getValue() + "/lsp/" + name)));
440     }
441 }