Bump upstreams
[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.annotations.VisibleForTesting;
13 import com.google.common.util.concurrent.FluentFuture;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.Set;
22 import java.util.concurrent.ExecutionException;
23 import org.checkerframework.checker.lock.qual.GuardedBy;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.mdsal.common.api.CommitInfo;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
29 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
30 import org.opendaylight.protocol.bgp.mode.impl.BGPRouteEntryExportParametersImpl;
31 import org.opendaylight.protocol.bgp.rib.impl.spi.PeerTransactionChain;
32 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
33 import org.opendaylight.protocol.bgp.rib.impl.state.BGPPeerStateImpl;
34 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
35 import org.opendaylight.protocol.bgp.rib.spi.IdentifierUtils;
36 import org.opendaylight.protocol.bgp.rib.spi.Peer;
37 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
38 import org.opendaylight.protocol.bgp.rib.spi.entry.AbstractAdvertizedRoute;
39 import org.opendaylight.protocol.bgp.rib.spi.entry.ActualBestPathRoutes;
40 import org.opendaylight.protocol.bgp.rib.spi.entry.AdvertizedRoute;
41 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteEntryDependenciesContainer;
42 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteKeyIdentifier;
43 import org.opendaylight.protocol.bgp.rib.spi.entry.StaleBestPathRoute;
44 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryExportParameters;
45 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
46 import org.opendaylight.protocol.bgp.rib.spi.state.BGPAfiSafiState;
47 import org.opendaylight.protocol.bgp.rib.spi.state.BGPErrorHandlingState;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressNoZone;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.ClusterIdentifier;
57 import org.opendaylight.yangtools.yang.binding.ChildOf;
58 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
59 import org.opendaylight.yangtools.yang.binding.DataObject;
60 import org.opendaylight.yangtools.yang.common.Empty;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
63 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
64 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 abstract class AbstractPeer extends BGPPeerStateImpl implements BGPRouteEntryImportParameters, Peer,
69         PeerTransactionChain, FutureCallback<Empty> {
70     private static final Logger LOG = LoggerFactory.getLogger(AbstractPeer.class);
71
72     final RTCClientRouteCache rtCache = new RTCClientRouteCache();
73     final RIB rib;
74
75     private final ClusterIdentifier clusterId;
76     private final PeerRole peerRole;
77     private final AsNumber localAs;
78     private final String name;
79
80     // FIXME: Revisit locking here to improve concurrency:
81     //        -- identifiers, peerId are a shared resource
82     //        -- domChain seems to really be 'ribInChain', accessed from netty thread
83     //        -- ribOutChain is accessed from LocRibWriter
84     //        hence we want to use the two chains concurrently. The problem is their lifecycle in response to errors,
85     //        which needs figuring out.
86     @GuardedBy("this")
87     private DOMTransactionChain domChain;
88     // FIXME: This is an invariant once the peer is 'resolved' -- which happens instantaneously for ApplicationPeer.
89     //        There are also a number YangInstanceIdentifiers which are tied to it. We want to keep all of them in one
90     //        structure for isolation. This could be a separate DTO (JDK16 record) or isolated into an abstract behavior
91     //        class.
92     @GuardedBy("this")
93     PeerId peerId;
94
95     // These seem to be separate
96     @GuardedBy("this")
97     @VisibleForTesting
98     DOMTransactionChain ribOutChain;
99     @GuardedBy("this")
100     private FluentFuture<? extends CommitInfo> submitted;
101
102     @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR",
103         justification = "False positive on synchronized createDomChain()")
104     AbstractPeer(
105             final RIB rib,
106             final String peerName,
107             final String groupId,
108             final PeerRole role,
109             final @Nullable ClusterIdentifier clusterId,
110             final @Nullable AsNumber localAs,
111             final IpAddressNoZone neighborAddress,
112             final Set<TablesKey> afiSafisAdvertized,
113             final Set<TablesKey> afiSafisGracefulAdvertized,
114             final Map<TablesKey, Integer> afiSafisLlGracefulAdvertized) {
115         super(rib.getInstanceIdentifier(), groupId, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized,
116                 afiSafisLlGracefulAdvertized);
117         name = peerName;
118         peerRole = role;
119         this.clusterId = clusterId;
120         this.localAs = localAs;
121         this.rib = rib;
122         createDomChain();
123     }
124
125     AbstractPeer(
126             final RIB rib,
127             final String peerName,
128             final String groupId,
129             final PeerRole role,
130             final IpAddressNoZone neighborAddress,
131             final Set<TablesKey> afiSafisGracefulAdvertized) {
132         this(rib, peerName, groupId, role, null, null, neighborAddress,
133                 rib.getLocalTablesKeys(), afiSafisGracefulAdvertized, Collections.emptyMap());
134     }
135
136     final synchronized FluentFuture<? extends CommitInfo> removePeer(final @Nullable YangInstanceIdentifier peerPath) {
137         if (peerPath == null) {
138             return CommitInfo.emptyFluentFuture();
139         }
140         LOG.info("Closed per Peer {} removed", peerPath);
141         final DOMDataTreeWriteTransaction tx = domChain.newWriteOnlyTransaction();
142         tx.delete(LogicalDatastoreType.OPERATIONAL, peerPath);
143         final FluentFuture<? extends CommitInfo> future = tx.commit();
144         future.addCallback(new FutureCallback<CommitInfo>() {
145             @Override
146             public void onSuccess(final CommitInfo result) {
147                 LOG.debug("Peer {} removed", peerPath);
148             }
149
150             @Override
151             public void onFailure(final Throwable throwable) {
152                 LOG.error("Failed to remove Peer {}", peerPath, throwable);
153             }
154         }, MoreExecutors.directExecutor());
155         return future;
156     }
157
158     final YangInstanceIdentifier createPeerPath(final PeerId newPeerId) {
159         return rib.getYangRibId().node(PEER_NID).node(IdentifierUtils.domPeerId(newPeerId));
160     }
161
162     @Override
163     public final synchronized PeerId getPeerId() {
164         return peerId;
165     }
166
167     @Override
168     public final PeerRole getRole() {
169         return peerRole;
170     }
171
172     @Override
173     public final PeerRole getFromPeerRole() {
174         return getRole();
175     }
176
177     @Override
178     public final PeerId getFromPeerId() {
179         return getPeerId();
180     }
181
182     @Override
183     public final ClusterIdentifier getFromClusterId() {
184         return getClusterId();
185     }
186
187     @Override
188     public final void onSuccess(final Empty value) {
189         LOG.debug("Transaction chain successful");
190     }
191
192     @Override
193     public final BGPErrorHandlingState getBGPErrorHandlingState() {
194         return this;
195     }
196
197     @Override
198     public final BGPAfiSafiState getBGPAfiSafiState() {
199         return this;
200     }
201
202     @Override
203     public final AsNumber getFromPeerLocalAs() {
204         return getLocalAs();
205     }
206
207     @Override
208     public final String getName() {
209         return name;
210     }
211
212     @Override
213     public final ClusterIdentifier getClusterId() {
214         return clusterId;
215     }
216
217     @Override
218     public final AsNumber getLocalAs() {
219         return localAs;
220     }
221
222     @Override
223     public synchronized DOMTransactionChain getDomChain() {
224         return domChain;
225     }
226
227     /**
228      * Returns true if route can be send.
229      */
230     private boolean filterRoutes(final PeerId fromPeer, final TablesKey localTK) {
231         return supportsTable(localTK) && !fromPeer.equals(getPeerId());
232     }
233
234     @Override
235     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>>
236             void initializeRibOut(final RouteEntryDependenciesContainer entryDep,
237                     final List<ActualBestPathRoutes<C, S>> routesToStore) {
238         if (ribOutChain == null) {
239             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
240             return;
241         }
242
243         final RIBSupport<C, S> ribSupport = entryDep.getRIBSupport();
244         final YangInstanceIdentifier tableRibout = getRibOutIId(ribSupport.tablesKey());
245         final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
246
247         final DOMDataTreeWriteTransaction tx = ribOutChain.newWriteOnlyTransaction();
248         for (final ActualBestPathRoutes<C, S> initRoute : routesToStore) {
249             if (!supportsLLGR() && initRoute.isDepreferenced()) {
250                 // Stale Long-lived Graceful Restart routes should not be propagated
251                 continue;
252             }
253
254             final PeerId fromPeerId = initRoute.getFromPeerId();
255             if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
256                 continue;
257             }
258
259             final MapEntryNode route = initRoute.getRoute();
260             final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
261             if (fromPeer == null) {
262                 LOG.debug("Failed to acquire peer structure for {}, ignoring route {}", fromPeerId, initRoute);
263                 continue;
264             }
265
266             final YangInstanceIdentifier routePath = createRoutePath(ribSupport, tableRibout, initRoute,
267                 addPathSupported);
268             applyExportPolicy(entryDep, fromPeerId, route, routePath, initRoute.getAttributes()).ifPresent(
269                 attributes -> storeRoute(ribSupport, initRoute, route, routePath, attributes, tx));
270         }
271
272         final FluentFuture<? extends CommitInfo> future = tx.commit();
273         submitted = future;
274         future.addCallback(new FutureCallback<CommitInfo>() {
275             @Override
276             public void onSuccess(final CommitInfo result) {
277                 LOG.trace("Successful update commit");
278             }
279
280             @Override
281             public void onFailure(final Throwable trw) {
282                 LOG.error("Failed update commit", trw);
283             }
284         }, MoreExecutors.directExecutor());
285     }
286
287     @Override
288     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>>
289             void refreshRibOut(final RouteEntryDependenciesContainer entryDep,
290                 final List<StaleBestPathRoute> staleRoutes, final List<AdvertizedRoute<C, S>> newRoutes) {
291         if (ribOutChain == null) {
292             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
293             return;
294         }
295         final DOMDataTreeWriteTransaction tx = ribOutChain.newWriteOnlyTransaction();
296         final RIBSupport<C, S> ribSupport = entryDep.getRIBSupport();
297         deleteRouteRibOut(ribSupport, staleRoutes, tx);
298         installRouteRibOut(entryDep, newRoutes, tx);
299
300         final FluentFuture<? extends CommitInfo> future = tx.commit();
301         submitted = future;
302         future.addCallback(new FutureCallback<CommitInfo>() {
303             @Override
304             public void onSuccess(final CommitInfo result) {
305                 LOG.trace("Successful update commit");
306             }
307
308             @Override
309             public void onFailure(final Throwable trw) {
310                 LOG.error("Failed update commit", trw);
311             }
312         }, MoreExecutors.directExecutor());
313     }
314
315     @Override
316     public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>>
317             void reEvaluateAdvertizement(final RouteEntryDependenciesContainer entryDep,
318                 final List<ActualBestPathRoutes<C, S>> routesToStore) {
319         if (ribOutChain == null) {
320             LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
321             return;
322         }
323
324         final RIBSupport<C, S> ribSupport = entryDep.getRIBSupport();
325         final NodeIdentifierWithPredicates tk = ribSupport.tablesKey();
326         final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
327
328         final DOMDataTreeWriteTransaction tx = ribOutChain.newWriteOnlyTransaction();
329         for (final ActualBestPathRoutes<C, S> 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.orElseThrow(), tx);
345                     continue;
346                 }
347             }
348
349             deleteRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, tx);
350         }
351
352         final FluentFuture<? extends CommitInfo> future = tx.commit();
353         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.name()), 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>> void installRouteRibOut(
392             final RouteEntryDependenciesContainer entryDep, final List<AdvertizedRoute<C, S>> routes,
393             final DOMDataTreeWriteOperations tx) {
394         final RIBSupport<C, S> ribSupport = entryDep.getRIBSupport();
395         final TablesKey tk = ribSupport.getTablesKey();
396         final BGPPeerTracker peerTracker = entryDep.getPeerTracker();
397         final boolean addPathSupported = supportsAddPathSupported(tk);
398         final YangInstanceIdentifier tableRibout = getRibOutIId(ribSupport.tablesKey());
399
400         for (final AdvertizedRoute<C, S> advRoute : routes) {
401             final PeerId fromPeerId = advRoute.getFromPeerId();
402             if (!filterRoutes(fromPeerId, tk) || !advRoute.isFirstBestPath() && !addPathSupported) {
403                 continue;
404             }
405             if (!supportsLLGR() && advRoute.isDepreferenced()) {
406                 // https://tools.ietf.org/html/draft-uttaro-idr-bgp-persistence-04#section-4.3
407                 //     o  The route SHOULD NOT be advertised to any neighbor from which the
408                 //        Long-lived Graceful Restart Capability has not been received.  The
409                 //        exception is described in the Optional Partial Deployment
410                 //        Procedure section (Section 4.7).  Note that this requirement
411                 //        implies that such routes should be withdrawn from any such
412                 //        neighbor.
413                 deleteRoute(ribSupport, addPathSupported, tableRibout, advRoute, tx);
414                 continue;
415             }
416
417             final Peer fromPeer = peerTracker.getPeer(fromPeerId);
418             final ContainerNode attributes = advRoute.getAttributes();
419             if (fromPeer != null && attributes != null) {
420                 final YangInstanceIdentifier routePath = createRoutePath(ribSupport, tableRibout, advRoute,
421                     addPathSupported);
422                 final MapEntryNode route = advRoute.getRoute();
423                 applyExportPolicy(entryDep, fromPeerId, route, routePath, attributes).ifPresent(
424                     attrs -> storeRoute(ribSupport, advRoute, route, routePath, attrs, tx));
425             }
426         }
427     }
428
429     private static YangInstanceIdentifier createRoutePath(final RIBSupport<?, ?> ribSupport,
430             final YangInstanceIdentifier tableRibout, final RouteKeyIdentifier advRoute, final boolean withAddPath) {
431         return ribSupport.createRouteIdentifier(tableRibout,
432             withAddPath ? advRoute.getAddPathRouteKeyIdentifier() : advRoute.getNonAddPathRouteKeyIdentifier());
433     }
434
435     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>>
436             void deleteRouteRibOut(final RIBSupport<C, S> ribSupport, final List<StaleBestPathRoute> staleRoutesIid,
437                 final DOMDataTreeWriteOperations tx) {
438         final YangInstanceIdentifier tableRibout = getRibOutIId(ribSupport.tablesKey());
439         final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
440         staleRoutesIid.forEach(staleRouteIid
441             -> removeRoute(ribSupport, addPathSupported, tableRibout, staleRouteIid, tx));
442     }
443
444     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>> void storeRoute(
445             final RIBSupport<C, S> ribSupport, final RouteKeyIdentifier advRoute, final MapEntryNode route,
446             final YangInstanceIdentifier routePath, final ContainerNode effAttr, final DOMDataTreeWriteOperations tx) {
447         LOG.debug("Write advRoute {} to peer AdjRibsOut {}", advRoute, getPeerId());
448         tx.put(LogicalDatastoreType.OPERATIONAL, routePath, ribSupport.createRoute(route,
449             (NodeIdentifierWithPredicates) routePath.getLastPathArgument(), effAttr));
450     }
451
452     private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>>
453             void removeRoute(final RIBSupport<C, S> ribSupport, final boolean addPathSupported,
454                 final YangInstanceIdentifier tableRibout, final StaleBestPathRoute staleRouteIid,
455                 final DOMDataTreeWriteOperations tx) {
456         if (addPathSupported) {
457             List<NodeIdentifierWithPredicates> staleRoutesIId = staleRouteIid.getAddPathRouteKeyIdentifiers();
458             for (final NodeIdentifierWithPredicates id : staleRoutesIId) {
459                 final YangInstanceIdentifier ribOutTarget = ribSupport.createRouteIdentifier(tableRibout, id);
460                 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
461                 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
462             }
463         } else {
464             if (!staleRouteIid.isNonAddPathBestPathNew()) {
465                 return;
466             }
467             final YangInstanceIdentifier ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
468                     staleRouteIid.getNonAddPathRouteKeyIdentifier());
469             LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
470             tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
471         }
472     }
473
474     // FIXME: why is this different from removeRoute()?
475     private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>> void deleteRoute(
476             final RIBSupport<C, S> ribSupport,  final boolean addPathSupported,
477             final YangInstanceIdentifier tableRibout, final AbstractAdvertizedRoute<C, S> advRoute,
478             final DOMDataTreeWriteOperations tx) {
479         final YangInstanceIdentifier ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
480             addPathSupported ? advRoute.getAddPathRouteKeyIdentifier() : advRoute.getNonAddPathRouteKeyIdentifier());
481         LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
482         tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
483     }
484
485     // FIXME: make this asynchronous?
486     final synchronized void releaseRibOutChain(final boolean isWaitForSubmitted) {
487         if (isWaitForSubmitted) {
488             if (submitted != null) {
489                 try {
490                     submitted.get();
491                 } catch (final InterruptedException | ExecutionException throwable) {
492                     LOG.error("Write routes failed", throwable);
493                 }
494             }
495         }
496
497         if (ribOutChain != null) {
498             LOG.info("Closing peer chain {}", getPeerId());
499             ribOutChain.close();
500             ribOutChain = null;
501         }
502     }
503
504     final synchronized void createDomChain() {
505         if (domChain == null) {
506             LOG.info("Creating DOM peer chain {}", getPeerId());
507             domChain = rib.createPeerDOMChain();
508             domChain.addCallback(this);
509         }
510     }
511
512     final synchronized void closeDomChain() {
513         if (domChain != null) {
514             LOG.info("Closing DOM peer chain {}", getPeerId());
515             domChain.close();
516             domChain = null;
517         }
518     }
519
520     boolean supportsLLGR() {
521         return false;
522     }
523 }