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;
12 import java.util.Optional;
13 import javax.annotation.concurrent.NotThreadSafe;
14 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
15 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
16 import org.opendaylight.protocol.bgp.mode.api.BestPath;
17 import org.opendaylight.protocol.bgp.mode.spi.AbstractRouteEntry;
18 import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
28 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
35 abstract class BaseAbstractRouteEntry extends AbstractRouteEntry {
36 private static final Logger LOG = LoggerFactory.getLogger(BaseAbstractRouteEntry.class);
37 private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
38 private OffsetMap offsets = OffsetMap.EMPTY;
39 private ContainerNode[] values = EMPTY_ATTRIBUTES;
40 private BaseBestPath bestPath;
41 private BaseBestPath removedBestPath;
43 private int addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
44 int offset = this.offsets.offsetOf(routerId);
46 final OffsetMap newOffsets = this.offsets.with(routerId);
47 offset = newOffsets.offsetOf(routerId);
49 this.values = newOffsets.expand(this.offsets, this.values, offset);
50 this.offsets = newOffsets;
53 this.offsets.setValue(this.values, offset, attributes);
54 LOG.trace("Added route from {} attributes {}", routerId, attributes);
61 * @param routerId router ID in unsigned integer
62 * @param offset of removed route
63 * @return true if its the last route
65 protected final boolean removeRoute(final UnsignedInteger routerId, final int offset) {
66 this.values = this.offsets.removeValue(this.values, offset);
67 this.offsets = this.offsets.without(routerId);
68 return this.offsets.isEmpty();
72 public final boolean selectBest(final long localAs) {
74 * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
76 final BasePathSelector selector = new BasePathSelector(localAs);
78 // Select the best route.
79 for (int i = 0; i < this.offsets.size(); ++i) {
80 final UnsignedInteger routerId = this.offsets.getRouterKey(i);
81 final ContainerNode attributes = this.offsets.getValue(this.values, i);
82 LOG.trace("Processing router id {} attributes {}", routerId, attributes);
83 selector.processPath(routerId, attributes);
86 // Get the newly-selected best path.
87 final BaseBestPath newBestPath = selector.result();
88 final boolean modified = newBestPath == null || !newBestPath.equals(this.bestPath);
90 this.removedBestPath = this.bestPath;
91 LOG.trace("Previous best {}, current best {}", this.bestPath, newBestPath);
92 this.bestPath = newBestPath;
98 public int addRoute(final UnsignedInteger routerId, final Long remotePathId, final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
99 LOG.trace("Find {} in {}", attributesIdentifier, data);
100 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attributesIdentifier).orNull();
101 return addRoute(routerId, advertisedAttrs);
105 public void updateRoute(final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup,
106 final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx, final PathArgument routeIdPA) {
107 if (this.removedBestPath != null) {
108 removePathFromDataStore(this.removedBestPath, routeIdPA, locRibTarget, peerPT, localTK, ribSup, discPeers, tx);
109 this.removedBestPath = null;
111 if (this.bestPath != null) {
112 addPathToDataStore(this.bestPath, routeIdPA, locRibTarget, ribSup, peerPT, localTK, discPeers, tx);
117 public void writeRoute(final PeerId destPeer, final PathArgument routeId, final YangInstanceIdentifier rootPath, final PeerExportGroup peerGroup,
118 final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
119 final DOMDataWriteTransaction tx) {
120 if (this.bestPath != null) {
121 final BaseBestPath path = this.bestPath;
122 if (filterRoutes(path.getPeerId(), destPeer, peerPT, localTK, discPeers)) {
123 final ContainerNode effAttrib = peerGroup.effectiveAttributes(path.getPeerId(), path.getAttributes());
124 writeRoute(destPeer, getAdjRibOutYII(ribSup, rootPath, routeId, localTK), effAttrib, createValue(routeId, path), ribSup, tx);
129 private void removePathFromDataStore(final BestPath path, final PathArgument routeIdPA, final YangInstanceIdentifier locRibTarget,
130 final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
131 final DOMDataWriteTransaction tx) {
132 LOG.trace("Best Path removed {}", path);
133 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
134 final YangInstanceIdentifier pathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdPA);
135 YangInstanceIdentifier pathAddPathTarget = null;
136 if (routeIdAddPath != null) {
137 pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
139 fillLocRib(pathAddPathTarget == null ? pathTarget : pathAddPathTarget, null, tx);
140 fillAdjRibsOut(null, null, routeIdPA, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
143 private void addPathToDataStore(final BestPath path, final PathArgument routeIdPA, final YangInstanceIdentifier locRibTarget,
144 final RIBSupport ribSup, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final CacheDisconnectedPeers discPeers,
145 final DOMDataWriteTransaction tx) {
146 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
147 final YangInstanceIdentifier pathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdPA);
148 final NormalizedNode<?, ?> value = createValue(routeIdPA, path);
149 NormalizedNode<?, ?> addPathValue = null;
150 YangInstanceIdentifier pathAddPathTarget = null;
151 if (routeIdAddPath == null) {
152 LOG.trace("Selected best value {}", value);
154 pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
155 addPathValue = createValue(routeIdAddPath, path);
156 LOG.trace("Selected best value {}", addPathValue);
158 fillLocRib(pathAddPathTarget == null ? pathTarget : pathAddPathTarget, addPathValue == null ? value : addPathValue, tx);
159 fillAdjRibsOut(path.getAttributes(), value, routeIdPA, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
162 final OffsetMap getOffsets() {
167 private void fillAdjRibsOut(final ContainerNode attributes, final NormalizedNode<?, ?> value,
168 final PathArgument routeId, final PeerId routePeerId, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup,
169 final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx) {
171 * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
172 * expose from which client a particular route was learned from in the local RIB, and have
173 * the listener perform filtering.
175 * We walk the policy set in order to minimize the amount of work we do for multiple peers:
176 * if we have two eBGP peers, for example, there is no reason why we should perform the translation
179 for (final PeerRole role : PeerRole.values()) {
180 final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
181 if (peerGroup != null) {
182 final ContainerNode effAttrib = peerGroup.effectiveAttributes(routePeerId, attributes);
183 peerGroup.getPeers().stream()
184 .filter(pid -> filterRoutes(routePeerId, pid.getKey(), peerPT, localTK, discPeers))
185 .forEach(pid -> update(pid.getKey(), getAdjRibOutYII(ribSup, pid.getValue().getYii(), routeId, localTK), effAttrib, value, ribSup, tx));
190 private void update(final PeerId destPeer, final YangInstanceIdentifier routeTarget, final ContainerNode effAttrib,
191 final NormalizedNode<?, ?> value, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
192 if (!writeRoute(destPeer, routeTarget, effAttrib, value, ribSup, tx)) {
193 LOG.trace("Removing {} from transaction for peer {}", routeTarget, destPeer);
194 tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);