Match AfiSafi Policy implementation
[bgpcep.git] / bgp / path-selection-mode / src / main / java / org / opendaylight / protocol / bgp / mode / impl / base / BaseRouteEntry.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 java.util.Optional;
13 import javax.annotation.Nullable;
14 import javax.annotation.concurrent.NotThreadSafe;
15 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.protocol.bgp.mode.impl.BGPRouteEntryExportParametersImpl;
18 import org.opendaylight.protocol.bgp.mode.spi.AbstractRouteEntry;
19 import org.opendaylight.protocol.bgp.rib.spi.BGPPeerTracker;
20 import org.opendaylight.protocol.bgp.rib.spi.Peer;
21 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
22 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteEntryDependenciesContainer;
23 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteEntryInfo;
24 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRibRoutingPolicy;
25 import org.opendaylight.protocol.bgp.rib.spi.policy.BGPRouteEntryExportParameters;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
31 import org.opendaylight.yangtools.yang.binding.Identifier;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 @NotThreadSafe
38 final class BaseRouteEntry extends AbstractRouteEntry<BaseBestPath> {
39     private static final Logger LOG = LoggerFactory.getLogger(BaseRouteEntry.class);
40     private OffsetMap offsets = OffsetMap.EMPTY;
41     private Route[] values = EMPTY_VALUES;
42     private BaseBestPath bestPath;
43     private BaseBestPath removedBestPath;
44
45     BaseRouteEntry(final BGPPeerTracker peerTracker) {
46         super(peerTracker);
47     }
48
49     @Override
50     public  boolean removeRoute(final UnsignedInteger routerId, final long remotePathId) {
51         final int offset = this.offsets.offsetOf(routerId);
52         this.values = this.offsets.removeValue(this.values, offset, EMPTY_VALUES);
53         this.offsets = this.offsets.without(routerId);
54         return this.offsets.isEmpty();
55     }
56
57     @Override
58     public Route createRoute(final RIBSupport ribSup, String routeKey, final long pathId,
59             final BaseBestPath path) {
60         final Route route = this.offsets.getValue(this.values, this.offsets.offsetOf(path.getRouterId()));
61         return ribSup.createRoute(route, routeKey, pathId, path.getAttributes());
62     }
63
64     @Override
65     public boolean selectBest(final long localAs) {
66         /*
67          * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
68          */
69         final BasePathSelector selector = new BasePathSelector(localAs);
70
71         // Select the best route.
72         for (int i = 0; i < this.offsets.size(); ++i) {
73             final UnsignedInteger routerId = this.offsets.getRouterKey(i);
74             final Attributes attributes = this.offsets.getValue(this.values, i).getAttributes();
75             LOG.trace("Processing router id {} attributes {}", routerId, attributes);
76             selector.processPath(routerId, attributes);
77         }
78
79         // Get the newly-selected best path.
80         final BaseBestPath newBestPath = selector.result();
81         final boolean modified = newBestPath == null || !newBestPath.equals(this.bestPath);
82         if (modified) {
83             if (this.offsets.isEmpty()) {
84                 this.removedBestPath = this.bestPath;
85             }
86             LOG.trace("Previous best {}, current best {}", this.bestPath, newBestPath);
87             this.bestPath = newBestPath;
88         }
89         return modified;
90     }
91
92     @Override
93     public int addRoute(final UnsignedInteger routerId, final long remotePathId, final Route route) {
94         int offset = this.offsets.offsetOf(routerId);
95         if (offset < 0) {
96             final OffsetMap newOffsets = this.offsets.with(routerId);
97             offset = newOffsets.offsetOf(routerId);
98
99             this.values = newOffsets.expand(this.offsets, this.values, offset);
100             this.offsets = newOffsets;
101         }
102
103         this.offsets.setValue(this.values, offset, route);
104         LOG.trace("Added route {} from {}", route, routerId);
105         return offset;
106     }
107
108     @Override
109     public void updateBestPaths(
110             final RouteEntryDependenciesContainer entryDependencies,
111             final String routeKey,
112             final WriteTransaction tx) {
113         if (this.removedBestPath != null) {
114             removePathFromDataStore(entryDependencies, routeKey, tx);
115             this.removedBestPath = null;
116         }
117         if (this.bestPath != null) {
118             addPathToDataStore(entryDependencies, routeKey, tx);
119         }
120     }
121
122     @Override
123     @SuppressWarnings("unchecked")
124     public void initializeBestPaths(
125             final RouteEntryDependenciesContainer entryDep,
126             final RouteEntryInfo entryInfo,
127             final WriteTransaction tx) {
128         if (this.bestPath == null) {
129             return;
130         }
131         final TablesKey localTK = entryDep.getLocalTablesKey();
132         final Peer toPeer = entryInfo.getToPeer();
133         if (!filterRoutes(this.bestPath.getPeerId(), toPeer, localTK)) {
134             return;
135         }
136         final RIBSupport ribSupport = entryDep.getRibSupport();
137         Identifier routeIdentifier = ribSupport.createRouteListKey(this.bestPath.getPathId(), entryInfo.getRouteKey());
138         final BGPRouteEntryExportParameters routeEntry = new BGPRouteEntryExportParametersImpl(
139                 this.peerTracker.getPeer(this.bestPath.getPeerId()), toPeer);
140         final Optional<Attributes> effAttrib = entryDep.getRoutingPolicies()
141                 .applyExportPolicies(routeEntry, this.bestPath.getAttributes(), entryDep.getAfiSafType());
142         if (effAttrib.isPresent()) {
143             final Route route = createRoute(ribSupport,
144                     entryInfo.getRouteKey(), this.bestPath.getPathId(), this.bestPath);
145             InstanceIdentifier ribOutIId = ribSupport.createRouteIdentifier(toPeer.getRibOutIId(localTK),
146                     routeIdentifier);
147             LOG.debug("Write route {} to peer AdjRibsOut {}", route, toPeer.getPeerId());
148             tx.put(LogicalDatastoreType.OPERATIONAL, ribOutIId, route);
149             tx.put(LogicalDatastoreType.OPERATIONAL, ribOutIId.child(Attributes.class), effAttrib.get());
150         }
151     }
152
153     @SuppressWarnings("unchecked")
154     private void removePathFromDataStore(final RouteEntryDependenciesContainer entryDep,
155             final String routeKey, final WriteTransaction tx) {
156         LOG.trace("Best Path removed {}", this.removedBestPath);
157         final KeyedInstanceIdentifier<Tables, TablesKey> locRibTarget = entryDep.getLocRibTableTarget();
158         final RIBSupport ribSup = entryDep.getRibSupport();
159         Identifier routeIdentifier = ribSup.createRouteListKey(this.removedBestPath.getPathId(), routeKey);
160         final InstanceIdentifier routeTarget = ribSup.createRouteIdentifier(locRibTarget, routeIdentifier);
161         LOG.debug("Delete route from LocRib {}", routeTarget);
162         tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
163         fillAdjRibsOut(null, null, routeIdentifier, this.removedBestPath.getPeerId(),
164                 entryDep, tx);
165     }
166
167     @SuppressWarnings("unchecked")
168     private void addPathToDataStore(final RouteEntryDependenciesContainer entryDep,
169             final String routeKey, final WriteTransaction tx) {
170         final RIBSupport ribSup = entryDep.getRibSupport();
171         final Route route = createRoute(ribSup, routeKey, this.bestPath.getPathId(), this.bestPath);
172         LOG.trace("Selected best route {}", route);
173
174         Identifier routeIdentifier = ribSup.createRouteListKey(this.bestPath.getPathId(), routeKey);
175         final KeyedInstanceIdentifier<Tables, TablesKey> locRibTarget = entryDep.getLocRibTableTarget();
176         final InstanceIdentifier routeTarget = ribSup.createRouteIdentifier(locRibTarget, routeIdentifier);
177         LOG.debug("Write route to LocRib {}", route);
178         tx.put(LogicalDatastoreType.OPERATIONAL, routeTarget, route);
179         fillAdjRibsOut(this.bestPath.getAttributes(), route, routeIdentifier, this.bestPath.getPeerId(),
180                 entryDep, tx);
181     }
182
183     @VisibleForTesting
184     @SuppressWarnings("unchecked")
185     private void fillAdjRibsOut(
186             @Nullable final Attributes attributes,
187             @Nullable final Route route,
188             final Identifier routeKey,
189             final PeerId fromPeerId,
190             final RouteEntryDependenciesContainer routeEntryDep,
191             final WriteTransaction tx) {
192         /*
193          * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
194          * expose from which client a particular route was learned from in the local RIB, and have
195          * the listener perform filtering.
196          *
197          * We walk the policy set in order to minimize the amount of work we do for multiple peers:
198          * if we have two eBGP peers, for example, there is no reason why we should perform the translation
199          * multiple times.
200          */
201         final TablesKey localTK = routeEntryDep.getLocalTablesKey();
202         final BGPRibRoutingPolicy routingPolicies = routeEntryDep.getRoutingPolicies();
203         final RIBSupport ribSupport = routeEntryDep.getRibSupport();
204         for (final Peer toPeer : this.peerTracker.getPeers()) {
205             if (!filterRoutes(fromPeerId, toPeer, localTK)) {
206                 continue;
207             }
208             Optional<Attributes> effAttr = Optional.empty();
209             final Peer fromPeer = this.peerTracker.getPeer(fromPeerId);
210             if (fromPeer != null && attributes != null) {
211                 final BGPRouteEntryExportParameters routeEntry
212                         = new BGPRouteEntryExportParametersImpl(fromPeer, toPeer);
213                 effAttr = routingPolicies.applyExportPolicies(routeEntry, attributes, routeEntryDep.getAfiSafType());
214             }
215             final InstanceIdentifier ribOutTarget
216                     = ribSupport.createRouteIdentifier(toPeer.getRibOutIId(localTK), routeKey);
217             if (effAttr.isPresent() && route != null) {
218                 LOG.debug("Write route {} to peer AdjRibsOut {}", route, toPeer.getPeerId());
219                 tx.put(LogicalDatastoreType.OPERATIONAL, ribOutTarget, route);
220                 tx.put(LogicalDatastoreType.OPERATIONAL, ribOutTarget.child(Attributes.class), effAttr.get());
221             } else {
222                 LOG.trace("Removing {} from transaction for peer {}", ribOutTarget, toPeer.getPeerId());
223                 tx.delete(LogicalDatastoreType.OPERATIONAL, ribOutTarget);
224             }
225         }
226     }
227 }