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.add;
10 import com.google.common.collect.Lists;
11 import com.google.common.primitives.UnsignedInteger;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Optional;
15 import javax.annotation.concurrent.NotThreadSafe;
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.spi.AbstractRouteEntry;
19 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
20 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
21 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
22 import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerId;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerRole;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
29 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
37 * A single route entry inside a route table. Maintains the attributes of
38 * from all contributing peers. The information is stored in arrays with a
39 * shared map of offsets for peers to allow lookups. This is needed to
40 * maintain low memory overhead in face of large number of routes and peers,
41 * where individual object overhead becomes the dominating factor.
44 public abstract class AddPathAbstractRouteEntry extends AbstractRouteEntry {
46 private static final Logger LOG = LoggerFactory.getLogger(AddPathAbstractRouteEntry.class);
47 private List<AddPathBestPath> bestPath;
48 private List<AddPathBestPath> bestPathRemoved;
49 protected OffsetMap offsets = OffsetMap.EMPTY;
50 protected ContainerNode[] values = new ContainerNode[0];
51 protected Long[] pathsId = new Long[0];
52 private long pathIdCounter = 0L;
53 private boolean oldNonAddPathBestPathTheSame;
54 private List<AddPathBestPath> newBestPathToBeAdvertised;
55 private List<RemovedPath> removedPaths;
57 private static final class RemovedPath {
58 private final RouteKey key;
59 private final Long pathId;
61 RemovedPath(final RouteKey key, final Long pathId) {
70 UnsignedInteger getRouteId() {
71 return this.key.getRouteId();
75 private int addRoute(final RouteKey key, final ContainerNode attributes) {
76 int offset = this.offsets.offsetOf(key);
78 final OffsetMap newOffsets = this.offsets.with(key);
79 offset = newOffsets.offsetOf(key);
80 final ContainerNode[] newAttributes = newOffsets.expand(this.offsets, this.values, offset);
81 final Long[] newPathsId = newOffsets.expand(this.offsets, this.pathsId, offset);
82 this.values = newAttributes;
83 this.offsets = newOffsets;
84 this.pathsId = newPathsId;
85 this.offsets.setValue(this.pathsId, offset, ++this.pathIdCounter);
87 this.offsets.setValue(this.values, offset, attributes);
88 LOG.trace("Added route from {} attributes {}", key.getRouteId(), attributes);
92 protected int addRoute(final RouteKey key, final NodeIdentifier attributesIdentifier,
93 final NormalizedNode<?, ?> data) {
94 LOG.trace("Find {} in {}", attributesIdentifier, data);
95 final ContainerNode advertisedAttrs
96 = (ContainerNode) NormalizedNodes.findNode(data, attributesIdentifier).orElse(null);
97 return addRoute(key, advertisedAttrs);
103 * @param key RouteKey of removed route
104 * @param offset Offset of removed route
105 * @return true if it was the last route
107 protected final boolean removeRoute(final RouteKey key, final int offset) {
108 final Long pathId = this.offsets.getValue(this.pathsId, offset);
109 this.values = this.offsets.removeValue(this.values, offset);
110 this.pathsId = this.offsets.removeValue(this.pathsId, offset);
111 this.offsets = this.offsets.without(key);
112 if (this.removedPaths == null) {
113 this.removedPaths = new ArrayList<>();
115 this.removedPaths.add(new RemovedPath(key, pathId));
120 public void updateRoute(final TablesKey localTK, final ExportPolicyPeerTracker peerPT,
121 final YangInstanceIdentifier locRibTarget, final RIBSupport ribSupport, final DOMDataWriteTransaction tx,
122 final PathArgument routeIdPA) {
123 if (this.bestPathRemoved != null) {
124 this.bestPathRemoved.forEach(path -> {
125 final PathArgument routeIdAddPath = ribSupport.getRouteIdAddPath(path.getPathId(), routeIdPA);
126 final YangInstanceIdentifier pathAddPathTarget = ribSupport
127 .routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
128 fillLocRib(pathAddPathTarget, null, tx);
130 this.bestPathRemoved = null;
132 if (this.removedPaths != null) {
133 this.removedPaths.forEach(removedPath -> {
134 final PathArgument routeIdAddPath = ribSupport.getRouteIdAddPath(removedPath.getPathId(), routeIdPA);
135 fillAdjRibsOut(true, null, null, null, routeIdPA, routeIdAddPath,
136 RouterIds.createPeerId(removedPath.getRouteId()), peerPT, localTK, ribSupport, tx);
138 this.removedPaths = null;
141 if (this.newBestPathToBeAdvertised != null) {
142 this.newBestPathToBeAdvertised.forEach(path -> addPathToDataStore(path,
143 isFirstBestPath(this.bestPath.indexOf(path)), routeIdPA, locRibTarget, ribSupport, peerPT,
145 this.newBestPathToBeAdvertised = null;
150 public void writeRoute(final PeerId destPeer, final PathArgument routeId, final YangInstanceIdentifier rootPath,
151 final PeerExportGroup peerGroup, final TablesKey localTK, final ExportPolicyPeerTracker peerPT,
152 final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
153 final boolean destPeerSupAddPath = peerPT.isAddPathSupportedByPeer(destPeer);
154 if (this.bestPath != null) {
155 final PeerRole destPeerRole = getRoutePeerIdRole(peerPT, destPeer);
156 this.bestPath.stream().filter(path -> filterRoutes(path.getPeerId(), destPeer, peerPT, localTK,
157 destPeerRole) && peersSupportsAddPathOrIsFirstBestPath(destPeerSupAddPath,
158 isFirstBestPath(this.bestPath.indexOf(path)))).forEach(path -> writeRoutePath(destPeer, routeId,
159 peerPT, peerGroup, destPeerSupAddPath, path, rootPath, localTK, ribSup, tx));
163 private void writeRoutePath(final PeerId destPeer, final PathArgument routeId, final ExportPolicyPeerTracker peerPT,
164 final PeerExportGroup peerGroup, final boolean destPeerSupAddPath, final BestPath path,
165 final YangInstanceIdentifier rootPath, final TablesKey localTK, final RIBSupport ribSup,
166 final DOMDataWriteTransaction tx) {
167 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeId);
168 final ContainerNode effectiveAttributes = peerGroup
169 .effectiveAttributes(getRoutePeerIdRole(peerPT,path.getPeerId()), path.getAttributes());
170 if (destPeerSupAddPath) {
171 writeRoute(destPeer, getAdjRibOutYII(ribSup, rootPath, routeIdAddPath, localTK), effectiveAttributes,
172 createValue(routeIdAddPath, path), ribSup, tx);
174 writeRoute(destPeer, getAdjRibOutYII(ribSup, rootPath, routeId, localTK), effectiveAttributes,
175 createValue(routeId, path), ribSup, tx);
179 private void addPathToDataStore(final BestPath path, final boolean isFirstBestPath, final PathArgument routeIdPA,
180 final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup, final ExportPolicyPeerTracker peerPT,
181 final TablesKey localTK, final DOMDataWriteTransaction tx) {
182 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
183 final YangInstanceIdentifier pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER),
185 final MapEntryNode addPathValue = createValue(routeIdAddPath, path);
186 final MapEntryNode value = createValue(routeIdPA, path);
187 LOG.trace("Selected best value {}", addPathValue);
188 fillLocRib(pathAddPathTarget, addPathValue, tx);
189 fillAdjRibsOut(isFirstBestPath, path.getAttributes(), value, addPathValue, routeIdPA, routeIdAddPath,
190 path.getPeerId(), peerPT, localTK,
194 private void fillAdjRibsOut(final boolean isFirstBestPath, final ContainerNode attributes,
195 final NormalizedNode<?, ?> value, final MapEntryNode addPathValue, final PathArgument routeId,
196 final PathArgument routeIdAddPath, final PeerId routePeerId, final ExportPolicyPeerTracker peerPT,
197 final TablesKey localTK, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
199 * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
200 * expose from which client a particular route was learned from in the local RIB, and have
201 * the listener perform filtering.
203 * We walk the policy set in order to minimize the amount of work we do for multiple peers:
204 * if we have two eBGP peers, for example, there is no reason why we should perform the translation
207 for (final PeerRole role : PeerRole.values()) {
208 final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
209 if (peerGroup != null) {
210 final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(getRoutePeerIdRole(peerPT,
211 routePeerId), attributes);
212 peerGroup.forEach((destPeer, rootPath) -> {
213 final boolean destPeerSupAddPath = peerPT.isAddPathSupportedByPeer(destPeer);
214 if (filterRoutes(routePeerId, destPeer, peerPT, localTK, role)
215 && peersSupportsAddPathOrIsFirstBestPath(destPeerSupAddPath, isFirstBestPath)) {
216 if (destPeerSupAddPath) {
217 update(destPeer, getAdjRibOutYII(ribSup, rootPath, routeIdAddPath, localTK),
218 effectiveAttributes, addPathValue, ribSup, tx);
219 } else if (!this.oldNonAddPathBestPathTheSame) {
220 update(destPeer, getAdjRibOutYII(ribSup, rootPath, routeId, localTK),
221 effectiveAttributes, value, ribSup, tx);
230 protected final OffsetMap getOffsets() {
234 public final boolean isEmpty() {
235 return this.offsets.isEmpty();
238 private void selectBest(final RouteKey key, final AddPathSelector selector) {
239 final int offset = this.offsets.offsetOf(key);
240 final ContainerNode attributes = this.offsets.getValue(this.values, offset);
241 final Long pathId = this.offsets.getValue(this.pathsId, offset);
242 LOG.trace("Processing router key {} attributes {}", key, attributes);
243 selector.processPath(attributes, key, offset, pathId);
247 * Process best path selection.
249 * @param localAs The local autonomous system number
250 * @param keyList List of RouteKey
251 * @return the best path inside offset map passed
253 protected AddPathBestPath selectBest(final long localAs, final List<RouteKey> keyList) {
255 * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
257 final AddPathSelector selector = new AddPathSelector(localAs);
258 Lists.reverse(keyList).forEach(key -> selectBest(key, selector));
259 LOG.trace("Best path selected {}", this.bestPath);
260 return selector.result();
263 private static boolean isFirstBestPath(final int bestPathPosition) {
264 return bestPathPosition == 0;
267 private static boolean peersSupportsAddPathOrIsFirstBestPath(final boolean peerSupportsAddPath,
268 final boolean isFirstBestPath) {
269 return !(!peerSupportsAddPath && !isFirstBestPath);
272 protected boolean isBestPathNew(final List<AddPathBestPath> newBestPathList) {
273 this.oldNonAddPathBestPathTheSame = isNonAddPathBestPathTheSame(newBestPathList);
274 filterRemovedPaths(newBestPathList);
275 if (this.bestPathRemoved != null && !this.bestPathRemoved.isEmpty() || newBestPathList != null
276 && !newBestPathList.equals(this.bestPath)) {
277 this.newBestPathToBeAdvertised = new ArrayList<>(newBestPathList);
278 if (this.bestPath != null) {
279 this.newBestPathToBeAdvertised.removeAll(this.bestPath);
281 this.bestPath = newBestPathList;
282 LOG.trace("Actual Best {}, removed best {}", this.bestPath, this.bestPathRemoved);
288 private boolean isNonAddPathBestPathTheSame(final List<AddPathBestPath> newBestPathList) {
289 return !(isEmptyOrNull(this.bestPath) || isEmptyOrNull(newBestPathList))
290 && this.bestPath.get(0).equals(newBestPathList.get(0));
293 private static boolean isEmptyOrNull(final List<AddPathBestPath> pathList) {
294 return pathList == null || pathList.isEmpty();
297 private void filterRemovedPaths(final List<AddPathBestPath> newBestPathList) {
298 if (this.bestPath == null) {
301 this.bestPathRemoved = new ArrayList<>(this.bestPath);
302 this.bestPath.forEach(oldBest -> {
303 final Optional<AddPathBestPath> present = newBestPathList.stream()
304 .filter(newBest -> newBest.getPathId() == oldBest.getPathId()
305 && newBest.getRouteKey() == oldBest.getRouteKey()).findAny();
306 present.ifPresent(addPathBestPath -> this.bestPathRemoved.remove(oldBest));