2 * Copyright (c) 2015 Cisco Systems, Inc. and others. 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.mode.impl.base;
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.primitives.UnsignedInteger;
13 import java.util.Optional;
14 import javax.annotation.concurrent.NotThreadSafe;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
17 import org.opendaylight.protocol.bgp.mode.api.BestPath;
18 import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
19 import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
20 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
21 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
22 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
23 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibOut;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 abstract class BaseAbstractRouteEntry implements RouteEntry {
42 private static final NodeIdentifier ROUTES_IDENTIFIER = new NodeIdentifier(Routes.QNAME);
43 private static final Logger LOG = LoggerFactory.getLogger(BaseAbstractRouteEntry.class);
44 private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
45 private OffsetMap offsets = OffsetMap.EMPTY;
46 private ContainerNode[] values = EMPTY_ATTRIBUTES;
47 private Optional<BaseBestPath> bestPath = Optional.empty();
48 private Optional<BaseBestPath> removedBestPath = Optional.empty();
50 private int addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
51 int offset = this.offsets.offsetOf(routerId);
53 final OffsetMap newOffsets = this.offsets.with(routerId);
54 offset = newOffsets.offsetOf(routerId);
56 this.values = newOffsets.expand(this.offsets, this.values, offset);
57 this.offsets = newOffsets;
60 this.offsets.setValue(this.values, offset, attributes);
61 LOG.trace("Added route from {} attributes {}", routerId, attributes);
68 * @param routerId router ID in unsigned integer
69 * @param offset offset Offset of removed route
70 * @return true if its the last route
72 final boolean removeRoute(final UnsignedInteger routerId, final int offset) {
73 this.values = this.offsets.removeValue(this.values, offset);
74 this.offsets = this.offsets.without(routerId);
75 return this.offsets.isEmty();
79 public final boolean selectBest(final long localAs) {
81 * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
83 final BasePathSelector selector = new BasePathSelector(localAs);
85 // Select the best route.
86 for (int i = 0; i < this.offsets.size(); ++i) {
87 final UnsignedInteger routerId = this.offsets.getRouterId(i);
88 final ContainerNode attributes = this.offsets.getValue(this.values, i);
89 LOG.trace("Processing router id {} attributes {}", routerId, attributes);
90 selector.processPath(routerId, attributes);
93 // Get the newly-selected best path.
94 final Optional<BaseBestPath> newBestPath = Optional.ofNullable(selector.result());
95 final boolean modified = !newBestPath.equals(this.bestPath);
97 this.removedBestPath = this.bestPath;
100 LOG.trace("Previous best {}, current best {}, result {}", this.bestPath, newBestPath, modified);
101 this.bestPath = newBestPath;
106 public int addRoute(final UnsignedInteger routerId, final NodeIdentifier attrII, final NormalizedNode<?, ?> data) {
107 LOG.trace("Find {} in {}", attrII, data);
108 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attrII).orNull();
109 return addRoute(routerId, advertisedAttrs);
113 public void updateRoute(final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup,
114 final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx, final PathArgument routeIdPA) {
115 final YangInstanceIdentifier writePath = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdPA);
116 if (this.removedBestPath.isPresent()) {
117 removePathFromDataStore(this.removedBestPath.get(), routeIdPA, writePath, peerPT, localTK, ribSup, discPeers, tx);
118 this.removedBestPath = Optional.empty();
120 if (this.bestPath.isPresent()) {
121 addPathToDataStore(this.bestPath.get(), routeIdPA, writePath, ribSup, peerPT, localTK, discPeers, tx);
126 public void writeRoute(final PeerId destPeer, final PathArgument routeId, final YangInstanceIdentifier rootPath,
127 final PeerExportGroup peerGroup, final TablesKey localTK, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
128 final BaseBestPath path = this.bestPath.get();
129 final ContainerNode effAttrib = peerGroup.effectiveAttributes(path.getPeerId(), path.getAttributes());
130 writeRoute(destPeer, getRouteTarget(ribSup, rootPath, routeId, localTK), effAttrib, createValue(routeId, path), ribSup, tx);
133 private void removePathFromDataStore(final BestPath path, final PathArgument routeIdPathArgument, final YangInstanceIdentifier writePath,
134 final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
135 final DOMDataWriteTransaction tx) {
136 LOG.trace("Best Path removed {}", path);
137 fillLocRib(writePath, tx, null);
138 fillAdjRibsOut(tx, null, null, routeIdPathArgument, path.getPeerId(), peerPT, localTK, ribSup, discPeers);
141 private void addPathToDataStore(final BestPath path, final PathArgument routeIdPathArgument, final YangInstanceIdentifier writePath,
142 final RIBSupport ribSup, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final CacheDisconnectedPeers discPeers,
143 final DOMDataWriteTransaction tx) {
144 final NormalizedNode<?, ?> value = createValue(routeIdPathArgument, path);
145 LOG.trace("Selected best value {}", value);
146 fillLocRib(writePath, tx, value);
147 fillAdjRibsOut(tx, path.getAttributes(), value, routeIdPathArgument, path.getPeerId(), peerPT, localTK, ribSup, discPeers);
150 private boolean writeRoute(final PeerId destPeer, final YangInstanceIdentifier routeTarget, final ContainerNode effAttrib,
151 final NormalizedNode<?, ?> value, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
152 if (effAttrib != null && value != null) {
153 LOG.debug("Write route {} to peer AdjRibsOut {}", value, destPeer);
154 tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, value);
155 tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget.node(ribSup.routeAttributesIdentifier()), effAttrib);
161 final OffsetMap getOffsets() {
165 private void fillLocRib(final YangInstanceIdentifier writePath, final DOMDataWriteTransaction tx, final NormalizedNode<?, ?> value) {
167 LOG.debug("Write route to LocRib {}", value);
168 tx.put(LogicalDatastoreType.OPERATIONAL, writePath, value);
170 LOG.debug("Delete route from LocRib {}", writePath);
171 tx.delete(LogicalDatastoreType.OPERATIONAL, writePath);
176 private void fillAdjRibsOut(final DOMDataWriteTransaction tx, final ContainerNode attributes, final NormalizedNode<?, ?> value,
177 final PathArgument routeId, final PeerId routePeerId, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup,
178 final CacheDisconnectedPeers discPeers) {
180 * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
181 * expose from which client a particular route was learned from in the local RIB, and have
182 * the listener perform filtering.
184 * We walk the policy set in order to minimize the amount of work we do for multiple peers:
185 * if we have two eBGP peers, for example, there is no reason why we should perform the translation
188 for (final PeerRole role : PeerRole.values()) {
189 if (PeerRole.Internal.equals(role)) {
192 final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
193 if (peerGroup != null) {
194 final ContainerNode effAttrib = peerGroup.effectiveAttributes(routePeerId, attributes);
195 peerGroup.getPeers().stream()
196 .filter(pid -> filterRoutes(pid, routePeerId, peerPT, localTK, discPeers))
197 .forEach(pid -> update(pid.getKey(), getRouteTarget(ribSup, pid.getValue(), routeId, localTK), effAttrib, value,
203 private boolean filterRoutes(final Map.Entry<PeerId, YangInstanceIdentifier> pid, final PeerId destPeer, final ExportPolicyPeerTracker peerPT,
204 final TablesKey localTK, final CacheDisconnectedPeers discPeers) {
205 final PeerId peerId = pid.getKey();
206 return !peerId.equals(destPeer) && isTableSupported(destPeer, peerPT, localTK) && !discPeers.isPeerDisconnected(destPeer);
209 private void update(final PeerId destPeer, final YangInstanceIdentifier routeTarget, final ContainerNode effAttrib,
210 final NormalizedNode<?, ?> value, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
211 if (!writeRoute(destPeer, routeTarget, effAttrib, value, ribSup, tx)) {
212 LOG.trace("Removing {} from transaction for peer {}", routeTarget, destPeer);
213 tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
217 private boolean isTableSupported(final PeerId destPeer, final ExportPolicyPeerTracker peerPT, final TablesKey localTK) {
218 if (!peerPT.isTableSupported(destPeer)) {
219 LOG.trace("Route rejected, peer {} does not support this table type {}", destPeer, localTK);
226 private YangInstanceIdentifier getRouteTarget(final RIBSupport ribSup, final YangInstanceIdentifier rootPath, final PathArgument routeId,
227 final TablesKey localTK) {
228 return ribSup.routePath(rootPath.node(AdjRibOut.QNAME).node(Tables.QNAME).node(RibSupportUtils.toYangTablesKey(localTK)).
229 node(ROUTES_IDENTIFIER), routeId);