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