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.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;
20 import java.util.Optional;
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;
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;
73 final PeerRole peerRole;
74 private final ClusterIdentifier clusterId;
75 private final AsNumber localAs;
77 private DOMTransactionChain domChain;
80 DOMTransactionChain ribOutChain;
84 private FluentFuture<? extends CommitInfo> submitted;
85 RTCClientRouteCache rtCache = new RTCClientRouteCache();
89 final String peerName,
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;
110 final String peerName,
111 final String groupId,
113 final IpAddressNoZone neighborAddress,
114 final Set<TablesKey> afiSafisGracefulAdvertized) {
115 this(rib, peerName, groupId, role, null, null, neighborAddress,
116 rib.getLocalTablesKeys(), afiSafisGracefulAdvertized, Collections.emptyMap());
119 final synchronized FluentFuture<? extends CommitInfo> removePeer(final @Nullable YangInstanceIdentifier peerPath) {
120 if (peerPath == null) {
121 return CommitInfo.emptyFluentFuture();
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>() {
129 public void onSuccess(final CommitInfo result) {
130 LOG.debug("Peer {} removed", peerPath);
134 public void onFailure(final Throwable throwable) {
135 LOG.error("Failed to remove Peer {}", peerPath, throwable);
137 }, MoreExecutors.directExecutor());
141 synchronized YangInstanceIdentifier createPeerPath() {
142 return this.rib.getYangRibId().node(PEER_NID).node(IdentifierUtils.domPeerId(this.peerId));
146 public final synchronized PeerId getPeerId() {
151 public final PeerRole getRole() {
152 return this.peerRole;
156 public final synchronized byte[] getRawIdentifier() {
157 return Arrays.copyOf(this.rawIdentifier, this.rawIdentifier.length);
161 public final PeerRole getFromPeerRole() {
166 public final PeerId getFromPeerId() {
171 public final ClusterIdentifier getFromClusterId() {
172 return getClusterId();
176 public final void onTransactionChainSuccessful(final DOMTransactionChain chain) {
177 LOG.debug("Transaction chain {} successful.", chain);
181 public final BGPErrorHandlingState getBGPErrorHandlingState() {
186 public final BGPAfiSafiState getBGPAfiSafiState() {
191 public final AsNumber getFromPeerLocalAs() {
196 public final String getName() {
201 public final ClusterIdentifier getClusterId() {
202 return this.clusterId;
206 public final AsNumber getLocalAs() {
211 public synchronized DOMTransactionChain getDomChain() {
212 return this.domChain;
216 * Returns true if route can be send.
218 private boolean filterRoutes(final PeerId fromPeer, final TablesKey localTK) {
219 return supportsTable(localTK) && !fromPeer.equals(getPeerId());
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());
231 final RIBSupport<C, S> ribSupport = entryDep.getRIBSupport();
232 final YangInstanceIdentifier tableRibout = getRibOutIId(ribSupport.tablesKey());
233 final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
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
242 final PeerId fromPeerId = initRoute.getFromPeerId();
243 if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
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);
254 final YangInstanceIdentifier routePath = createRoutePath(ribSupport, tableRibout, initRoute,
256 applyExportPolicy(entryDep, fromPeerId, route, routePath, initRoute.getAttributes()).ifPresent(
257 attributes -> storeRoute(ribSupport, initRoute, route, routePath, attributes, tx));
260 final FluentFuture<? extends CommitInfo> future = tx.commit();
261 this.submitted = future;
262 future.addCallback(new FutureCallback<CommitInfo>() {
264 public void onSuccess(final CommitInfo result) {
265 LOG.trace("Successful update commit");
269 public void onFailure(final Throwable trw) {
270 LOG.error("Failed update commit", trw);
272 }, MoreExecutors.directExecutor());
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());
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);
288 final FluentFuture<? extends CommitInfo> future = tx.commit();
289 this.submitted = future;
290 future.addCallback(new FutureCallback<CommitInfo>() {
292 public void onSuccess(final CommitInfo result) {
293 LOG.trace("Successful update commit");
297 public void onFailure(final Throwable trw) {
298 LOG.error("Failed update commit", trw);
300 }, MoreExecutors.directExecutor());
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());
312 final RIBSupport<C, S> ribSupport = entryDep.getRIBSupport();
313 final NodeIdentifierWithPredicates tk = ribSupport.tablesKey();
314 final boolean addPathSupported = supportsAddPathSupported(ribSupport.getTablesKey());
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())) {
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,
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);
337 deleteRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, tx);
340 final FluentFuture<? extends CommitInfo> future = tx.commit();
341 this.submitted = future;
342 future.addCallback(new FutureCallback<CommitInfo>() {
344 public void onSuccess(final CommitInfo result) {
345 LOG.trace("Successful update commit");
349 public void onFailure(final Throwable trw) {
350 LOG.error("Failed update commit", trw);
352 }, MoreExecutors.directExecutor());
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);
363 final Attributes bindingAttrs = ribSupport.attributeFromContainerNode(attrs);
364 final Optional<Attributes> optExportAttrs = entryDep.getRoutingPolicies().applyExportPolicies(routeEntry,
365 bindingAttrs, entryDep.getAfiSafType());
366 if (optExportAttrs.isEmpty()) {
368 return Optional.empty();
370 final Attributes exportAttrs = optExportAttrs.orElseThrow();
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));
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());
388 for (final AdvertizedRoute<C, S> advRoute : routes) {
389 final PeerId fromPeerId = advRoute.getFromPeerId();
390 if (!filterRoutes(fromPeerId, tk) || !advRoute.isFirstBestPath() && !addPathSupported) {
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
401 deleteRoute(ribSupport, addPathSupported, tableRibout, advRoute, tx);
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,
410 final MapEntryNode route = advRoute.getRoute();
411 applyExportPolicy(entryDep, fromPeerId, route, routePath, attributes).ifPresent(
412 attrs -> storeRoute(ribSupport, advRoute, route, routePath, attrs, tx));
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());
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));
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));
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);
452 if (!staleRouteIid.isNonAddPathBestPathNew()) {
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);
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);
473 final synchronized void releaseRibOutChain(final boolean isWaitForSubmitted) {
474 if (isWaitForSubmitted) {
475 if (this.submitted != null) {
477 this.submitted.get();
478 } catch (final InterruptedException | ExecutionException throwable) {
479 LOG.error("Write routes failed", throwable);
484 if (this.ribOutChain != null) {
485 LOG.info("Closing peer chain {}", getPeerId());
486 this.ribOutChain.close();
487 this.ribOutChain = null;
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);
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;
506 boolean supportsLLGR() {