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 javax.annotation.Nullable;
23 import javax.annotation.concurrent.GuardedBy;
24 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
28 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
30 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
31 import org.opendaylight.mdsal.common.api.CommitInfo;
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 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 BindingTransactionChain bindingChain;
87 private FluentFuture<? extends CommitInfo> submitted;
88 RTCClientRouteCache rtCache = new RTCClientRouteCache();
92 final String peerName,
95 @Nullable final ClusterIdentifier clusterId,
96 @Nullable final 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;
108 this.domChain = this.rib.createPeerDOMChain(this);
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(@Nullable final YangInstanceIdentifier peerPath) {
123 if (peerPath == null) {
124 return CommitInfo.emptyFluentFuture();
126 LOG.info("Closed per Peer {} removed", peerPath);
127 final DOMDataWriteTransaction 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 TransactionChain<?, ?> chain) {
180 LOG.debug("Transaction chain {} successful.", chain);
184 public final BGPErrorHandlingState getBGPErrorHandlingState() {
189 public final BGPAfiSafiState getBGPAfiSafiState() {
194 public final AsNumber getFromPeerLocalAs() {
199 public final String getName() {
204 public final ClusterIdentifier getClusterId() {
205 return this.clusterId;
209 public final AsNumber getLocalAs() {
214 public synchronized DOMTransactionChain getDomChain() {
215 return this.domChain;
219 * Returns true if route can be send.
221 private boolean filterRoutes(final PeerId fromPeer, final TablesKey localTK) {
222 return supportsTable(localTK) && !fromPeer.equals(getPeerId());
226 public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
227 R extends Route & ChildOf<? super S> & Identifiable<I>,
228 I extends Identifier<R>> void initializeRibOut(final RouteEntryDependenciesContainer entryDep,
229 final List<ActualBestPathRoutes<C, S, R, I>> routesToStore) {
230 if (this.bindingChain == null) {
231 LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
235 final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
236 final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
237 final boolean addPathSupported = supportsAddPathSupported(tk);
239 final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
240 for (final ActualBestPathRoutes<C, S, R, I> initializingRoute : routesToStore) {
241 if (!supportsLLGR() && initializingRoute.isDepreferenced()) {
242 // Stale Long-lived Graceful Restart routes should not be propagated
246 final PeerId fromPeerId = initializingRoute.getFromPeerId();
247 if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
251 final R route = initializingRoute.getRoute();
252 final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
253 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
254 this, route.getRouteKey(), this.rtCache);
256 final Optional<Attributes> effAttr = entryDep.getRoutingPolicies()
257 .applyExportPolicies(routeEntry, initializingRoute.getAttributes(), entryDep.getAfiSafType());
258 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
260 effAttr.ifPresent(attributes
261 -> storeRoute(ribSupport, addPathSupported, tableRibout, initializingRoute, route, attributes, tx));
264 final FluentFuture<? extends CommitInfo> future = tx.commit();
265 this.submitted = future;
266 future.addCallback(new FutureCallback<CommitInfo>() {
268 public void onSuccess(final CommitInfo result) {
269 LOG.trace("Successful update commit");
273 public void onFailure(final Throwable trw) {
274 LOG.error("Failed update commit", trw);
276 }, MoreExecutors.directExecutor());
280 public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
281 R extends Route & ChildOf<? super S> & Identifiable<I>,
282 I extends Identifier<R>> void refreshRibOut(final RouteEntryDependenciesContainer entryDep,
283 final List<StaleBestPathRoute<C, S, R, I>> staleRoutes, final List<AdvertizedRoute<C, S, R, I>> newRoutes) {
284 if (this.bindingChain == null) {
285 LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
288 final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
289 final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
290 deleteRouteRibOut(ribSupport, staleRoutes, tx);
291 installRouteRibOut(entryDep, newRoutes, tx);
293 final FluentFuture<? extends CommitInfo> future = tx.commit();
294 this.submitted = future;
295 future.addCallback(new FutureCallback<CommitInfo>() {
297 public void onSuccess(final CommitInfo result) {
298 LOG.trace("Successful update commit");
302 public void onFailure(final Throwable trw) {
303 LOG.error("Failed update commit", trw);
305 }, MoreExecutors.directExecutor());
309 public final synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
310 R extends Route & ChildOf<? super S> & Identifiable<I>,
311 I extends Identifier<R>> void reEvaluateAdvertizement(
312 final RouteEntryDependenciesContainer entryDep,
313 final List<ActualBestPathRoutes<C, S, R, I>> routesToStore) {
314 if (this.bindingChain == null) {
315 LOG.debug("Session closed, skip changes to peer AdjRibsOut {}", getPeerId());
319 final RIBSupport<C,S,R,I> ribSupport = entryDep.getRIBSupport();
320 final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
321 final boolean addPathSupported = supportsAddPathSupported(tk);
323 final WriteTransaction tx = this.bindingChain.newWriteOnlyTransaction();
324 for (final ActualBestPathRoutes<C, S, R, I> actualBestRoute : routesToStore) {
325 final PeerId fromPeerId = actualBestRoute.getFromPeerId();
326 if (!filterRoutes(fromPeerId, ribSupport.getTablesKey())) {
330 final R route = actualBestRoute.getRoute();
331 final Optional<Attributes> effAttr;
332 if (supportsLLGR() || !actualBestRoute.isDepreferenced()) {
333 final Peer fromPeer = entryDep.getPeerTracker().getPeer(fromPeerId);
334 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
335 this, route.getRouteKey(), this.rtCache);
336 effAttr = entryDep.getRoutingPolicies()
337 .applyExportPolicies(routeEntry, actualBestRoute.getAttributes(), entryDep.getAfiSafType());
339 // Stale Long-lived Graceful Restart routes should not be propagated
340 effAttr = Optional.empty();
343 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
344 if (effAttr.isPresent()) {
345 storeRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, route, effAttr.get(), tx);
347 deleteRoute(ribSupport, addPathSupported, tableRibout, actualBestRoute, tx);
351 final FluentFuture<? extends CommitInfo> future = tx.commit();
352 this.submitted = future;
353 future.addCallback(new FutureCallback<CommitInfo>() {
355 public void onSuccess(final CommitInfo result) {
356 LOG.trace("Successful update commit");
360 public void onFailure(final Throwable trw) {
361 LOG.error("Failed update commit", trw);
363 }, MoreExecutors.directExecutor());
366 private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
367 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void installRouteRibOut(
368 final RouteEntryDependenciesContainer entryDep, final List<AdvertizedRoute<C, S, R, I>> routes,
369 final WriteTransaction tx) {
370 final TablesKey tk = entryDep.getRIBSupport().getTablesKey();
371 final BGPPeerTracker peerTracker = entryDep.getPeerTracker();
372 final RIBSupport<C, S, R, I> ribSupport = entryDep.getRIBSupport();
373 final BGPRibRoutingPolicy routingPolicies = entryDep.getRoutingPolicies();
374 final boolean addPathSupported = supportsAddPathSupported(tk);
375 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
377 for (final AdvertizedRoute<C, S, R, I> advRoute : routes) {
378 final PeerId fromPeerId = advRoute.getFromPeerId();
379 if (!filterRoutes(fromPeerId, tk) || !advRoute.isFirstBestPath() && !addPathSupported) {
382 if (!supportsLLGR() && advRoute.isDepreferenced()) {
383 // https://tools.ietf.org/html/draft-uttaro-idr-bgp-persistence-04#section-4.3
384 // o The route SHOULD NOT be advertised to any neighbor from which the
385 // Long-lived Graceful Restart Capability has not been received. The
386 // exception is described in the Optional Partial Deployment
387 // Procedure section (Section 4.7). Note that this requirement
388 // implies that such routes should be withdrawn from any such
390 deleteRoute(ribSupport, addPathSupported, tableRibout, advRoute, tx);
394 final R route = advRoute.getRoute();
395 Optional<Attributes> effAttr = Optional.empty();
396 final Peer fromPeer = peerTracker.getPeer(fromPeerId);
397 final Attributes attributes = advRoute.getAttributes();
398 if (fromPeer != null && attributes != null) {
399 final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(fromPeer,
400 this, route.getRouteKey(), this.rtCache);
401 effAttr = routingPolicies.applyExportPolicies(routeEntry, attributes, entryDep.getAfiSafType());
403 effAttr.ifPresent(attributes1
404 -> storeRoute(ribSupport, addPathSupported, tableRibout, advRoute, route, attributes1, tx));
408 private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
409 R extends Route & ChildOf<? super S> & Identifiable<I>,
410 I extends Identifier<R>> void deleteRouteRibOut(
411 final RIBSupport<C, S, R, I> ribSupport,
412 final List<StaleBestPathRoute<C, S, R, I>> staleRoutesIid,
413 final WriteTransaction tx) {
414 final TablesKey tk = ribSupport.getTablesKey();
415 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout = getRibOutIId(tk);
416 final boolean addPathSupported = supportsAddPathSupported(tk);
417 staleRoutesIid.forEach(staleRouteIid
418 -> removeRoute(ribSupport, addPathSupported, tableRibout, staleRouteIid, tx));
421 private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
422 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void storeRoute(
423 final RIBSupport<C, S, R, I> ribSupport, final boolean addPathSupported,
424 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
425 final RouteKeyIdentifier<R, I> advRoute, final R route, final Attributes effAttr,
426 final WriteTransaction tx) {
427 final InstanceIdentifier<R> ribOut;
429 if (!addPathSupported) {
430 ribOut = ribSupport.createRouteIdentifier(tableRibout, advRoute.getNonAddPathRouteKeyIdentifier());
431 newKey = ribSupport.createRouteListKey(route.getRouteKey());
433 ribOut = ribSupport.createRouteIdentifier(tableRibout, advRoute.getAddPathRouteKeyIdentifier());
434 newKey = ribSupport.createRouteListKey(route.getPathId(), route.getRouteKey());
437 final R newRoute = ribSupport.createRoute(route, newKey, effAttr);
438 LOG.debug("Write advRoute {} to peer AdjRibsOut {}", advRoute, getPeerId());
439 tx.put(LogicalDatastoreType.OPERATIONAL, ribOut, newRoute);
442 private synchronized <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
443 R extends Route & ChildOf<? super S> & Identifiable<I>,
444 I extends Identifier<R>> void removeRoute(final RIBSupport<C, S, R, I> ribSupport,
445 final boolean addPathSupported, final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
446 final StaleBestPathRoute<C, S, R, I> staleRouteIid, final WriteTransaction tx) {
447 if (addPathSupported) {
448 List<I> staleRoutesIId = staleRouteIid.getAddPathRouteKeyIdentifiers();
449 for (final I id : staleRoutesIId) {
450 final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout, id);
451 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
452 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
455 if (!staleRouteIid.isNonAddPathBestPathNew()) {
458 final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
459 staleRouteIid.getNonAddPathRouteKeyIdentifier());
460 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
461 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
465 // FIXME: why is this different from removeRoute()?
466 private <C extends Routes & DataObject & ChoiceIn<Tables>, S extends ChildOf<? super C>,
467 R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>> void deleteRoute(
468 final RIBSupport<C, S, R, I> ribSupport, final boolean addPathSupported,
469 final KeyedInstanceIdentifier<Tables, TablesKey> tableRibout,
470 final AbstractAdvertizedRoute<C, S , R, I> advRoute, final WriteTransaction tx) {
471 final InstanceIdentifier<R> ribOutTarget = ribSupport.createRouteIdentifier(tableRibout,
472 addPathSupported ? advRoute.getAddPathRouteKeyIdentifier() : advRoute.getNonAddPathRouteKeyIdentifier());
473 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, getPeerId());
474 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
477 final synchronized void releaseBindingChain() {
478 if (this.submitted != null) {
480 this.submitted.get();
481 } catch (final InterruptedException | ExecutionException throwable) {
482 LOG.error("Write routes failed", throwable);
488 private synchronized void closeBindingChain() {
489 if (this.bindingChain != null) {
490 LOG.info("Closing peer chain {}", getPeerId());
491 this.bindingChain.close();
492 this.bindingChain = null;
496 final synchronized void closeDomChain() {
497 if (this.domChain != null) {
498 LOG.info("Closing DOM peer chain {}", getPeerId());
499 this.domChain.close();
500 this.domChain = null;
504 boolean supportsLLGR() {