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