7c2b8b8cc67b41efec6c044b88218395a97bc3c3
[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.common.api.data.LogicalDatastoreType;
14 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
15 import org.opendaylight.protocol.bgp.mode.api.BestPath;
16 import org.opendaylight.protocol.bgp.mode.spi.AbstractRouteEntry;
17 import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
18 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
19 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
20 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
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.NormalizedNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 @NotThreadSafe
34 abstract class BaseAbstractRouteEntry extends AbstractRouteEntry {
35     private static final Logger LOG = LoggerFactory.getLogger(BaseAbstractRouteEntry.class);
36     private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
37     private OffsetMap offsets = OffsetMap.EMPTY;
38     private ContainerNode[] values = EMPTY_ATTRIBUTES;
39     private BaseBestPath bestPath;
40     private BaseBestPath removedBestPath;
41
42     private int addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
43         int offset = this.offsets.offsetOf(routerId);
44         if (offset < 0) {
45             final OffsetMap newOffsets = this.offsets.with(routerId);
46             offset = newOffsets.offsetOf(routerId);
47
48             this.values = newOffsets.expand(this.offsets, this.values, offset);
49             this.offsets = newOffsets;
50         }
51
52         this.offsets.setValue(this.values, offset, attributes);
53         LOG.trace("Added route from {} attributes {}", routerId, attributes);
54         return offset;
55     }
56
57     /**
58      * Remove route
59      *
60      * @param routerId router ID in unsigned integer
61      * @param offset of removed route
62      * @return true if its the last route
63      */
64     protected final boolean removeRoute(final UnsignedInteger routerId, final int offset) {
65         this.values = this.offsets.removeValue(this.values, offset);
66         this.offsets = this.offsets.without(routerId);
67         return this.offsets.isEmpty();
68     }
69
70     @Override
71     public final boolean selectBest(final long localAs) {
72         /*
73          * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
74          */
75         final BasePathSelector selector = new BasePathSelector(localAs);
76
77         // Select the best route.
78         for (int i = 0; i < this.offsets.size(); ++i) {
79             final UnsignedInteger routerId = this.offsets.getRouterKey(i);
80             final ContainerNode attributes = this.offsets.getValue(this.values, i);
81             LOG.trace("Processing router id {} attributes {}", routerId, attributes);
82             selector.processPath(routerId, attributes);
83         }
84
85         // Get the newly-selected best path.
86         final BaseBestPath newBestPath = selector.result();
87         final boolean modified = newBestPath == null || !newBestPath.equals(this.bestPath);
88         if (modified) {
89             if(this.offsets.isEmpty()) {
90                 this.removedBestPath = this.bestPath;
91             }
92             LOG.trace("Previous best {}, current best {}", this.bestPath, newBestPath);
93             this.bestPath = newBestPath;
94         }
95         return modified;
96     }
97
98     @Override
99     public int addRoute(final UnsignedInteger routerId, final Long remotePathId, final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
100         LOG.trace("Find {} in {}", attributesIdentifier, data);
101         final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attributesIdentifier).orNull();
102         return addRoute(routerId, advertisedAttrs);
103     }
104
105     @Override
106     public void updateRoute(final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup,
107         final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx, final PathArgument routeIdPA) {
108         if (this.removedBestPath != null) {
109             removePathFromDataStore(this.removedBestPath, routeIdPA, locRibTarget, peerPT, localTK, ribSup, discPeers, tx);
110             this.removedBestPath = null;
111         }
112         if (this.bestPath != null) {
113             addPathToDataStore(this.bestPath, routeIdPA, locRibTarget, ribSup, peerPT, localTK, discPeers, tx);
114         }
115     }
116
117     @Override
118     public void writeRoute(final PeerId destPeer, final PathArgument routeId, final YangInstanceIdentifier rootPath, final PeerExportGroup peerGroup,
119         final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
120         final DOMDataWriteTransaction tx) {
121         if (this.bestPath != null) {
122             final BaseBestPath path = this.bestPath;
123             final PeerRole destPeerRole = getRoutePeerIdRole(peerPT, destPeer);
124             if (filterRoutes(path.getPeerId(), destPeer, peerPT, localTK, discPeers, destPeerRole)) {
125                 final ContainerNode effAttrib = peerGroup.effectiveAttributes(getRoutePeerIdRole(peerPT,path.getPeerId()), path.getAttributes());
126                 writeRoute(destPeer, getAdjRibOutYII(ribSup, rootPath, routeId, localTK), effAttrib, createValue(routeId, path), ribSup, tx);
127             }
128         }
129     }
130
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);
140         }
141         fillLocRib(pathAddPathTarget == null ? pathTarget : pathAddPathTarget, null, tx);
142         fillAdjRibsOut(null, null, routeIdPA, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
143     }
144
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);
155         } else {
156             pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
157             addPathValue = createValue(routeIdAddPath, path);
158             LOG.trace("Selected best value {}", addPathValue);
159         }
160         fillLocRib(pathAddPathTarget == null ? pathTarget : pathAddPathTarget, addPathValue == null ? value : addPathValue, tx);
161         fillAdjRibsOut(path.getAttributes(), value, routeIdPA, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
162     }
163
164     final OffsetMap getOffsets() {
165         return this.offsets;
166     }
167
168     @VisibleForTesting
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) {
172         /*
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.
176          *
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
179          * multiple times.
180          */
181         for (final PeerRole role : PeerRole.values()) {
182             final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
183             if (peerGroup != null) {
184                 final ContainerNode effAttrib = peerGroup.effectiveAttributes(getRoutePeerIdRole(peerPT, routePeerId), attributes);
185                 peerGroup.getPeers().stream()
186                     .filter(pid -> filterRoutes(routePeerId, pid.getKey(), peerPT, localTK, discPeers, getRoutePeerIdRole(peerPT, pid.getKey())))
187                     .forEach(pid -> update(pid.getKey(), getAdjRibOutYII(ribSup, pid.getValue().getYii(), routeId, localTK), effAttrib, value, ribSup, tx));
188             }
189         }
190     }
191
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);
197         }
198     }
199 }