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