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