Reduce number of parameters for path selection
[bgpcep.git] / bgp / path-selection-mode / src / main / java / org / opendaylight / protocol / bgp / mode / impl / base / BaseAbstractRouteEntry.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.protocol.bgp.mode.impl.base;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.primitives.UnsignedInteger;
12 import javax.annotation.concurrent.NotThreadSafe;
13 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
14 import org.opendaylight.protocol.bgp.mode.spi.AbstractRouteEntry;
15 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
16 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
17 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
18 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteEntryDependenciesContainer;
19 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteEntryInfo;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerId;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerRole;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.rib.TablesKey;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
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;
33
34 @NotThreadSafe
35 abstract class BaseAbstractRouteEntry extends AbstractRouteEntry<BaseBestPath> {
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;
42
43     /**
44      * Remove route.
45      *
46      * @param routerId router ID in unsigned integer
47      * @param offset   of removed route
48      * @return true if its the last route
49      */
50     protected final boolean removeRoute(final UnsignedInteger routerId, final int offset) {
51         this.values = this.offsets.removeValue(this.values, offset);
52         this.offsets = this.offsets.without(routerId);
53         return this.offsets.isEmpty();
54     }
55
56     @Override
57     public final boolean selectBest(final long localAs) {
58         /*
59          * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
60          */
61         final BasePathSelector selector = new BasePathSelector(localAs);
62
63         // Select the best route.
64         for (int i = 0; i < this.offsets.size(); ++i) {
65             final UnsignedInteger routerId = this.offsets.getRouterKey(i);
66             final ContainerNode attributes = this.offsets.getValue(this.values, i);
67             LOG.trace("Processing router id {} attributes {}", routerId, attributes);
68             selector.processPath(routerId, attributes);
69         }
70
71         // Get the newly-selected best path.
72         final BaseBestPath newBestPath = selector.result();
73         final boolean modified = newBestPath == null || !newBestPath.equals(this.bestPath);
74         if (modified) {
75             if (this.offsets.isEmpty()) {
76                 this.removedBestPath = this.bestPath;
77             }
78             LOG.trace("Previous best {}, current best {}", this.bestPath, newBestPath);
79             this.bestPath = newBestPath;
80         }
81         return modified;
82     }
83
84     @Override
85     public int addRoute(final UnsignedInteger routerId, final Long remotePathId,
86             final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
87         LOG.trace("Find {} in {}", attributesIdentifier, data);
88         final ContainerNode advertisedAttrs
89                 = (ContainerNode) NormalizedNodes
90                 .findNode(data, attributesIdentifier).orElse(null);
91         int offset = this.offsets.offsetOf(routerId);
92         if (offset < 0) {
93             final OffsetMap newOffsets = this.offsets.with(routerId);
94             offset = newOffsets.offsetOf(routerId);
95
96             this.values = newOffsets.expand(this.offsets, this.values, offset);
97             this.offsets = newOffsets;
98         }
99
100         this.offsets.setValue(this.values, offset, advertisedAttrs);
101         LOG.trace("Added route from {} attributes {}", routerId, advertisedAttrs);
102         return offset;
103     }
104
105     @Override
106     public void updateBestPaths(final RouteEntryDependenciesContainer entryDependencies,
107             final NodeIdentifierWithPredicates routeIdPA, final DOMDataWriteTransaction tx) {
108         if (this.removedBestPath != null) {
109             removePathFromDataStore(entryDependencies, routeIdPA, tx);
110             this.removedBestPath = null;
111         }
112         if (this.bestPath != null) {
113             addPathToDataStore(entryDependencies, this.bestPath, routeIdPA, tx);
114         }
115     }
116
117     @Override
118     public void initializeBestPaths(final RouteEntryDependenciesContainer entryDependencies,
119             final RouteEntryInfo entryInfo, final PeerExportGroup peerGroup, final DOMDataWriteTransaction tx) {
120         if (this.bestPath != null) {
121             final ExportPolicyPeerTracker peerPT = entryDependencies.getExportPolicyPeerTracker();
122             final TablesKey localTK = entryDependencies.getLocalTablesKey();
123             final BaseBestPath path = this.bestPath;
124             final PeerId toPeerId = entryInfo.getToPeerId();
125             final PeerRole destPeerRole = getRoutePeerIdRole(peerPT, toPeerId);
126             if (filterRoutes(path.getPeerId(), toPeerId, peerPT, localTK, destPeerRole)) {
127                 final NodeIdentifierWithPredicates routeId = entryInfo.getRouteId();
128                 final RIBSupport ribSupport = entryDependencies.getRibSupport();
129                 NodeIdentifierWithPredicates routeIdDest = ribSupport.getRouteIdAddPath(path.getPathId(), routeId);
130                 if (routeIdDest == null) {
131                     routeIdDest = routeId;
132                 }
133                 final ContainerNode effAttrib = peerGroup.effectiveAttributes(getRoutePeerIdRole(peerPT,
134                         path.getPeerId()), path.getAttributes());
135                 final YangInstanceIdentifier rootPath = entryInfo.getRootPath();
136                 writeRoute(toPeerId, getAdjRibOutYII(ribSupport, rootPath, routeIdDest, localTK), effAttrib,
137                         createValue(routeIdDest, path), ribSupport, tx);
138             }
139         }
140     }
141
142     private void removePathFromDataStore(final RouteEntryDependenciesContainer entryDependencies,
143             final PathArgument routeIdPA, final DOMDataWriteTransaction tx) {
144         LOG.trace("Best Path removed {}", this.removedBestPath);
145         final YangInstanceIdentifier locRibTarget = entryDependencies.getLocRibTableTarget();
146         final RIBSupport ribSup = entryDependencies.getRibSupport();
147         PathArgument routeIdTarget = ribSup.getRouteIdAddPath(this.removedBestPath.getPathId(), routeIdPA);
148         if (routeIdTarget == null) {
149             routeIdTarget = routeIdPA;
150         }
151         fillLocRib(ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdTarget), null, tx);
152         fillAdjRibsOut(entryDependencies,null, null, routeIdTarget, this.removedBestPath.getPeerId(), tx);
153     }
154
155     private void addPathToDataStore(final RouteEntryDependenciesContainer entryDependencies, final BaseBestPath path,
156             final NodeIdentifierWithPredicates routeIdPA, final DOMDataWriteTransaction tx) {
157         final RIBSupport ribSup = entryDependencies.getRibSupport();
158         final YangInstanceIdentifier locRibTarget = entryDependencies.getLocRibTableTarget();
159         NodeIdentifierWithPredicates routeIdDest = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
160         if (routeIdDest == null) {
161             routeIdDest = routeIdPA;
162         }
163
164         final MapEntryNode value = createValue(routeIdDest, path);
165         LOG.trace("Selected best value {}", value);
166
167         final YangInstanceIdentifier pathAddPathTarget
168                 = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdDest);
169         fillLocRib(pathAddPathTarget, value, tx);
170         fillAdjRibsOut(entryDependencies, path.getAttributes(), value, routeIdDest, path.getPeerId(), tx);
171     }
172
173     final OffsetMap getOffsets() {
174         return this.offsets;
175     }
176
177     @VisibleForTesting
178     private void fillAdjRibsOut(final RouteEntryDependenciesContainer entryDependencies,
179             final ContainerNode attributes, final MapEntryNode value,
180             final PathArgument routeId, final PeerId routePeerId, final DOMDataWriteTransaction tx) {
181         /*
182          * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
183          * expose from which client a particular route was learned from in the local RIB, and have
184          * the listener perform filtering.
185          *
186          * We walk the policy set in order to minimize the amount of work we do for multiple peers:
187          * if we have two eBGP peers, for example, there is no reason why we should perform the translation
188          * multiple times.
189          */
190         final ExportPolicyPeerTracker peerPT = entryDependencies.getExportPolicyPeerTracker();
191         final TablesKey localTK = entryDependencies.getLocalTablesKey();
192         final RIBSupport ribSup = entryDependencies.getRibSupport();
193         for (final PeerRole role : PeerRole.values()) {
194             final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
195             if (peerGroup != null) {
196                 final ContainerNode effAttrib = peerGroup.effectiveAttributes(getRoutePeerIdRole(peerPT, routePeerId),
197                         attributes);
198                 peerGroup.forEach((destPeer, rootPath) -> {
199                     if (!filterRoutes(routePeerId, destPeer, peerPT, localTK, getRoutePeerIdRole(peerPT, destPeer))) {
200                         return;
201                     }
202                     update(destPeer, getAdjRibOutYII(ribSup, rootPath, routeId, localTK), effAttrib, value, ribSup, tx);
203                 });
204             }
205         }
206     }
207 }