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 java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.List;
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.impl.OffsetMap;
19 import org.opendaylight.protocol.bgp.mode.spi.AbstractRouteEntry;
20 import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
21 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
22 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
23 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup.PeerExporTuple;
24 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
31 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * A single route entry inside a route table. Maintains the attributes of
40 * from all contributing peers. The information is stored in arrays with a
41 * shared map of offsets for peers to allow lookups. This is needed to
42 * maintain low memory overhead in face of large number of routes and peers,
43 * where individual object overhead becomes the dominating factor.
46 public abstract class AddPathAbstractRouteEntry extends AbstractRouteEntry {
48 private static final Logger LOG = LoggerFactory.getLogger(AddPathAbstractRouteEntry.class);
49 protected List<AddPathBestPath> bestPath = new ArrayList<>();
50 protected List<AddPathBestPath> bestPathRemoved = new ArrayList<>();
51 protected OffsetMap<RouteKey> offsets = new OffsetMap<>(RouteKey.class);
52 protected ContainerNode[] values = new ContainerNode[0];
53 protected Long[] pathsId = new Long[0];
54 private long pathIdCounter = 0L;
56 private int addRoute(final RouteKey key, final ContainerNode attributes) {
57 int offset = this.offsets.offsetOf(key);
59 final OffsetMap<RouteKey> newOffsets = this.offsets.with(key);
60 offset = newOffsets.offsetOf(key);
61 final ContainerNode[] newAttributes = newOffsets.expand(this.offsets, this.values, offset);
62 final Long[] newPathsId = newOffsets.expand(this.offsets, this.pathsId, offset);
63 this.values = newAttributes;
64 this.offsets = newOffsets;
65 this.pathsId = newPathsId;
66 this.offsets.setValue(this.pathsId, offset, ++this.pathIdCounter);
68 this.offsets.setValue(this.values, offset, attributes);
69 LOG.trace("Added route from {} attributes {}", key.getRouteId(), attributes);
73 protected int addRoute(final RouteKey key, final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
74 LOG.trace("Find {} in {}", attributesIdentifier, data);
75 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attributesIdentifier).orNull();
76 return addRoute(key, advertisedAttrs);
82 * @param key RouteKey of removed route
83 * @param offset Offset of removed route
84 * @return true if it was the last route
86 protected final boolean removeRoute(final RouteKey key, final int offset) {
87 this.values = this.offsets.removeValue(this.values, offset);
88 this.pathsId = this.offsets.removeValue(this.pathsId, offset);
89 this.offsets = this.offsets.without(key);
94 public void updateRoute(final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup,
95 final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx, final PathArgument routeIdPA) {
96 this.bestPathRemoved.forEach(path -> removePathFromDataStore(path, isFirstBestPath(bestPathRemoved.indexOf(path)), routeIdPA, locRibTarget, ribSup,
97 peerPT, localTK, discPeers,tx));
98 this.bestPathRemoved = Collections.emptyList();
100 this.bestPath.forEach(path -> addPathToDataStore(path, isFirstBestPath(this.bestPath.indexOf(path)), routeIdPA, locRibTarget, ribSup,
101 peerPT, localTK, discPeers,tx));
105 public void writeRoute(final PeerId destPeer, final PathArgument routeId, final YangInstanceIdentifier rootPath, final PeerExportGroup peerGroup,
106 final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
107 final DOMDataWriteTransaction tx) {
108 final boolean destPeerSupAddPath = peerPT.isAddPathSupportedByPeer(destPeer);
109 this.bestPath.stream().filter(path -> filterRoutes(path.getPeerId(), destPeer, peerPT, localTK, discPeers) &&
110 peersSupportsAddPathOrIsFirstBestPath(destPeerSupAddPath, isFirstBestPath(this.bestPath.indexOf(path))))
111 .forEach(path -> writeRoutePath(destPeer, routeId, peerGroup, destPeerSupAddPath, path, rootPath, localTK, ribSup, tx));
114 private void writeRoutePath(final PeerId destPeer, final PathArgument routeId, final PeerExportGroup peerGroup, final boolean destPeerSupAddPath,
115 final BestPath path, final YangInstanceIdentifier rootPath, final TablesKey localTK, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
116 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeId);
117 final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(path.getPeerId(), path.getAttributes());
118 if (destPeerSupAddPath) {
119 writeRoute(destPeer, getAdjRibOutYII(ribSup, rootPath, routeIdAddPath, localTK), effectiveAttributes, createValue(routeIdAddPath, path), ribSup, tx);
121 writeRoute(destPeer, getAdjRibOutYII(ribSup, rootPath, routeId, localTK), effectiveAttributes, createValue(routeId, path), ribSup, tx);
125 private void addPathToDataStore(final BestPath path, final boolean isFirstBestPath, final PathArgument routeIdPA, final YangInstanceIdentifier locRibTarget,
126 final RIBSupport ribSup, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final CacheDisconnectedPeers discPeers,
127 final DOMDataWriteTransaction tx) {
128 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
129 final YangInstanceIdentifier pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
130 final MapEntryNode addPathValue = createValue(routeIdAddPath, path);
131 final MapEntryNode value = createValue(routeIdPA, path);
132 LOG.trace("Selected best value {}", addPathValue);
133 fillLocRib(pathAddPathTarget, addPathValue, tx);
134 fillAdjRibsOut(isFirstBestPath, path.getAttributes(), value, addPathValue, routeIdPA, routeIdAddPath, path.getPeerId(), peerPT, localTK,
135 ribSup, discPeers, tx);
138 private void removePathFromDataStore(final BestPath path, final boolean isFirstBestPath, final PathArgument routeIdPA,
139 final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup, final ExportPolicyPeerTracker peerPT, final TablesKey localTK,
140 final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx) {
141 LOG.trace("Best Path removed {}", path);
142 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
143 final YangInstanceIdentifier pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
145 fillLocRib(pathAddPathTarget, null, tx);
146 fillAdjRibsOut(isFirstBestPath, null, null, null, routeIdPA, routeIdAddPath, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
149 private void fillAdjRibsOut(final boolean isFirstBestPath, final ContainerNode attributes, final NormalizedNode<?, ?> value, final MapEntryNode addPathValue,
150 final PathArgument routeId, final PathArgument routeIdAddPath, final PeerId routePeerId, final ExportPolicyPeerTracker peerPT, final TablesKey
151 localTK, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx) {
153 * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
154 * expose from which client a particular route was learned from in the local RIB, and have
155 * the listener perform filtering.
157 * We walk the policy set in order to minimize the amount of work we do for multiple peers:
158 * if we have two eBGP peers, for example, there is no reason why we should perform the translation
161 for (final PeerRole role : PeerRole.values()) {
162 final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
163 if (peerGroup != null) {
164 final ContainerNode effectiveAttributes = peerGroup.effectiveAttributes(routePeerId, attributes);
165 for (final Map.Entry<PeerId, PeerExporTuple> pid : peerGroup.getPeers()) {
166 final PeerId destPeer = pid.getKey();
167 final boolean destPeerSupAddPath = peerPT.isAddPathSupportedByPeer(destPeer);
168 if (filterRoutes(routePeerId, destPeer, peerPT, localTK, discPeers) && peersSupportsAddPathOrIsFirstBestPath(destPeerSupAddPath, isFirstBestPath)) {
169 if (destPeerSupAddPath) {
170 update(destPeer, getAdjRibOutYII(ribSup, pid.getValue().getYii(), routeIdAddPath, localTK), effectiveAttributes,
174 update(destPeer, getAdjRibOutYII(ribSup, pid.getValue().getYii(), routeId, localTK), effectiveAttributes, value, ribSup, tx);
182 private void update(final PeerId destPeer, final YangInstanceIdentifier routeTarget, final ContainerNode effAttr, final NormalizedNode<?, ?> value,
183 final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
184 if (!writeRoute(destPeer, routeTarget, effAttr, value, ribSup, tx)) {
185 LOG.trace("Removing {} from transaction for peer {}", routeTarget, destPeer);
186 tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
190 protected final OffsetMap<RouteKey> getOffsets() {
194 public final boolean isEmpty() {
195 return this.offsets.isEmty();
198 protected void selectBest(final RouteKey key, final AddPathSelector selector) {
199 final int offset = this.offsets.offsetOf(key);
200 final ContainerNode attributes = this.offsets.getValue(this.values, offset);
201 final Long pathId = this.offsets.getValue(this.pathsId, offset);
202 LOG.trace("Processing router key {} attributes {}", key, attributes);
203 selector.processPath(attributes, key, offset, pathId);
207 * Process best path selection
209 * @param localAs The local autonomous system number
210 * @param keyList List of RouteKey
211 * @return the best path inside offset map passed
213 protected AddPathBestPath selectBest(final long localAs, final List<RouteKey> keyList) {
215 * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
217 final AddPathSelector selector = new AddPathSelector(localAs);
218 keyList.forEach(key -> selectBest(key, selector));
219 LOG.trace("Best path selected {}", this.bestPath);
220 return selector.result();
223 protected boolean isFirstBestPath(final int bestPathPosition) {
224 return bestPathPosition == 0;
227 private boolean peersSupportsAddPathOrIsFirstBestPath(final boolean peerSupportsAddPath, final boolean isFirstBestPath) {
228 return !(!peerSupportsAddPath && !isFirstBestPath);