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.binding.api.WriteTransaction;
27 import org.opendaylight.mdsal.common.api.CommitInfo;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
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.BGPRibRoutingPolicy;
47 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryExportParameters;
48 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryImportParameters;
49 import org.opendaylight.protocol.bgp.rib.spi.state.BGPAfiSafiState;
50 import org.opendaylight.protocol.bgp.rib.spi.state.BGPErrorHandlingState;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.ClusterIdentifier;
61 import org.opendaylight.yangtools.yang.binding.ChildOf;
62 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
63 import org.opendaylight.yangtools.yang.binding.DataObject;
64 import org.opendaylight.yangtools.yang.binding.Identifiable;
65 import org.opendaylight.yangtools.yang.binding.Identifier;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
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 IpAddress 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 IpAddress 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 TablesKey tk = entryDep.getRIBSupport().getTablesKey();
242 final boolean addPathSupported = supportsAddPathSupported(tk);
244 final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
245 for (final ActualBestPathRoutes<C, S, R, I> initializingRoute : routesToStore) {
246 if (!supportsLLGR() && initializingRoute.isDepreferenced()) {
247 // Stale Long-lived Graceful Restart routes should not be propagated
251 final PeerId fromPeerId = initializingRoute.getFromPeerId();
252 if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
256 final R route = initializingRoute.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, initializingRoute);
263 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
264 this, route.getRouteKey(), this.rtCache);
266 final Optional<Attributes> effAttr = entryDep.getRoutingPolicies()
267 .applyExportPolicies(routeEntry, initializingRoute.getAttributes(), entryDep.getAfiSafType());
268 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
270 effAttr.ifPresent(attributes
271 -> storeRoute(ribSupport, addPathSupported, tableRibout, initializingRoute, route, attributes, tx));
274 final FluentFuture<? extends CommitInfo> future = tx.commit();
275 this.submitted = future;
276 future.addCallback(new FutureCallback<CommitInfo>() {
278 public void onSuccess(final CommitInfo result) {
279 LOG.trace("Successful update commit");
283 public void onFailure(final Throwable trw) {
284 LOG.error("Failed update commit", trw);
286 }, MoreExecutors.directExecutor());
290 public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
291 R extends Route & ChildOf<? super S> & Identifiable<I>,
292 I extends Identifier<R>> void refreshRibOut(final RouteEntryDependenciesContainer entryDep,
293 final List<StaleBestPathRoute<C, S, R, I>> staleRoutes, final List<AdvertizedRoute<C, S, R, I>> newRoutes) {
294 if (this.bindingChain == null) {
295 LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
298 final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
299 final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
300 deleteRouteRibOut(ribSupport, staleRoutes, tx);
301 installRouteRibOut(entryDep, newRoutes, tx);
303 final FluentFuture<? extends CommitInfo> future = tx.commit();
304 this.submitted = future;
305 future.addCallback(new FutureCallback<CommitInfo>() {
307 public void onSuccess(final CommitInfo result) {
308 LOG.trace("Successful update commit");
312 public void onFailure(final Throwable trw) {
313 LOG.error("Failed update commit", trw);
315 }, MoreExecutors.directExecutor());
319 public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
320 R extends Route & ChildOf<? super S> & Identifiable<I>,
321 I extends Identifier<R>> void reEvaluateAdvertizement(
322 final RouteEntryDependenciesContainer entryDep,
323 final List<ActualBestPathRoutes<C, S, R, I>> routesToStore) {
324 if (this.bindingChain == null) {
325 LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
329 final RIBSupport<C,S,R,I> ribSupport = entryDep.getRIBSupport();
330 final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
331 final boolean addPathSupported = supportsAddPathSupported(tk);
333 final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
334 for (final ActualBestPathRoutes<C, S, R, I> actualBestRoute : routesToStore) {
335 final PeerId fromPeerId = actualBestRoute.getFromPeerId();
336 if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
340 final R route = actualBestRoute.getRoute();
341 final Optional<Attributes> effAttr;
342 if (supportsLLGR() || !actualBestRoute.isDepreferenced()) {
343 final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
344 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
345 this, route.getRouteKey(), this.rtCache);
346 effAttr = entryDep.getRoutingPolicies()
347 .applyExportPolicies(routeEntry, actualBestRoute.getAttributes(), entryDep.getAfiSafType());
349 // Stale Long-lived Graceful Restart routes should not be propagated
350 effAttr = Optional.empty();
353 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
354 if (effAttr.isPresent()) {
355 storeRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, route, effAttr.get(), tx);
357 deleteRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, tx);
361 final FluentFuture<? extends CommitInfo> future = tx.commit();
362 this.submitted = future;
363 future.addCallback(new FutureCallback<CommitInfo>() {
365 public void onSuccess(final CommitInfo result) {
366 LOG.trace("Successful update commit");
370 public void onFailure(final Throwable trw) {
371 LOG.error("Failed update commit", trw);
373 }, MoreExecutors.directExecutor());
376 private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
377 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void installRouteRibOut(
378 final RouteEntryDependenciesContainer entryDep, final List<AdvertizedRoute<C, S, R, I>> routes,
379 final WriteTransaction tx) {
380 final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
381 final BGPPeerTracker peerTracker = entryDep.getPeerTracker();
382 final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
383 final BGPRibRoutingPolicy routingPolicies = entryDep.getRoutingPolicies();
384 final boolean addPathSupported = supportsAddPathSupported(tk);
385 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
387 for (final AdvertizedRoute<C, S, R, I> advRoute : routes) {
388 final PeerId fromPeerId = advRoute.getFromPeerId();
389 if (!filterRoutes(fromPeerId, tk) || !advRoute.isFirstBestPath() && !addPathSupported) {
392 if (!supportsLLGR() && advRoute.isDepreferenced()) {
393 // https://tools.ietf.org/html/draft-uttaro-idr-bgp-persistence-04#section-4.3
394 // o The route SHOULD NOT be advertised to any neighbor from which the
395 // Long-lived Graceful Restart Capability has not been received. The
396 // exception is described in the Optional Partial Deployment
397 // Procedure section (Section 4.7). Note that this requirement
398 // implies that such routes should be withdrawn from any such
400 deleteRoute(ribSupport, addPathSupported, tableRibout, advRoute, tx);
404 final R route = advRoute.getRoute();
405 Optional<Attributes> effAttr = Optional.empty();
406 final Peer fromPeer = peerTracker.getPeer(fromPeerId);
407 final Attributes attributes = advRoute.getAttributes();
408 if (fromPeer != null && attributes != null) {
409 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
410 this, route.getRouteKey(), this.rtCache);
411 effAttr = routingPolicies.applyExportPolicies(routeEntry, attributes, entryDep.getAfiSafType());
413 effAttr.ifPresent(attributes1
414 -> storeRoute(ribSupport, addPathSupported, tableRibout, advRoute, route, attributes1, tx));
418 private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
419 R extends Route & ChildOf<? super S> & Identifiable<I>,
420 I extends Identifier<R>> void deleteRouteRibOut(
421 final RIBSupport<C, S, R, I> ribSupport,
422 final List<StaleBestPathRoute<C, S, R, I>> staleRoutesIid,
423 final WriteTransaction tx) {
424 final TablesKey tk = ribSupport.getTablesKey();
425 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
426 final boolean addPathSupported = supportsAddPathSupported(tk);
427 staleRoutesIid.forEach(staleRouteIid
428 -> removeRoute(ribSupport, addPathSupported, tableRibout, staleRouteIid, tx));
431 private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
432 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void storeRoute(
433 final RIBSupport<C, S, R, I> ribSupport, final boolean addPathSupported,
434 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
435 final RouteKeyIdentifier<R, I> advRoute, final R route, final Attributes effAttr,
436 final WriteTransaction tx) {
437 final InstanceIdentifier<R> ribOut;
439 if (!addPathSupported) {
440 ribOut = ribSupport.createRouteIdentifier(tableRibout, advRoute.getNonAddPathRouteKeyIdentifier());
441 newKey = ribSupport.createRouteListKey(route.getRouteKey());
443 ribOut = ribSupport.createRouteIdentifier(tableRibout, advRoute.getAddPathRouteKeyIdentifier());
444 newKey = ribSupport.createRouteListKey(route.getPathId(), route.getRouteKey());
447 final R newRoute = ribSupport.createRoute(route, newKey, effAttr);
448 LOG.debug("Write advRoute {} to peer AdjRibsOut {}", advRoute, getPeerId());
449 tx.put(LogicalDatastoreType.OPERATIONAL, ribOut, newRoute);
452 private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
453 R extends Route & ChildOf<? super S> & Identifiable<I>,
454 I extends Identifier<R>> void removeRoute(final RIBSupport<C, S, R, I> ribSupport,
455 final boolean addPathSupported, final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
456 final StaleBestPathRoute<C, S, R, I> staleRouteIid, final WriteTransaction tx) {
457 if (addPathSupported) {
458 List<I> staleRoutesIId = staleRouteIid.getAddPathRouteKeyIdentifiers();
459 for (final I id : staleRoutesIId) {
460 final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout, id);
461 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
462 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
465 if (!staleRouteIid.isNonAddPathBestPathNew()) {
468 final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
469 staleRouteIid.getNonAddPathRouteKeyIdentifier());
470 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
471 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
475 // FIXME: why is this different from removeRoute()?
476 private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
477 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void deleteRoute(
478 final RIBSupport<C, S, R, I> ribSupport, final boolean addPathSupported,
479 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
480 final AbstractAdvertizedRoute<C, S , R, I> advRoute, final WriteTransaction tx) {
481 final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
482 addPathSupported ? advRoute.getAddPathRouteKeyIdentifier() : advRoute.getNonAddPathRouteKeyIdentifier());
483 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
484 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
487 final synchronized void releaseBindingChain() {
488 if (this.submitted != null) {
490 this.submitted.get();
491 } catch (final InterruptedException | ExecutionException throwable) {
492 LOG.error("Write routes failed", throwable);
498 private synchronized void closeBindingChain() {
499 if (this.bindingChain != null) {
500 LOG.info("Closing peer chain {}", getPeerId());
501 this.bindingChain.close();
502 this.bindingChain = null;
506 final synchronized void createDomChain() {
507 if (this.domChain == null) {
508 LOG.info("Creating DOM peer chain {}", getPeerId());
509 this.domChain = this.rib.createPeerDOMChain(this);
513 final synchronized void closeDomChain() {
514 if (this.domChain != null) {
515 LOG.info("Closing DOM peer chain {}", getPeerId());
516 this.domChain.close();
517 this.domChain = null;
521 boolean supportsLLGR() {