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