Convert LocRibWriter to DOM APIs
[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.common.api.CommitInfo;
27 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations;
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.BGPRouteEntryExportParameters;
47 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
48 import org.opendaylight.protocol.bgp.rib.spi.state.BGPAfiSafiState;
49 import org.opendaylight.protocol.bgp.rib.spi.state.BGPErrorHandlingState;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressNoZone;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.ClusterIdentifier;
60 import org.opendaylight.yangtools.yang.binding.ChildOf;
61 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
62 import org.opendaylight.yangtools.yang.binding.DataObject;
63 import org.opendaylight.yangtools.yang.binding.Identifiable;
64 import org.opendaylight.yangtools.yang.binding.Identifier;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
67 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
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 IpAddressNoZone 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 IpAddressNoZone 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 YangInstanceIdentifier tableRibout = getRibOutIId(ribSupport.tablesKey());
242         final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
243
244         final DOMDataTreeWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
245         for (final ActualBestPathRoutes<C, S, R, I> initRoute : routesToStore) {
246             if (!supportsLLGR() && initRoute.isDepreferenced()) {
247                 // Stale Long-lived Graceful Restart routes should not be propagated
248                 continue;
249             }
250
251             final PeerId fromPeerId = initRoute.getFromPeerId();
252             if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
253                 continue;
254             }
255
256             final MapEntryNode route = initRoute.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, initRoute);
260                 continue;
261             }
262
263             final YangInstanceIdentifier routePath = createRoutePath(ribSupport, tableRibout, initRoute,
264                 addPathSupported);
265             applyExportPolicy(entryDep, fromPeerId, route, routePath, initRoute.getAttributes()).ifPresent(
266                 attributes -> storeRoute(ribSupport, initRoute, route, routePath, attributes, tx));
267         }
268
269         final FluentFuture<? extends CommitInfo> future = tx.commit();
270         this.submitted = future;
271         future.addCallback(new FutureCallback<CommitInfo>() {
272             @Override
273             public void onSuccess(final CommitInfo result) {
274                 LOG.trace("Successful update commit");
275             }
276
277             @Override
278             public void onFailure(final Throwable trw) {
279                 LOG.error("Failed update commit", trw);
280             }
281         }, MoreExecutors.directExecutor());
282     }
283
284     @Override
285     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
286             R extends Route & ChildOf<? super S> & Identifiable<I>,
287             I extends Identifier<R>> void refreshRibOut(final RouteEntryDependenciesContainer entryDep,
288             final List<StaleBestPathRoute<C, S, R, I>> staleRoutes, final List<AdvertizedRoute<C, S, R, I>> newRoutes) {
289         if (this.bindingChain == null) {
290             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
291             return;
292         }
293         final DOMDataTreeWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
294         final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
295         deleteRouteRibOut(ribSupport, staleRoutes, tx);
296         installRouteRibOut(entryDep, newRoutes, tx);
297
298         final FluentFuture<? extends CommitInfo> future = tx.commit();
299         this.submitted = future;
300         future.addCallback(new FutureCallback<CommitInfo>() {
301             @Override
302             public void onSuccess(final CommitInfo result) {
303                 LOG.trace("Successful update commit");
304             }
305
306             @Override
307             public void onFailure(final Throwable trw) {
308                 LOG.error("Failed update commit", trw);
309             }
310         }, MoreExecutors.directExecutor());
311     }
312
313     @Override
314     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
315             R extends Route & ChildOf<? super S> & Identifiable<I>,
316             I extends Identifier<R>> void reEvaluateAdvertizement(
317             final RouteEntryDependenciesContainer entryDep,
318             final List<ActualBestPathRoutes<C, S, R, I>> routesToStore) {
319         if (this.bindingChain == null) {
320             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
321             return;
322         }
323
324         final RIBSupport<C,S,R,I> ribSupport = entryDep.getRIBSupport();
325         final NodeIdentifierWithPredicates tk = ribSupport.tablesKey();
326         final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
327
328         final DOMDataTreeWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
329         for (final ActualBestPathRoutes<C, S, R, I> actualBestRoute : routesToStore) {
330             final PeerId fromPeerId = actualBestRoute.getFromPeerId();
331             if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
332                 continue;
333             }
334
335             final YangInstanceIdentifier tableRibout = getRibOutIId(tk);
336             // Stale Long-lived Graceful Restart routes should not be propagated
337             if (supportsLLGR() || !actualBestRoute.isDepreferenced()) {
338                 final YangInstanceIdentifier routePath = createRoutePath(ribSupport, tableRibout, actualBestRoute,
339                     addPathSupported);
340                 final MapEntryNode route = actualBestRoute.getRoute();
341                 final Optional<ContainerNode> effAttr = applyExportPolicy(entryDep, fromPeerId, route, routePath,
342                     actualBestRoute.getAttributes());
343                 if (effAttr.isPresent()) {
344                     storeRoute(ribSupport, actualBestRoute, route, routePath, effAttr.get(), tx);
345                     continue;
346                 }
347             }
348
349             deleteRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, tx);
350         }
351
352         final FluentFuture<? extends CommitInfo> future = tx.commit();
353         this.submitted = future;
354         future.addCallback(new FutureCallback<CommitInfo>() {
355             @Override
356             public void onSuccess(final CommitInfo result) {
357                 LOG.trace("Successful update commit");
358             }
359
360             @Override
361             public void onFailure(final Throwable trw) {
362                 LOG.error("Failed update commit", trw);
363             }
364         }, MoreExecutors.directExecutor());
365     }
366
367     private Optional<ContainerNode> applyExportPolicy(final RouteEntryDependenciesContainer entryDep,
368             final PeerId fromPeerId, final MapEntryNode route, final YangInstanceIdentifier routePath,
369             final ContainerNode attrs) {
370         final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
371         final RIBSupport<?, ?, ?, ?> ribSupport = entryDep.getRIBSupport();
372         final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer, this,
373             ribSupport.extractRouteKey(route.getIdentifier()), this.rtCache);
374
375         final Attributes bindingAttrs = ribSupport.attributeFromContainerNode(attrs);
376         final Optional<Attributes> optExportAttrs = entryDep.getRoutingPolicies().applyExportPolicies(routeEntry,
377             bindingAttrs, entryDep.getAfiSafType());
378         if (optExportAttrs.isEmpty()) {
379             // Discards route
380             return Optional.empty();
381         }
382         final Attributes exportAttrs = optExportAttrs.orElseThrow();
383
384         // If the same object is returned we can just reuse 'attrs' instead. Since we are in control of lifecycle here,
385         // we use identity comparison, as equality is too costly for the common case -- assuming export policy will not
386         // churn objects when it does not have to
387         return Optional.of(exportAttrs == bindingAttrs ? attrs
388             : ribSupport.attributeToContainerNode(routePath.node(ribSupport.routeAttributesIdentifier()), exportAttrs));
389     }
390
391     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
392             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void installRouteRibOut(
393                     final RouteEntryDependenciesContainer entryDep, final List<AdvertizedRoute<C, S, R, I>> routes,
394                     final DOMDataTreeWriteOperations tx) {
395         final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
396         final TablesKey tk = ribSupport.getTablesKey();
397         final BGPPeerTracker peerTracker = entryDep.getPeerTracker();
398         final boolean addPathSupported = supportsAddPathSupported(tk);
399         final YangInstanceIdentifier tableRibout = getRibOutIId(ribSupport.tablesKey());
400
401         for (final AdvertizedRoute<C, S, R, I> advRoute : routes) {
402             final PeerId fromPeerId = advRoute.getFromPeerId();
403             if (!filterRoutes(fromPeerId, tk) || !advRoute.isFirstBestPath() && !addPathSupported) {
404                 continue;
405             }
406             if (!supportsLLGR() && advRoute.isDepreferenced()) {
407                 // https://tools.ietf.org/html/draft-uttaro-idr-bgp-persistence-04#section-4.3
408                 //     o  The route SHOULD NOT be advertised to any neighbor from which the
409                 //        Long-lived Graceful Restart Capability has not been received.  The
410                 //        exception is described in the Optional Partial Deployment
411                 //        Procedure section (Section 4.7).  Note that this requirement
412                 //        implies that such routes should be withdrawn from any such
413                 //        neighbor.
414                 deleteRoute(ribSupport, addPathSupported, tableRibout, advRoute, tx);
415                 continue;
416             }
417
418             final Peer fromPeer = peerTracker.getPeer(fromPeerId);
419             final ContainerNode attributes = advRoute.getAttributes();
420             if (fromPeer != null && attributes != null) {
421                 final YangInstanceIdentifier routePath = createRoutePath(ribSupport, tableRibout, advRoute,
422                     addPathSupported);
423                 final MapEntryNode route = advRoute.getRoute();
424                 applyExportPolicy(entryDep, fromPeerId, route, routePath, attributes).ifPresent(
425                     attrs -> storeRoute(ribSupport, advRoute, route, routePath, attrs, tx));
426             }
427         }
428     }
429
430     private static YangInstanceIdentifier createRoutePath(final RIBSupport<?, ?, ?, ?> ribSupport,
431             final YangInstanceIdentifier tableRibout, final RouteKeyIdentifier<?, ?> advRoute,
432             final boolean withAddPath) {
433         return ribSupport.createRouteIdentifier(tableRibout,
434             withAddPath ? advRoute.getAddPathRouteKeyIdentifier() : advRoute.getNonAddPathRouteKeyIdentifier());
435     }
436
437     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
438             R extends Route & ChildOf<? super S> & Identifiable<I>,
439             I extends Identifier<R>> void deleteRouteRibOut(
440             final RIBSupport<C, S, R, I> ribSupport,
441             final List<StaleBestPathRoute<C, S, R, I>> staleRoutesIid,
442             final DOMDataTreeWriteOperations tx) {
443         final YangInstanceIdentifier tableRibout = getRibOutIId(ribSupport.tablesKey());
444         final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
445         staleRoutesIid.forEach(staleRouteIid
446             -> removeRoute(ribSupport, addPathSupported, tableRibout, staleRouteIid, tx));
447     }
448
449     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
450             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void storeRoute(
451                     final RIBSupport<C, S, R, I> ribSupport, final RouteKeyIdentifier<R, I> advRoute,
452                     final MapEntryNode route, final YangInstanceIdentifier routePath, final ContainerNode effAttr,
453                     final DOMDataTreeWriteOperations tx) {
454         LOG.debug("Write advRoute {} to peer AdjRibsOut {}", advRoute, getPeerId());
455         tx.put(LogicalDatastoreType.OPERATIONAL, routePath, ribSupport.createRoute(route,
456             (NodeIdentifierWithPredicates) routePath.getLastPathArgument(), effAttr));
457     }
458
459     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
460             R extends Route & ChildOf<? super S> & Identifiable<I>,
461             I extends Identifier<R>> void removeRoute(final RIBSupport<C, S, R, I> ribSupport,
462             final boolean addPathSupported, final YangInstanceIdentifier tableRibout,
463             final StaleBestPathRoute<C, S, R, I> staleRouteIid, final DOMDataTreeWriteOperations tx) {
464         if (addPathSupported) {
465             List<NodeIdentifierWithPredicates> staleRoutesIId = staleRouteIid.getAddPathRouteKeyIdentifiers();
466             for (final NodeIdentifierWithPredicates id : staleRoutesIId) {
467                 final YangInstanceIdentifier ribOutTarget = ribSupport.createRouteIdentifier(tableRibout, id);
468                 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
469                 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
470             }
471         } else {
472             if (!staleRouteIid.isNonAddPathBestPathNew()) {
473                 return;
474             }
475             final YangInstanceIdentifier ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
476                     staleRouteIid.getNonAddPathRouteKeyIdentifier());
477             LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
478             tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
479         }
480     }
481
482     // FIXME: why is this different from removeRoute()?
483     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
484             R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void deleteRoute(
485             final RIBSupport<C, S, R, I> ribSupport,  final boolean addPathSupported,
486             final YangInstanceIdentifier tableRibout,
487             final AbstractAdvertizedRoute<C, S , R, I> advRoute, final DOMDataTreeWriteOperations tx) {
488         final YangInstanceIdentifier ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
489             addPathSupported ? advRoute.getAddPathRouteKeyIdentifier() : advRoute.getNonAddPathRouteKeyIdentifier());
490         LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
491         tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
492     }
493
494     final synchronized void releaseBindingChain(final boolean isWaitForSubmitted) {
495         if (isWaitForSubmitted) {
496             if (this.submitted != null) {
497                 try {
498                     this.submitted.get();
499                 } catch (final InterruptedException | ExecutionException throwable) {
500                     LOG.error("Write routes failed", throwable);
501                 }
502             }
503         }
504         closeBindingChain();
505     }
506
507     private synchronized void closeBindingChain() {
508         if (this.bindingChain != null) {
509             LOG.info("Closing peer chain {}", getPeerId());
510             this.bindingChain.close();
511             this.bindingChain = null;
512         }
513     }
514
515     final synchronized void createDomChain() {
516         if (this.domChain == null) {
517             LOG.info("Creating DOM peer chain {}", getPeerId());
518             this.domChain = this.rib.createPeerDOMChain(this);
519         }
520     }
521
522     final synchronized void closeDomChain() {
523         if (this.domChain != null) {
524             LOG.info("Closing DOM peer chain {}", getPeerId());
525             this.domChain.close();
526             this.domChain = null;
527         }
528     }
529
530     boolean supportsLLGR() {
531         return false;
532     }
533 }