2 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
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
8 package org.opendaylight.protocol.bgp.rib.impl;
10 import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.PEER_NID;
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;
19 import java.util.Optional;
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;
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;
77 final PeerRole peerRole;
78 private final ClusterIdentifier clusterId;
79 private final AsNumber localAs;
81 private DOMTransactionChain domChain;
83 TransactionChain bindingChain;
87 private FluentFuture<? extends CommitInfo> submitted;
88 RTCClientRouteCache rtCache = new RTCClientRouteCache();
92 final String peerName,
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;
113 final String peerName,
114 final String groupId,
116 final IpAddressNoZone neighborAddress,
117 final Set<TablesKey> afiSafisGracefulAdvertized) {
118 this(rib, peerName, groupId, role, null, null, neighborAddress,
119 rib.getLocalTablesKeys(), afiSafisGracefulAdvertized, Collections.emptyMap());
122 final synchronized FluentFuture<? extends CommitInfo> removePeer(final @Nullable YangInstanceIdentifier peerPath) {
123 if (peerPath == null) {
124 return CommitInfo.emptyFluentFuture();
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>() {
132 public void onSuccess(final CommitInfo result) {
133 LOG.debug("Peer {} removed", peerPath);
137 public void onFailure(final Throwable throwable) {
138 LOG.error("Failed to remove Peer {}", peerPath, throwable);
140 }, MoreExecutors.directExecutor());
144 synchronized YangInstanceIdentifier createPeerPath() {
145 return this.rib.getYangRibId().node(PEER_NID).node(IdentifierUtils.domPeerId(this.peerId));
149 public final synchronized PeerId getPeerId() {
154 public final PeerRole getRole() {
155 return this.peerRole;
159 public final synchronized byte[] getRawIdentifier() {
160 return Arrays.copyOf(this.rawIdentifier, this.rawIdentifier.length);
164 public final PeerRole getFromPeerRole() {
169 public final PeerId getFromPeerId() {
174 public final ClusterIdentifier getFromClusterId() {
175 return getClusterId();
179 public final void onTransactionChainSuccessful(final DOMTransactionChain chain) {
180 LOG.debug("Transaction chain {} successful.", chain);
184 public final void onTransactionChainSuccessful(final TransactionChain chain) {
185 LOG.debug("Transaction chain {} successful.", chain);
189 public final BGPErrorHandlingState getBGPErrorHandlingState() {
194 public final BGPAfiSafiState getBGPAfiSafiState() {
199 public final AsNumber getFromPeerLocalAs() {
204 public final String getName() {
209 public final ClusterIdentifier getClusterId() {
210 return this.clusterId;
214 public final AsNumber getLocalAs() {
219 public synchronized DOMTransactionChain getDomChain() {
220 return this.domChain;
224 * Returns true if route can be send.
226 private boolean filterRoutes(final PeerId fromPeer, final TablesKey localTK) {
227 return supportsTable(localTK) && !fromPeer.equals(getPeerId());
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());
240 final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
241 final YangInstanceIdentifier tableRibout = getRibOutIId(ribSupport.tablesKey());
242 final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
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
251 final PeerId fromPeerId = initRoute.getFromPeerId();
252 if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
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);
263 final YangInstanceIdentifier routePath = createRoutePath(ribSupport, tableRibout, initRoute,
265 applyExportPolicy(entryDep, fromPeerId, route, routePath, initRoute.getAttributes()).ifPresent(
266 attributes -> storeRoute(ribSupport, initRoute, route, routePath, attributes, tx));
269 final FluentFuture<? extends CommitInfo> future = tx.commit();
270 this.submitted = future;
271 future.addCallback(new FutureCallback<CommitInfo>() {
273 public void onSuccess(final CommitInfo result) {
274 LOG.trace("Successful update commit");
278 public void onFailure(final Throwable trw) {
279 LOG.error("Failed update commit", trw);
281 }, MoreExecutors.directExecutor());
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());
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);
298 final FluentFuture<? extends CommitInfo> future = tx.commit();
299 this.submitted = future;
300 future.addCallback(new FutureCallback<CommitInfo>() {
302 public void onSuccess(final CommitInfo result) {
303 LOG.trace("Successful update commit");
307 public void onFailure(final Throwable trw) {
308 LOG.error("Failed update commit", trw);
310 }, MoreExecutors.directExecutor());
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());
324 final RIBSupport<C,S,R,I> ribSupport = entryDep.getRIBSupport();
325 final NodeIdentifierWithPredicates tk = ribSupport.tablesKey();
326 final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
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())) {
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,
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);
349 deleteRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, tx);
352 final FluentFuture<? extends CommitInfo> future = tx.commit();
353 this.submitted = future;
354 future.addCallback(new FutureCallback<CommitInfo>() {
356 public void onSuccess(final CommitInfo result) {
357 LOG.trace("Successful update commit");
361 public void onFailure(final Throwable trw) {
362 LOG.error("Failed update commit", trw);
364 }, MoreExecutors.directExecutor());
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);
375 final Attributes bindingAttrs = ribSupport.attributeFromContainerNode(attrs);
376 final Optional<Attributes> optExportAttrs = entryDep.getRoutingPolicies().applyExportPolicies(routeEntry,
377 bindingAttrs, entryDep.getAfiSafType());
378 if (optExportAttrs.isEmpty()) {
380 return Optional.empty();
382 final Attributes exportAttrs = optExportAttrs.orElseThrow();
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));
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());
401 for (final AdvertizedRoute<C, S, R, I> advRoute : routes) {
402 final PeerId fromPeerId = advRoute.getFromPeerId();
403 if (!filterRoutes(fromPeerId, tk) || !advRoute.isFirstBestPath() && !addPathSupported) {
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
414 deleteRoute(ribSupport, addPathSupported, tableRibout, advRoute, tx);
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,
423 final MapEntryNode route = advRoute.getRoute();
424 applyExportPolicy(entryDep, fromPeerId, route, routePath, attributes).ifPresent(
425 attrs -> storeRoute(ribSupport, advRoute, route, routePath, attrs, tx));
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());
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));
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));
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);
472 if (!staleRouteIid.isNonAddPathBestPathNew()) {
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);
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);
494 final synchronized void releaseBindingChain(final boolean isWaitForSubmitted) {
495 if (isWaitForSubmitted) {
496 if (this.submitted != null) {
498 this.submitted.get();
499 } catch (final InterruptedException | ExecutionException throwable) {
500 LOG.error("Write routes failed", throwable);
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;
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);
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;
530 boolean supportsLLGR() {