Remove duplicate OffsetMap code
[bgpcep.git] / bgp / path-selection-mode / src / main / java / org / opendaylight / protocol / bgp / mode / impl / add / AddPathAbstractRouteEntry.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.add;
9
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static org.opendaylight.protocol.bgp.parser.spi.PathIdUtil.NON_PATH_ID;
12 import static org.opendaylight.protocol.bgp.parser.spi.PathIdUtil.NON_PATH_ID_VALUE;
13
14 import com.google.common.collect.ImmutableList;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.stream.Collectors;
20 import javax.annotation.concurrent.NotThreadSafe;
21 import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
22 import org.opendaylight.protocol.bgp.mode.impl.BestPathStateImpl;
23 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
24 import org.opendaylight.protocol.bgp.rib.spi.RouterId;
25 import org.opendaylight.protocol.bgp.rib.spi.entry.ActualBestPathRoutes;
26 import org.opendaylight.protocol.bgp.rib.spi.entry.AdvertizedRoute;
27 import org.opendaylight.protocol.bgp.rib.spi.entry.RouteEntryInfo;
28 import org.opendaylight.protocol.bgp.rib.spi.entry.StaleBestPathRoute;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.PathId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
33 import org.opendaylight.yangtools.yang.binding.ChildOf;
34 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
35 import org.opendaylight.yangtools.yang.binding.DataObject;
36 import org.opendaylight.yangtools.yang.binding.Identifiable;
37 import org.opendaylight.yangtools.yang.binding.Identifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * A single route entry inside a route table. Maintains the attributes of
43  * from all contributing peers. The information is stored in arrays with a
44  * shared map of offsets for peers to allow lookups. This is needed to
45  * maintain low memory overhead in face of large number of routes and peers,
46  * where individual object overhead becomes the dominating factor.
47  */
48 @NotThreadSafe
49 public abstract class AddPathAbstractRouteEntry<C extends Routes & DataObject & ChoiceIn<Tables>,
50         S extends ChildOf<? super C>,
51         R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
52         implements RouteEntry<C, S, R, I> {
53
54     private static final Logger LOG = LoggerFactory.getLogger(AddPathAbstractRouteEntry.class);
55     private static final Long[] EMPTY_PATHS_ID = new Long[0];
56     private static final Route[] EMPTY_VALUES = new Route[0];
57
58     private RouteKeyOffsets offsets = RouteKeyOffsets.EMPTY;
59     private R[] values = (R[]) EMPTY_VALUES;
60     private Long[] pathsId = EMPTY_PATHS_ID;
61     private List<AddPathBestPath> bestPath;
62     private List<AddPathBestPath> bestPathRemoved;
63     private List<AddPathBestPath> newBestPathToBeAdvertised;
64     private List<Long> removedPathsId;
65
66     private long pathIdCounter = 0L;
67     private boolean isNonAddPathBestPathNew;
68
69     private R createRoute(final RIBSupport<C, S, R, I> ribSup, final String routeKey, final AddPathBestPath path) {
70         final RouteKeyOffsets map = this.offsets;
71         final R route = map.getValue(this.values, map.offsetOf(path.getRouteKey()));
72         return ribSup.createRoute(route, ribSup.createRouteListKey(pathIdObj(path.getPathIdLong()), routeKey),
73             path.getAttributes());
74     }
75
76     @Override
77     public final int addRoute(final RouterId routerId, final Long remotePathId, final R route) {
78         final RouteKey key = new RouteKey(routerId, remotePathId);
79         int offset = this.offsets.offsetOf(key);
80         if (offset < 0) {
81             final RouteKeyOffsets newOffsets = this.offsets.with(key);
82             offset = newOffsets.offsetOf(key);
83             final R[] newRoute = newOffsets.expand(this.offsets, this.values, offset);
84             final Long[] newPathsId = newOffsets.expand(this.offsets, this.pathsId, offset);
85             this.values = newRoute;
86             this.offsets = newOffsets;
87             this.pathsId = newPathsId;
88             this.offsets.setValue(this.pathsId, offset, ++this.pathIdCounter);
89         }
90         this.offsets.setValue(this.values, offset, route);
91         LOG.trace("Added route {} from {}", route, routerId);
92         return offset;
93     }
94
95     @Override
96     public final boolean removeRoute(final RouterId routerId, final Long remotePathId) {
97         final RouteKey key = new RouteKey(routerId, remotePathId);
98         final int offset = this.offsets.offsetOf(key);
99         final Long pathId = this.offsets.getValue(this.pathsId, offset);
100         this.values = this.offsets.removeValue(this.values, offset, (R[]) EMPTY_VALUES);
101         this.pathsId = this.offsets.removeValue(this.pathsId, offset, EMPTY_PATHS_ID);
102         this.offsets = this.offsets.without(key);
103         if (this.removedPathsId == null) {
104             this.removedPathsId = new ArrayList<>();
105         }
106         this.removedPathsId.add(pathId);
107         return this.offsets.isEmpty();
108     }
109
110     @Override
111     public final Optional<StaleBestPathRoute<C, S, R, I>> removeStalePaths(final RIBSupport<C, S, R, I> ribSupport,
112             final String routeKey) {
113         if ((this.bestPathRemoved == null || this.bestPathRemoved.isEmpty()) && this.removedPathsId == null) {
114             return Optional.empty();
115         }
116         List<Long> stalePaths = Collections.emptyList();
117         if (this.bestPathRemoved != null && !this.bestPathRemoved.isEmpty()) {
118             stalePaths = this.bestPathRemoved.stream().map(AddPathBestPath::getPathId).collect(Collectors.toList());
119             this.bestPathRemoved = null;
120         }
121         final StaleBestPathRoute<C, S, R, I> stale = new StaleBestPathRoute<>(ribSupport, routeKey, stalePaths,
122                 this.removedPathsId, this.isNonAddPathBestPathNew);
123         this.removedPathsId = null;
124         return Optional.of(stale);
125     }
126
127     @Override
128     public final List<AdvertizedRoute<C, S, R, I>> newBestPaths(final RIBSupport<C, S, R, I> ribSupport,
129             final String routeKey) {
130         if (this.newBestPathToBeAdvertised == null || this.newBestPathToBeAdvertised.isEmpty()) {
131             return Collections.emptyList();
132         }
133         final List<AdvertizedRoute<C, S, R, I>> advertized = new ArrayList<>(newBestPathToBeAdvertised.size());
134         final AddPathBestPath firstBestPath = this.bestPath.isEmpty() ? null : this.bestPath.get(0);
135         for (final AddPathBestPath path : this.newBestPathToBeAdvertised) {
136             final R routeAddPath = createRoute(ribSupport, routeKey, path);
137             // FIXME: can we use identity check here?
138             final boolean isFirstBestPath = firstBestPath != null && firstBestPath.equals(path);
139             final AdvertizedRoute<C, S, R, I> adv = new AdvertizedRoute<>(ribSupport, isFirstBestPath,
140                     routeAddPath, path.getAttributes(), path.getPeerId(), path.isDepreferenced());
141             advertized.add(adv);
142         }
143         this.newBestPathToBeAdvertised = null;
144         return advertized;
145     }
146
147     @Override
148     public final List<ActualBestPathRoutes<C, S, R, I>> actualBestPaths(final RIBSupport<C, S, R, I> ribSupport,
149             final RouteEntryInfo entryInfo) {
150         if (this.bestPath == null || this.bestPath.isEmpty()) {
151             return Collections.emptyList();
152         }
153         final List<ActualBestPathRoutes<C, S, R, I>> preexistentRoutes = new ArrayList<>();
154         for (final AddPathBestPath path : this.bestPath) {
155             final R route = createRoute(ribSupport, entryInfo.getRouteKey(), path);
156             final ActualBestPathRoutes<C, S, R, I> adv = new ActualBestPathRoutes<>(ribSupport, route, path.getPeerId(),
157                     path.getAttributes(), path.isDepreferenced());
158             preexistentRoutes.add(adv);
159         }
160         return preexistentRoutes;
161     }
162
163     @Override
164     public final boolean selectBest(final long localAs) {
165         final int size;
166         return isBestPathNew((size = offsets.size()) == 0 ? ImmutableList.of() : selectBest(localAs, size));
167     }
168
169     protected abstract ImmutableList<AddPathBestPath> selectBest(long localAs, int size);
170
171     /**
172      * Process a specific route offset into specified selector.
173      *
174      * @param selector selector to update
175      * @param offset offset to process
176      */
177     protected final void processOffset(final AddPathSelector selector, final int offset) {
178         final RouteKey key = offsets.getKey(offset);
179         final R route = offsets.getValue(values, offset);
180         final Long pathId = offsets.getValue(pathsId, offset);
181         LOG.trace("Processing router key {} route {}", key, route);
182         selector.processPath(route.getAttributes(), key, offset, pathId);
183     }
184
185     protected final AddPathBestPath bestPathAt(final int offset) {
186         final Route route = verifyNotNull(offsets.getValue(values, offset));
187         return new AddPathBestPath(new BestPathStateImpl(route.getAttributes()), offsets.getKey(offset),
188             offsets.getValue(pathsId, offset), offset);
189     }
190
191     private boolean isBestPathNew(final ImmutableList<AddPathBestPath> newBestPathList) {
192         this.isNonAddPathBestPathNew = !isNonAddPathBestPathTheSame(newBestPathList);
193         filterRemovedPaths(newBestPathList);
194         if (this.bestPathRemoved != null && !this.bestPathRemoved.isEmpty()
195                 || newBestPathList != null
196                 && !newBestPathList.equals(this.bestPath)) {
197             if (this.bestPath != null) {
198                 this.newBestPathToBeAdvertised = new ArrayList<>(newBestPathList);
199                 this.newBestPathToBeAdvertised.removeAll(this.bestPath);
200             } else {
201                 this.newBestPathToBeAdvertised = newBestPathList;
202             }
203             this.bestPath = newBestPathList;
204             LOG.trace("Actual Best {}, removed best {}", this.bestPath, this.bestPathRemoved);
205             return true;
206         }
207         return false;
208     }
209
210     private boolean isNonAddPathBestPathTheSame(final List<AddPathBestPath> newBestPathList) {
211         return !(isEmptyOrNull(this.bestPath) || isEmptyOrNull(newBestPathList))
212                 && this.bestPath.get(0).equals(newBestPathList.get(0));
213     }
214
215     private static boolean isEmptyOrNull(final List<AddPathBestPath> pathList) {
216         return pathList == null || pathList.isEmpty();
217     }
218
219     private void filterRemovedPaths(final List<AddPathBestPath> newBestPathList) {
220         if (this.bestPath == null) {
221             return;
222         }
223         this.bestPathRemoved = new ArrayList<>(this.bestPath);
224         this.bestPath.forEach(oldBest -> {
225             final Optional<AddPathBestPath> present = newBestPathList.stream()
226                     .filter(newBest -> newBest.getPathId() == oldBest.getPathId()
227                             && newBest.getRouteKey() == oldBest.getRouteKey()).findAny();
228             present.ifPresent(addPathBestPath -> this.bestPathRemoved.remove(oldBest));
229         });
230     }
231
232     private static PathId pathIdObj(final Long pathId) {
233         return pathId == NON_PATH_ID_VALUE ? NON_PATH_ID : new PathId(pathId);
234     }
235 }