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