f8fc1cc867b82d60bbd1175e557206407ea6b5f5
[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 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.spi.AbstractRouteEntry;
18 import org.opendaylight.protocol.bgp.rib.spi.CacheDisconnectedPeers;
19 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
20 import org.opendaylight.protocol.bgp.rib.spi.PeerExportGroup;
21 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
28 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
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 {
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     private int addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
44         int offset = this.offsets.offsetOf(routerId);
45         if (offset < 0) {
46             final OffsetMap newOffsets = this.offsets.with(routerId);
47             offset = newOffsets.offsetOf(routerId);
48
49             this.values = newOffsets.expand(this.offsets, this.values, offset);
50             this.offsets = newOffsets;
51         }
52
53         this.offsets.setValue(this.values, offset, attributes);
54         LOG.trace("Added route from {} attributes {}", routerId, attributes);
55         return offset;
56     }
57
58     /**
59      * Remove route
60      *
61      * @param routerId router ID in unsigned integer
62      * @param offset of removed route
63      * @return true if its the last route
64      */
65     protected final boolean removeRoute(final UnsignedInteger routerId, final int offset) {
66         this.values = this.offsets.removeValue(this.values, offset);
67         this.offsets = this.offsets.without(routerId);
68         return this.offsets.isEmpty();
69     }
70
71     @Override
72     public final boolean selectBest(final long localAs) {
73         /*
74          * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
75          */
76         final BasePathSelector selector = new BasePathSelector(localAs);
77
78         // Select the best route.
79         for (int i = 0; i < this.offsets.size(); ++i) {
80             final UnsignedInteger routerId = this.offsets.getRouterKey(i);
81             final ContainerNode attributes = this.offsets.getValue(this.values, i);
82             LOG.trace("Processing router id {} attributes {}", routerId, attributes);
83             selector.processPath(routerId, attributes);
84         }
85
86         // Get the newly-selected best path.
87         final BaseBestPath newBestPath = selector.result();
88         final boolean modified = newBestPath == null || !newBestPath.equals(this.bestPath);
89         if (modified) {
90             this.removedBestPath = this.bestPath;
91             LOG.trace("Previous best {}, current best {}", this.bestPath, newBestPath);
92             this.bestPath = newBestPath;
93         }
94         return modified;
95     }
96
97     @Override
98     public int addRoute(final UnsignedInteger routerId, final Long remotePathId, final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
99         LOG.trace("Find {} in {}", attributesIdentifier, data);
100         final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attributesIdentifier).orNull();
101         return addRoute(routerId, advertisedAttrs);
102     }
103
104     @Override
105     public void updateRoute(final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final YangInstanceIdentifier locRibTarget, final RIBSupport ribSup,
106         final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx, final PathArgument routeIdPA) {
107         if (this.removedBestPath != null) {
108             removePathFromDataStore(this.removedBestPath, routeIdPA, locRibTarget, peerPT, localTK, ribSup, discPeers, tx);
109             this.removedBestPath = null;
110         }
111         if (this.bestPath != null) {
112             addPathToDataStore(this.bestPath, routeIdPA, locRibTarget, ribSup, peerPT, localTK, discPeers, tx);
113         }
114     }
115
116     @Override
117     public void writeRoute(final PeerId destPeer, final PathArgument routeId, final YangInstanceIdentifier rootPath, final PeerExportGroup peerGroup,
118         final TablesKey localTK, final ExportPolicyPeerTracker peerPT, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
119         final DOMDataWriteTransaction tx) {
120         if (this.bestPath != null) {
121             final BaseBestPath path = this.bestPath;
122             if (filterRoutes(path.getPeerId(), destPeer, peerPT, localTK, discPeers)) {
123                 final ContainerNode effAttrib = peerGroup.effectiveAttributes(path.getPeerId(), path.getAttributes());
124                 writeRoute(destPeer, getAdjRibOutYII(ribSup, rootPath, routeId, localTK), effAttrib, createValue(routeId, path), ribSup, tx);
125             }
126         }
127     }
128
129     private void removePathFromDataStore(final BestPath path, final PathArgument routeIdPA, final YangInstanceIdentifier locRibTarget,
130         final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup, final CacheDisconnectedPeers discPeers,
131         final DOMDataWriteTransaction tx) {
132         LOG.trace("Best Path removed {}", path);
133         final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
134         final YangInstanceIdentifier pathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdPA);
135         YangInstanceIdentifier pathAddPathTarget = null;
136         if (routeIdAddPath != null) {
137             pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
138         }
139         fillLocRib(pathAddPathTarget == null ? pathTarget : pathAddPathTarget, null, tx);
140         fillAdjRibsOut(null, null, routeIdPA, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
141     }
142
143     private void addPathToDataStore(final BestPath path, final PathArgument routeIdPA, final YangInstanceIdentifier locRibTarget,
144         final RIBSupport ribSup, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final CacheDisconnectedPeers discPeers,
145         final DOMDataWriteTransaction tx) {
146         final PathArgument routeIdAddPath = ribSup.getRouteIdAddPath(path.getPathId(), routeIdPA);
147         final YangInstanceIdentifier pathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdPA);
148         final NormalizedNode<?, ?> value = createValue(routeIdPA, path);
149         NormalizedNode<?, ?> addPathValue = null;
150         YangInstanceIdentifier pathAddPathTarget = null;
151         if (routeIdAddPath == null) {
152             LOG.trace("Selected best value {}", value);
153         } else {
154             pathAddPathTarget = ribSup.routePath(locRibTarget.node(ROUTES_IDENTIFIER), routeIdAddPath);
155             addPathValue = createValue(routeIdAddPath, path);
156             LOG.trace("Selected best value {}", addPathValue);
157         }
158         fillLocRib(pathAddPathTarget == null ? pathTarget : pathAddPathTarget, addPathValue == null ? value : addPathValue, tx);
159         fillAdjRibsOut(path.getAttributes(), value, routeIdPA, path.getPeerId(), peerPT, localTK, ribSup, discPeers, tx);
160     }
161
162     final OffsetMap getOffsets() {
163         return this.offsets;
164     }
165
166     @VisibleForTesting
167     private void fillAdjRibsOut(final ContainerNode attributes, final NormalizedNode<?, ?> value,
168         final PathArgument routeId, final PeerId routePeerId, final ExportPolicyPeerTracker peerPT, final TablesKey localTK, final RIBSupport ribSup,
169         final CacheDisconnectedPeers discPeers, final DOMDataWriteTransaction tx) {
170         /*
171          * We need to keep track of routers and populate adj-ribs-out, too. If we do not, we need to
172          * expose from which client a particular route was learned from in the local RIB, and have
173          * the listener perform filtering.
174          *
175          * We walk the policy set in order to minimize the amount of work we do for multiple peers:
176          * if we have two eBGP peers, for example, there is no reason why we should perform the translation
177          * multiple times.
178          */
179         for (final PeerRole role : PeerRole.values()) {
180             final PeerExportGroup peerGroup = peerPT.getPeerGroup(role);
181             if (peerGroup != null) {
182                 final ContainerNode effAttrib = peerGroup.effectiveAttributes(routePeerId, attributes);
183                 peerGroup.getPeers().stream()
184                     .filter(pid -> filterRoutes(routePeerId, pid.getKey(), peerPT, localTK, discPeers))
185                     .forEach(pid -> update(pid.getKey(), getAdjRibOutYII(ribSup, pid.getValue().getYii(), routeId, localTK), effAttrib, value, ribSup, tx));
186             }
187         }
188     }
189
190     private void update(final PeerId destPeer, final YangInstanceIdentifier routeTarget, final ContainerNode effAttrib,
191         final NormalizedNode<?, ?> value, final RIBSupport ribSup, final DOMDataWriteTransaction tx) {
192         if (!writeRoute(destPeer, routeTarget, effAttrib, value, ribSup, tx)) {
193             LOG.trace("Removing {} from transaction for peer {}", routeTarget, destPeer);
194             tx.delete(LogicalDatastoreType.OPERATIONAL, routeTarget);
195         }
196     }
197 }