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.impl.OffsetMap;
18 import org.opendaylight.protocol.bgp.mode.spi.AbstractRouteEntry;
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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.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.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 abstract class BaseAbstractRouteEntry extends AbstractRouteEntry {
38 private static final Logger LOG = LoggerFactory.getLogger(BaseAbstractRouteEntry.class);
39 private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
40 private OffsetMap<UnsignedInteger> offsets = new OffsetMap<>(UnsignedInteger.class);
41 private ContainerNode[] values = EMPTY_ATTRIBUTES;
42 private Optional<BaseBestPath> bestPath = Optional.empty();
43 private Optional<BaseBestPath> removedBestPath = Optional.empty();
45 private int addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
46 int offset = this.offsets.offsetOf(routerId);
48 final OffsetMap<UnsignedInteger> newOffsets = this.offsets.with(routerId);
49 offset = newOffsets.offsetOf(routerId);
51 this.values = newOffsets.expand(this.offsets, this.values, offset);
52 this.offsets = newOffsets;
55 this.offsets.setValue(this.values, offset, attributes);
56 LOG.trace("Added route from {} attributes {}", routerId, attributes);
63 * @param routerId router ID in unsigned integer
64 * @param offset of removed route
65 * @return true if its the last route
67 protected final boolean removeRoute(final UnsignedInteger routerId, final int offset) {
68 this.values = this.offsets.removeValue(this.values, offset);
69 this.offsets = this.offsets.without(routerId);
70 return this.offsets.isEmty();
74 public final boolean selectBest(final long localAs) {
76 * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
78 final BasePathSelector selector = new BasePathSelector(localAs);
80 // Select the best route.
81 for (int i = 0; i < this.offsets.size(); ++i) {
82 final UnsignedInteger routerId = this.offsets.getRouterKey(i);
83 final ContainerNode attributes = this.offsets.getValue(this.values, i);
84 LOG.trace("Processing router id {} attributes {}", routerId, attributes);
85 selector.processPath(routerId, attributes);
88 // Get the newly-selected best path.
89 final Optional<BaseBestPath> newBestPath = Optional.ofNullable(selector.result());
90 final boolean modified = !newBestPath.equals(this.bestPath);
92 this.removedBestPath = this.bestPath;
93 LOG.trace("Previous best {}, current best {}", this.bestPath, newBestPath);
94 this.bestPath = newBestPath;
100 public int addRoute(final UnsignedInteger routerId, final Long remotePathId, final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
101 LOG.trace("Find {} in {}", attributesIdentifier, data);
102 final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attributesIdentifier).orNull();
103 return addRoute(routerId, advertisedAttrs);
107 public void updateRoute(final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup,
108 final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx, final PathArgument routeIdPA) {
109 if (this.removedBestPath.isPresent()) {
110 removePathFromDataStore(this.removedBestPath.get(), routeIdPA, locRibTarget, peerPT, localTK, ribSup, discPeers, tx);
111 this.removedBestPath = Optional.empty();
113 if (this.bestPath.isPresent()) {
114 addPathToDataStore(this.bestPath.get(), routeIdPA, locRibTarget, ribSup, peerPT, localTK, discPeers, tx);
119 public void writeRoute(final PeerId destPeer, final PathArgument routeId, final YangInstanceIdentifier rootPath, final PeerExportGroup peerGroup,
120 final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
121 final DOMDataWriteTransaction tx) {
122 if (this.bestPath.isPresent()) {
123 final BaseBestPath path = this.bestPath.get();
124 if (filterRoutes(path.getPeerId(), destPeer, peerPT, localTK, discPeers)) {
125 final ContainerNode effAttrib = peerGroup.effectiveAttributes(path.getPeerId(), path.getAttributes());
126 writeRoute(destPeer, getAdjRibOutYII(ribSup, rootPath, routeId, localTK), effAttrib, createValue(routeId, path), ribSup, tx);
131 private void removePathFromDataStore(final BestPath path, final PathArgument routeIdPA, final YangInstanceIdentifier locRibTarget,
132 final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
133 final DOMDataWriteTransaction tx) {
134 LOG.trace("Best Path removed {}", path);
135 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
136 final YangInstanceIdentifier pathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdPA);
137 YangInstanceIdentifier pathAddPathTarget = null;
138 if (routeIdAddPath != null) {
139 pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
141 fillLocRib(pathAddPathTarget == null ? pathTarget : pathAddPathTarget, null, tx);
142 fillAdjRibsOut(null, null, routeIdPA, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
145 private void addPathToDataStore(final BestPath path, final PathArgument routeIdPA, final YangInstanceIdentifier locRibTarget,
146 final RIBSupport ribSup, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final CacheDisconnectedPeers discPeers,
147 final DOMDataWriteTransaction tx) {
148 final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
149 final YangInstanceIdentifier pathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdPA);
150 final NormalizedNode<?, ?> value = createValue(routeIdPA, path);
151 NormalizedNode<?, ?> addPathValue = null;
152 YangInstanceIdentifier pathAddPathTarget = null;
153 if (routeIdAddPath == null) {
154 LOG.trace("Selected best value {}", value);
156 pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
157 addPathValue = createValue(routeIdAddPath, path);
158 LOG.trace("Selected best value {}", addPathValue);
160 fillLocRib(pathAddPathTarget == null ? pathTarget : pathAddPathTarget, addPathValue == null ? value : addPathValue, tx);
161 fillAdjRibsOut(path.getAttributes(), value, routeIdPA, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
164 protected final OffsetMap<UnsignedInteger> getOffsets() {
169 private void fillAdjRibsOut(final ContainerNode attributes, final NormalizedNode<?, ?> value,
170 final PathArgument routeId, final PeerId routePeerId, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup,
171 final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx) {
173 * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
174 * expose from which client a particular route was learned from in the local RIB, and have
175 * the listener perform filtering.
177 * We walk the policy set in order to minimize the amount of work we do for multiple peers:
178 * if we have two eBGP peers, for example, there is no reason why we should perform the translation
181 for (final PeerRole role : PeerRole.values()) {
182 final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
183 if (peerGroup != null) {
184 final ContainerNode effAttrib = peerGroup.effectiveAttributes(routePeerId, attributes);
185 peerGroup.getPeers().stream()
186 .filter(pid -> filterRoutes(routePeerId, pid.getKey(), peerPT, localTK, discPeers))
187 .forEach(pid -> update(pid.getKey(), getAdjRibOutYII(ribSup, pid.getValue().getYii(), routeId, localTK), effAttrib, value, ribSup, tx));
192 private void update(final PeerId destPeer, final YangInstanceIdentifier routeTarget, final ContainerNode effAttrib,
193 final NormalizedNode<?, ?> value, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
194 if (!writeRoute(destPeer, routeTarget, effAttrib, value, ribSup, tx)) {
195 LOG.trace("Removing {} from transaction for peer {}", routeTarget, destPeer);
196 tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);