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