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