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