Fix NPE while accessing DomTxChain when bgp/app peer singleton service is restarted
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / AbstractPeer.java
1 /*
2  * Copyright (c) 2018 AT&T Intellectual Property. 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.protocol.bgp.rib.impl;
9
10 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.PEER_NID;
11
12 import com.google.common.util.concurrent.FluentFuture;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import java.util.Arrays;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Optional;
20 import java.util.Set;
21 import java.util.concurrent.ExecutionException;
22 import org.checkerframework.checker.lock.qual.GuardedBy;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.opendaylight.mdsal.binding.api.TransactionChain;
25 import org.opendaylight.mdsal.binding.api.TransactionChainListener;
26 import org.opendaylight.mdsal.binding.api.WriteTransaction;
27 import org.opendaylight.mdsal.common.api.CommitInfo;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
30 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
31 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
32 import org.opendaylight.protocol.bgp.mode.impl.BGPRouteEntryExportParametersImpl;
33 import org.opendaylight.protocol.bgp.rib.impl.spi.PeerTransactionChain;
34 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
35 import org.opendaylight.protocol.bgp.rib.impl.state.BGPPeerStateImpl;
36 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
37 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
38 import org.opendaylight.protocol.bgp.rib.spi.Peer;
39 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
40 import org.opendaylight.protocol.bgp.rib.spi.entry.AbstractAdvertizedRoute;
41 import org.opendaylight.protocol.bgp.rib.spi.entry.ActualBestPathRoutes;
42 import org.opendaylight.protocol.bgp.rib.spi.entry.AdvertizedRoute;
43 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteEntryDependenciesContainer;
44 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteKeyIdentifier;
45 import org.opendaylight.protocol.bgp.rib.spi.entry.StaleBestPathRoute;
46 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
47 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryExportParameters;
48 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
49 import org.opendaylight.protocol.bgp.rib.spi.state.BGPAfiSafiState;
50 import org.opendaylight.protocol.bgp.rib.spi.state.BGPErrorHandlingState;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.ClusterIdentifier;
61 import org.opendaylight.yangtools.yang.binding.ChildOf;
62 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
63 import org.opendaylight.yangtools.yang.binding.DataObject;
64 import org.opendaylight.yangtools.yang.binding.Identifiable;
65 import org.opendaylight.yangtools.yang.binding.Identifier;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 abstract class AbstractPeer extends BGPPeerStateImpl implements BGPRouteEntryImportParameters, TransactionChainListener,
73         DOMTransactionChainListener, Peer, PeerTransactionChain {
74     private static final Logger LOG = LoggerFactory.getLogger(AbstractPeer.class);
75     protected final RIB rib;
76     final String name;
77     final PeerRole peerRole;
78     private final ClusterIdentifier clusterId;
79     private final AsNumber localAs;
80     @GuardedBy("this")
81     private DOMTransactionChain domChain;
82     @GuardedBy("this")
83     TransactionChain bindingChain;
84     byte[] rawIdentifier;
85     @GuardedBy("this")
86     PeerId peerId;
87     private FluentFuture<? extends CommitInfo> submitted;
88     RTCClientRouteCache rtCache = new RTCClientRouteCache();
89
90     AbstractPeer(
91             final RIB rib,
92             final String peerName,
93             final String groupId,
94             final PeerRole role,
95             final @Nullable ClusterIdentifier clusterId,
96             final @Nullable AsNumber localAs,
97             final IpAddress neighborAddress,
98             final Set<TablesKey> afiSafisAdvertized,
99             final Set<TablesKey> afiSafisGracefulAdvertized,
100             final Map<TablesKey, Integer> afiSafisLlGracefulAdvertized) {
101         super(rib.getInstanceIdentifier(), groupId, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized,
102                 afiSafisLlGracefulAdvertized);
103         this.name = peerName;
104         this.peerRole = role;
105         this.clusterId = clusterId;
106         this.localAs = localAs;
107         this.rib = rib;
108         createDomChain();
109     }
110
111     AbstractPeer(
112             final RIB rib,
113             final String peerName,
114             final String groupId,
115             final PeerRole role,
116             final IpAddress neighborAddress,
117             final Set<TablesKey> afiSafisGracefulAdvertized) {
118         this(rib, peerName, groupId, role, null, null, neighborAddress,
119                 rib.getLocalTablesKeys(), afiSafisGracefulAdvertized, Collections.emptyMap());
120     }
121
122     final synchronized FluentFuture<? extends CommitInfo> removePeer(final @Nullable YangInstanceIdentifier peerPath) {
123         if (peerPath == null) {
124             return CommitInfo.emptyFluentFuture();
125         }
126         LOG.info("Closed per Peer {} removed", peerPath);
127         final DOMDataTreeWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
128         tx.delete(LogicalDatastoreType.OPERATIONAL, peerPath);
129         final FluentFuture<? extends CommitInfo> future = tx.commit();
130         future.addCallback(new FutureCallback<CommitInfo>() {
131             @Override
132             public void onSuccess(final CommitInfo result) {
133                 LOG.debug("Peer {} removed", peerPath);
134             }
135
136             @Override
137             public void onFailure(final Throwable throwable) {
138                 LOG.error("Failed to remove Peer {}", peerPath, throwable);
139             }
140         }, MoreExecutors.directExecutor());
141         return future;
142     }
143
144     synchronized YangInstanceIdentifier createPeerPath() {
145         return this.rib.getYangRibId().node(PEER_NID).node(IdentifierUtils.domPeerId(this.peerId));
146     }
147
148     @Override
149     public final synchronized PeerId getPeerId() {
150         return this.peerId;
151     }
152
153     @Override
154     public final PeerRole getRole() {
155         return this.peerRole;
156     }
157
158     @Override
159     public final synchronized byte[] getRawIdentifier() {
160         return Arrays.copyOf(this.rawIdentifier, this.rawIdentifier.length);
161     }
162
163     @Override
164     public final PeerRole getFromPeerRole() {
165         return getRole();
166     }
167
168     @Override
169     public final PeerId getFromPeerId() {
170         return getPeerId();
171     }
172
173     @Override
174     public final ClusterIdentifier getFromClusterId() {
175         return getClusterId();
176     }
177
178     @Override
179     public final void onTransactionChainSuccessful(final DOMTransactionChain chain) {
180         LOG.debug("Transaction chain {} successful.", chain);
181     }
182
183     @Override
184     public final void onTransactionChainSuccessful(final TransactionChain chain) {
185         LOG.debug("Transaction chain {} successful.", chain);
186     }
187
188     @Override
189     public final BGPErrorHandlingState getBGPErrorHandlingState() {
190         return this;
191     }
192
193     @Override
194     public final BGPAfiSafiState getBGPAfiSafiState() {
195         return this;
196     }
197
198     @Override
199     public final AsNumber getFromPeerLocalAs() {
200         return getLocalAs();
201     }
202
203     @Override
204     public final String getName() {
205         return this.name;
206     }
207
208     @Override
209     public final ClusterIdentifier getClusterId() {
210         return this.clusterId;
211     }
212
213     @Override
214     public final AsNumber getLocalAs() {
215         return this.localAs;
216     }
217
218     @Override
219     public synchronized DOMTransactionChain getDomChain() {
220         return this.domChain;
221     }
222
223     /**
224      * Returns true if route can be send.
225      */
226     private boolean filterRoutes(final PeerId fromPeer, final TablesKey localTK) {
227         return supportsTable(localTK) && !fromPeer.equals(getPeerId());
228     }
229
230     @Override
231     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
232             R extends Route & ChildOf<? super S> & Identifiable<I>,
233             I extends Identifier<R>> void initializeRibOut(final RouteEntryDependenciesContainer entryDep,
234                     final List<ActualBestPathRoutes<C, S, R, I>> routesToStore) {
235         if (this.bindingChain == null) {
236             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
237             return;
238         }
239
240         final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
241         final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
242         final boolean addPathSupported = supportsAddPathSupported(tk);
243
244         final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
245         for (final ActualBestPathRoutes<C, S, R, I> initializingRoute : routesToStore) {
246             if (!supportsLLGR() && initializingRoute.isDepreferenced()) {
247                 // Stale Long-lived Graceful Restart routes should not be propagated
248                 continue;
249             }
250
251             final PeerId fromPeerId = initializingRoute.getFromPeerId();
252             if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
253                 continue;
254             }
255
256             final R route = initializingRoute.getRoute();
257             final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
258             if (fromPeer == null) {
259                 LOG.debug("Failed to acquire peer structure for {}, ignoring route {}", fromPeerId, initializingRoute);
260                 continue;
261             }
262
263             final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
264                     this, route.getRouteKey(), this.rtCache);
265
266             final Optional<Attributes> effAttr = entryDep.getRoutingPolicies()
267                     .applyExportPolicies(routeEntry, initializingRoute.getAttributes(), entryDep.getAfiSafType());
268             final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
269
270             effAttr.ifPresent(attributes
271                 -> storeRoute(ribSupport, addPathSupported, tableRibout, initializingRoute, route, attributes, tx));
272         }
273
274         final FluentFuture<? extends CommitInfo> future = tx.commit();
275         this.submitted = future;
276         future.addCallback(new FutureCallback<CommitInfo>() {
277             @Override
278             public void onSuccess(final CommitInfo result) {
279                 LOG.trace("Successful update commit");
280             }
281
282             @Override
283             public void onFailure(final Throwable trw) {
284                 LOG.error("Failed update commit", trw);
285             }
286         }, MoreExecutors.directExecutor());
287     }
288
289     @Override
290     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
291             R extends Route & ChildOf<? super S> & Identifiable<I>,
292             I extends Identifier<R>> void refreshRibOut(final RouteEntryDependenciesContainer entryDep,
293             final List<StaleBestPathRoute<C, S, R, I>> staleRoutes, final List<AdvertizedRoute<C, S, R, I>> newRoutes) {
294         if (this.bindingChain == null) {
295             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
296             return;
297         }
298         final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
299         final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
300         deleteRouteRibOut(ribSupport, staleRoutes, tx);
301         installRouteRibOut(entryDep, newRoutes, tx);
302
303         final FluentFuture<? extends CommitInfo> future = tx.commit();
304         this.submitted = future;
305         future.addCallback(new FutureCallback<CommitInfo>() {
306             @Override
307             public void onSuccess(final CommitInfo result) {
308                 LOG.trace("Successful update commit");
309             }
310
311             @Override
312             public void onFailure(final Throwable trw) {
313                 LOG.error("Failed update commit", trw);
314             }
315         }, MoreExecutors.directExecutor());
316     }
317
318     @Override
319     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
320             R extends Route & ChildOf<? super S> & Identifiable<I>,
321             I extends Identifier<R>> void reEvaluateAdvertizement(
322             final RouteEntryDependenciesContainer entryDep,
323             final List<ActualBestPathRoutes<C, S, R, I>> routesToStore) {
324         if (this.bindingChain == null) {
325             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
326             return;
327         }
328
329         final RIBSupport<C,S,R,I> ribSupport = entryDep.getRIBSupport();
330         final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
331         final boolean addPathSupported = supportsAddPathSupported(tk);
332
333         final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
334         for (final ActualBestPathRoutes<C, S, R, I> actualBestRoute : routesToStore) {
335             final PeerId fromPeerId = actualBestRoute.getFromPeerId();
336             if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
337                 continue;
338             }
339
340             final R route = actualBestRoute.getRoute();
341             final Optional<Attributes> effAttr;
342             if (supportsLLGR() || !actualBestRoute.isDepreferenced()) {
343                 final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
344                 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
345                     this, route.getRouteKey(), this.rtCache);
346                 effAttr = entryDep.getRoutingPolicies()
347                         .applyExportPolicies(routeEntry, actualBestRoute.getAttributes(), entryDep.getAfiSafType());
348             } else {
349                 // Stale Long-lived Graceful Restart routes should not be propagated
350                 effAttr = Optional.empty();
351             }
352
353             final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
354             if (effAttr.isPresent()) {
355                 storeRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, route, effAttr.get(), tx);
356             } else {
357                 deleteRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, tx);
358             }
359         }
360
361         final FluentFuture<? extends CommitInfo> future = tx.commit();
362         this.submitted = future;
363         future.addCallback(new FutureCallback<CommitInfo>() {
364             @Override
365             public void onSuccess(final CommitInfo result) {
366                 LOG.trace("Successful update commit");
367             }
368
369             @Override
370             public void onFailure(final Throwable trw) {
371                 LOG.error("Failed update commit", trw);
372             }
373         }, MoreExecutors.directExecutor());
374     }
375
376     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
377             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void installRouteRibOut(
378                     final RouteEntryDependenciesContainer entryDep, final List<AdvertizedRoute<C, S, R, I>> routes,
379                     final WriteTransaction tx) {
380         final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
381         final BGPPeerTracker peerTracker = entryDep.getPeerTracker();
382         final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
383         final BGPRibRoutingPolicy routingPolicies = entryDep.getRoutingPolicies();
384         final boolean addPathSupported = supportsAddPathSupported(tk);
385         final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
386
387         for (final AdvertizedRoute<C, S, R, I> advRoute : routes) {
388             final PeerId fromPeerId = advRoute.getFromPeerId();
389             if (!filterRoutes(fromPeerId, tk) || !advRoute.isFirstBestPath() && !addPathSupported) {
390                 continue;
391             }
392             if (!supportsLLGR() && advRoute.isDepreferenced()) {
393                 // https://tools.ietf.org/html/draft-uttaro-idr-bgp-persistence-04#section-4.3
394                 //     o  The route SHOULD NOT be advertised to any neighbor from which the
395                 //        Long-lived Graceful Restart Capability has not been received.  The
396                 //        exception is described in the Optional Partial Deployment
397                 //        Procedure section (Section 4.7).  Note that this requirement
398                 //        implies that such routes should be withdrawn from any such
399                 //        neighbor.
400                 deleteRoute(ribSupport, addPathSupported, tableRibout, advRoute, tx);
401                 continue;
402             }
403
404             final R route = advRoute.getRoute();
405             Optional<Attributes> effAttr = Optional.empty();
406             final Peer fromPeer = peerTracker.getPeer(fromPeerId);
407             final Attributes attributes = advRoute.getAttributes();
408             if (fromPeer != null && attributes != null) {
409                 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
410                         this, route.getRouteKey(), this.rtCache);
411                 effAttr = routingPolicies.applyExportPolicies(routeEntry, attributes, entryDep.getAfiSafType());
412             }
413             effAttr.ifPresent(attributes1
414                 -> storeRoute(ribSupport, addPathSupported, tableRibout, advRoute, route, attributes1, tx));
415         }
416     }
417
418     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
419             R extends Route & ChildOf<? super S> & Identifiable<I>,
420             I extends Identifier<R>> void deleteRouteRibOut(
421             final RIBSupport<C, S, R, I> ribSupport,
422             final List<StaleBestPathRoute<C, S, R, I>> staleRoutesIid,
423             final WriteTransaction tx) {
424         final TablesKey tk = ribSupport.getTablesKey();
425         final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
426         final boolean addPathSupported = supportsAddPathSupported(tk);
427         staleRoutesIid.forEach(staleRouteIid
428             -> removeRoute(ribSupport, addPathSupported, tableRibout, staleRouteIid, tx));
429     }
430
431     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
432             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void storeRoute(
433                     final RIBSupport<C, S, R, I> ribSupport, final boolean addPathSupported,
434                     final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
435                     final RouteKeyIdentifier<R, I> advRoute, final R route, final Attributes effAttr,
436                     final WriteTransaction tx) {
437         final InstanceIdentifier<R> ribOut;
438         final I newKey;
439         if (!addPathSupported) {
440             ribOut = ribSupport.createRouteIdentifier(tableRibout, advRoute.getNonAddPathRouteKeyIdentifier());
441             newKey = ribSupport.createRouteListKey(route.getRouteKey());
442         } else {
443             ribOut = ribSupport.createRouteIdentifier(tableRibout, advRoute.getAddPathRouteKeyIdentifier());
444             newKey = ribSupport.createRouteListKey(route.getPathId(), route.getRouteKey());
445         }
446
447         final R newRoute = ribSupport.createRoute(route, newKey, effAttr);
448         LOG.debug("Write advRoute {} to peer AdjRibsOut {}", advRoute, getPeerId());
449         tx.put(LogicalDatastoreType.OPERATIONAL, ribOut, newRoute);
450     }
451
452     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
453             R extends Route & ChildOf<? super S> & Identifiable<I>,
454             I extends Identifier<R>> void removeRoute(final RIBSupport<C, S, R, I> ribSupport,
455             final boolean addPathSupported, final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
456             final StaleBestPathRoute<C, S, R, I> staleRouteIid, final WriteTransaction tx) {
457         if (addPathSupported) {
458             List<I> staleRoutesIId = staleRouteIid.getAddPathRouteKeyIdentifiers();
459             for (final I id : staleRoutesIId) {
460                 final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout, id);
461                 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
462                 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
463             }
464         } else {
465             if (!staleRouteIid.isNonAddPathBestPathNew()) {
466                 return;
467             }
468             final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
469                     staleRouteIid.getNonAddPathRouteKeyIdentifier());
470             LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
471             tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
472         }
473     }
474
475     // FIXME: why is this different from removeRoute()?
476     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
477             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void deleteRoute(
478             final RIBSupport<C, S, R, I> ribSupport,  final boolean addPathSupported,
479             final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
480             final AbstractAdvertizedRoute<C, S , R, I> advRoute, final WriteTransaction tx) {
481         final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
482             addPathSupported ? advRoute.getAddPathRouteKeyIdentifier() : advRoute.getNonAddPathRouteKeyIdentifier());
483         LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
484         tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
485     }
486
487     final synchronized void releaseBindingChain() {
488         if (this.submitted != null) {
489             try {
490                 this.submitted.get();
491             } catch (final InterruptedException | ExecutionException throwable) {
492                 LOG.error("Write routes failed", throwable);
493             }
494         }
495         closeBindingChain();
496     }
497
498     private synchronized void closeBindingChain() {
499         if (this.bindingChain != null) {
500             LOG.info("Closing peer chain {}", getPeerId());
501             this.bindingChain.close();
502             this.bindingChain = null;
503         }
504     }
505
506     final synchronized void createDomChain() {
507         if (this.domChain == null) {
508             LOG.info("Creating DOM peer chain {}", getPeerId());
509             this.domChain = this.rib.createPeerDOMChain(this);
510         }
511     }
512
513     final synchronized void closeDomChain() {
514         if (this.domChain != null) {
515             LOG.info("Closing DOM peer chain {}", getPeerId());
516             this.domChain.close();
517             this.domChain = null;
518         }
519     }
520
521     boolean supportsLLGR() {
522         return false;
523     }
524 }