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