Update RIB-based constants
[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 javax.annotation.Nullable;
23 import javax.annotation.concurrent.GuardedBy;
24 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
28 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
30 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
31 import org.opendaylight.mdsal.common.api.CommitInfo;
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         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     BindingTransactionChain 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             @Nullable final ClusterIdentifier clusterId,
96             @Nullable final 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         this.domChain = this.rib.createPeerDOMChain(this);
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(@Nullable final YangInstanceIdentifier peerPath) {
123         if (peerPath == null) {
124             return CommitInfo.emptyFluentFuture();
125         }
126         LOG.info("Closed per Peer {} removed", peerPath);
127         final DOMDataWriteTransaction 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 TransactionChain<?, ?> chain) {
180         LOG.debug("Transaction chain {} successful.", chain);
181     }
182
183     @Override
184     public final BGPErrorHandlingState getBGPErrorHandlingState() {
185         return this;
186     }
187
188     @Override
189     public final BGPAfiSafiState getBGPAfiSafiState() {
190         return this;
191     }
192
193     @Override
194     public final AsNumber getFromPeerLocalAs() {
195         return getLocalAs();
196     }
197
198     @Override
199     public final String getName() {
200         return this.name;
201     }
202
203     @Override
204     public final ClusterIdentifier getClusterId() {
205         return this.clusterId;
206     }
207
208     @Override
209     public final AsNumber getLocalAs() {
210         return this.localAs;
211     }
212
213     @Override
214     public synchronized DOMTransactionChain getDomChain() {
215         return this.domChain;
216     }
217
218     /**
219      * Returns true if route can be send.
220      */
221     private boolean filterRoutes(final PeerId fromPeer, final TablesKey localTK) {
222         return supportsTable(localTK) && !fromPeer.equals(getPeerId());
223     }
224
225     @Override
226     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
227             R extends Route & ChildOf<? super S> & Identifiable<I>,
228             I extends Identifier<R>> void initializeRibOut(final RouteEntryDependenciesContainer entryDep,
229                     final List<ActualBestPathRoutes<C, S, R, I>> routesToStore) {
230         if (this.bindingChain == null) {
231             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
232             return;
233         }
234
235         final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
236         final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
237         final boolean addPathSupported = supportsAddPathSupported(tk);
238
239         final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
240         for (final ActualBestPathRoutes<C, S, R, I> initializingRoute : routesToStore) {
241             if (!supportsLLGR() && initializingRoute.isDepreferenced()) {
242                 // Stale Long-lived Graceful Restart routes should not be propagated
243                 continue;
244             }
245
246             final PeerId fromPeerId = initializingRoute.getFromPeerId();
247             if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
248                 continue;
249             }
250
251             final R route = initializingRoute.getRoute();
252             final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
253             final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
254                     this, route.getRouteKey(), this.rtCache);
255
256             final Optional<Attributes> effAttr = entryDep.getRoutingPolicies()
257                     .applyExportPolicies(routeEntry, initializingRoute.getAttributes(), entryDep.getAfiSafType());
258             final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
259
260             effAttr.ifPresent(attributes
261                 -> storeRoute(ribSupport, addPathSupported, tableRibout, initializingRoute, route, attributes, tx));
262         }
263
264         final FluentFuture<? extends CommitInfo> future = tx.commit();
265         this.submitted = future;
266         future.addCallback(new FutureCallback<CommitInfo>() {
267             @Override
268             public void onSuccess(final CommitInfo result) {
269                 LOG.trace("Successful update commit");
270             }
271
272             @Override
273             public void onFailure(final Throwable trw) {
274                 LOG.error("Failed update commit", trw);
275             }
276         }, MoreExecutors.directExecutor());
277     }
278
279     @Override
280     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
281             R extends Route & ChildOf<? super S> & Identifiable<I>,
282             I extends Identifier<R>> void refreshRibOut(final RouteEntryDependenciesContainer entryDep,
283             final List<StaleBestPathRoute<C, S, R, I>> staleRoutes, final List<AdvertizedRoute<C, S, R, I>> newRoutes) {
284         if (this.bindingChain == null) {
285             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
286             return;
287         }
288         final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
289         final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
290         deleteRouteRibOut(ribSupport, staleRoutes, tx);
291         installRouteRibOut(entryDep, newRoutes, tx);
292
293         final FluentFuture<? extends CommitInfo> future = tx.commit();
294         this.submitted = future;
295         future.addCallback(new FutureCallback<CommitInfo>() {
296             @Override
297             public void onSuccess(final CommitInfo result) {
298                 LOG.trace("Successful update commit");
299             }
300
301             @Override
302             public void onFailure(final Throwable trw) {
303                 LOG.error("Failed update commit", trw);
304             }
305         }, MoreExecutors.directExecutor());
306     }
307
308     @Override
309     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
310             R extends Route & ChildOf<? super S> & Identifiable<I>,
311             I extends Identifier<R>> void reEvaluateAdvertizement(
312             final RouteEntryDependenciesContainer entryDep,
313             final List<ActualBestPathRoutes<C, S, R, I>> routesToStore) {
314         if (this.bindingChain == null) {
315             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
316             return;
317         }
318
319         final RIBSupport<C,S,R,I> ribSupport = entryDep.getRIBSupport();
320         final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
321         final boolean addPathSupported = supportsAddPathSupported(tk);
322
323         final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
324         for (final ActualBestPathRoutes<C, S, R, I> actualBestRoute : routesToStore) {
325             final PeerId fromPeerId = actualBestRoute.getFromPeerId();
326             if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
327                 continue;
328             }
329
330             final R route = actualBestRoute.getRoute();
331             final Optional<Attributes> effAttr;
332             if (supportsLLGR() || !actualBestRoute.isDepreferenced()) {
333                 final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
334                 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
335                     this, route.getRouteKey(), this.rtCache);
336                 effAttr = entryDep.getRoutingPolicies()
337                         .applyExportPolicies(routeEntry, actualBestRoute.getAttributes(), entryDep.getAfiSafType());
338             } else {
339                 // Stale Long-lived Graceful Restart routes should not be propagated
340                 effAttr = Optional.empty();
341             }
342
343             final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
344             if (effAttr.isPresent()) {
345                 storeRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, route, effAttr.get(), tx);
346             } else {
347                 deleteRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, tx);
348             }
349         }
350
351         final FluentFuture<? extends CommitInfo> future = tx.commit();
352         this.submitted = future;
353         future.addCallback(new FutureCallback<CommitInfo>() {
354             @Override
355             public void onSuccess(final CommitInfo result) {
356                 LOG.trace("Successful update commit");
357             }
358
359             @Override
360             public void onFailure(final Throwable trw) {
361                 LOG.error("Failed update commit", trw);
362             }
363         }, MoreExecutors.directExecutor());
364     }
365
366     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
367             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void installRouteRibOut(
368                     final RouteEntryDependenciesContainer entryDep, final List<AdvertizedRoute<C, S, R, I>> routes,
369                     final WriteTransaction tx) {
370         final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
371         final BGPPeerTracker peerTracker = entryDep.getPeerTracker();
372         final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
373         final BGPRibRoutingPolicy routingPolicies = entryDep.getRoutingPolicies();
374         final boolean addPathSupported = supportsAddPathSupported(tk);
375         final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
376
377         for (final AdvertizedRoute<C, S, R, I> advRoute : routes) {
378             final PeerId fromPeerId = advRoute.getFromPeerId();
379             if (!filterRoutes(fromPeerId, tk) || !advRoute.isFirstBestPath() && !addPathSupported) {
380                 continue;
381             }
382             if (!supportsLLGR() && advRoute.isDepreferenced()) {
383                 // https://tools.ietf.org/html/draft-uttaro-idr-bgp-persistence-04#section-4.3
384                 //     o  The route SHOULD NOT be advertised to any neighbor from which the
385                 //        Long-lived Graceful Restart Capability has not been received.  The
386                 //        exception is described in the Optional Partial Deployment
387                 //        Procedure section (Section 4.7).  Note that this requirement
388                 //        implies that such routes should be withdrawn from any such
389                 //        neighbor.
390                 deleteRoute(ribSupport, addPathSupported, tableRibout, advRoute, tx);
391                 continue;
392             }
393
394             final R route = advRoute.getRoute();
395             Optional<Attributes> effAttr = Optional.empty();
396             final Peer fromPeer = peerTracker.getPeer(fromPeerId);
397             final Attributes attributes = advRoute.getAttributes();
398             if (fromPeer != null && attributes != null) {
399                 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
400                         this, route.getRouteKey(), this.rtCache);
401                 effAttr = routingPolicies.applyExportPolicies(routeEntry, attributes, entryDep.getAfiSafType());
402             }
403             effAttr.ifPresent(attributes1
404                 -> storeRoute(ribSupport, addPathSupported, tableRibout, advRoute, route, attributes1, tx));
405         }
406     }
407
408     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
409             R extends Route & ChildOf<? super S> & Identifiable<I>,
410             I extends Identifier<R>> void deleteRouteRibOut(
411             final RIBSupport<C, S, R, I> ribSupport,
412             final List<StaleBestPathRoute<C, S, R, I>> staleRoutesIid,
413             final WriteTransaction tx) {
414         final TablesKey tk = ribSupport.getTablesKey();
415         final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
416         final boolean addPathSupported = supportsAddPathSupported(tk);
417         staleRoutesIid.forEach(staleRouteIid
418             -> removeRoute(ribSupport, addPathSupported, tableRibout, staleRouteIid, tx));
419     }
420
421     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
422             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void storeRoute(
423                     final RIBSupport<C, S, R, I> ribSupport, final boolean addPathSupported,
424                     final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
425                     final RouteKeyIdentifier<R, I> advRoute, final R route, final Attributes effAttr,
426                     final WriteTransaction tx) {
427         final InstanceIdentifier<R> ribOut;
428         final I newKey;
429         if (!addPathSupported) {
430             ribOut = ribSupport.createRouteIdentifier(tableRibout, advRoute.getNonAddPathRouteKeyIdentifier());
431             newKey = ribSupport.createRouteListKey(route.getRouteKey());
432         } else {
433             ribOut = ribSupport.createRouteIdentifier(tableRibout, advRoute.getAddPathRouteKeyIdentifier());
434             newKey = ribSupport.createRouteListKey(route.getPathId(), route.getRouteKey());
435         }
436
437         final R newRoute = ribSupport.createRoute(route, newKey, effAttr);
438         LOG.debug("Write advRoute {} to peer AdjRibsOut {}", advRoute, getPeerId());
439         tx.put(LogicalDatastoreType.OPERATIONAL, ribOut, newRoute);
440     }
441
442     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
443             R extends Route & ChildOf<? super S> & Identifiable<I>,
444             I extends Identifier<R>> void removeRoute(final RIBSupport<C, S, R, I> ribSupport,
445             final boolean addPathSupported, final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
446             final StaleBestPathRoute<C, S, R, I> staleRouteIid, final WriteTransaction tx) {
447         if (addPathSupported) {
448             List<I> staleRoutesIId = staleRouteIid.getAddPathRouteKeyIdentifiers();
449             for (final I id : staleRoutesIId) {
450                 final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout, id);
451                 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
452                 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
453             }
454         } else {
455             if (!staleRouteIid.isNonAddPathBestPathNew()) {
456                 return;
457             }
458             final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
459                     staleRouteIid.getNonAddPathRouteKeyIdentifier());
460             LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
461             tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
462         }
463     }
464
465     // FIXME: why is this different from removeRoute()?
466     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
467             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void deleteRoute(
468             final RIBSupport<C, S, R, I> ribSupport,  final boolean addPathSupported,
469             final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
470             final AbstractAdvertizedRoute<C, S , R, I> advRoute, final WriteTransaction tx) {
471         final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
472             addPathSupported ? advRoute.getAddPathRouteKeyIdentifier() : advRoute.getNonAddPathRouteKeyIdentifier());
473         LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
474         tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
475     }
476
477     final synchronized void releaseBindingChain() {
478         if (this.submitted != null) {
479             try {
480                 this.submitted.get();
481             } catch (final InterruptedException | ExecutionException throwable) {
482                 LOG.error("Write routes failed", throwable);
483             }
484         }
485         closeBindingChain();
486     }
487
488     private synchronized void closeBindingChain() {
489         if (this.bindingChain != null) {
490             LOG.info("Closing peer chain {}", getPeerId());
491             this.bindingChain.close();
492             this.bindingChain = null;
493         }
494     }
495
496     final synchronized void closeDomChain() {
497         if (this.domChain != null) {
498             LOG.info("Closing DOM peer chain {}", getPeerId());
499             this.domChain.close();
500             this.domChain = null;
501         }
502     }
503
504     boolean supportsLLGR() {
505         return false;
506     }
507 }