*/
package org.opendaylight.protocol.bgp.mode.impl.add;
+import static com.google.common.base.Verify.verifyNotNull;
+import static org.opendaylight.protocol.bgp.parser.spi.PathIdUtil.NON_PATH_ID;
+import static org.opendaylight.protocol.bgp.parser.spi.PathIdUtil.NON_PATH_ID_VALUE;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import com.google.common.primitives.UnsignedInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
-import javax.annotation.concurrent.NotThreadSafe;
import org.opendaylight.protocol.bgp.mode.api.RouteEntry;
+import org.opendaylight.protocol.bgp.mode.impl.BestPathStateImpl;
import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
+import org.opendaylight.protocol.bgp.rib.spi.RouterId;
import org.opendaylight.protocol.bgp.rib.spi.entry.ActualBestPathRoutes;
import org.opendaylight.protocol.bgp.rib.spi.entry.AdvertizedRoute;
import org.opendaylight.protocol.bgp.rib.spi.entry.RouteEntryInfo;
import org.opendaylight.protocol.bgp.rib.spi.entry.StaleBestPathRoute;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.PathId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Identifiable;
import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.common.Uint32;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * A single route entry inside a route table. Maintains the attributes of
- * from all contributing peers. The information is stored in arrays with a
- * shared map of offsets for peers to allow lookups. This is needed to
- * maintain low memory overhead in face of large number of routes and peers,
- * where individual object overhead becomes the dominating factor.
+ * A single route entry inside a route table. Maintains the attributes from all contributing peers. The information is
+ * stored in arrays with a shared map of offsets for peers to allow lookups. This is needed to maintain low memory
+ * overhead in face of large number of routes and peers, where individual object overhead becomes the dominating factor.
+ *
+ * <p>
+ * This class is NOT thread-safe.
*/
-@NotThreadSafe
public abstract class AddPathAbstractRouteEntry<C extends Routes & DataObject & ChoiceIn<Tables>,
- S extends ChildOf<? super C>,
- R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
+ S extends ChildOf<? super C>, R extends Route & ChildOf<? super S> & Identifiable<I>, I extends Identifier<R>>
implements RouteEntry<C, S, R, I> {
+ private static final class Stale<C extends Routes & DataObject & ChoiceIn<Tables>,
+ S extends ChildOf<? super C>, R extends Route & ChildOf<? super S> & Identifiable<I>,
+ I extends Identifier<R>> extends StaleBestPathRoute<C, S, R, I> {
+ private final List<I> addPathRouteKeyIdentifier;
+ private final List<I> staleRouteKeyIdentifier;
+ private final boolean isNonAddPathBestPathNew;
+
+ Stale(final RIBSupport<C, S, R, I> ribSupport, final String routeKey, final List<PathId> staleRoutesPathIds,
+ final List<PathId> withdrawalRoutePathIds, final boolean isNonAddPathBestPathNew) {
+ super(ribSupport.createRouteListKey(routeKey));
+ this.isNonAddPathBestPathNew = isNonAddPathBestPathNew;
+
+ this.staleRouteKeyIdentifier = staleRoutesPathIds.stream()
+ .map(pathId -> ribSupport.createRouteListKey(pathId, routeKey)).collect(Collectors.toList());
+ if (withdrawalRoutePathIds != null) {
+ this.addPathRouteKeyIdentifier = withdrawalRoutePathIds.stream()
+ .map(pathId -> ribSupport.createRouteListKey(pathId, routeKey)).collect(Collectors.toList());
+ } else {
+ this.addPathRouteKeyIdentifier = Collections.emptyList();
+ }
+ }
+
+ @Override
+ public List<I> getStaleRouteKeyIdentifiers() {
+ return this.staleRouteKeyIdentifier;
+ }
+
+ @Override
+ public List<I> getAddPathRouteKeyIdentifiers() {
+ return addPathRouteKeyIdentifier;
+ }
+
+ @Override
+ public boolean isNonAddPathBestPathNew() {
+ return isNonAddPathBestPathNew;
+ }
+ }
private static final Logger LOG = LoggerFactory.getLogger(AddPathAbstractRouteEntry.class);
- private static final Long[] EMPTY_PATHS_ID = new Long[0];
+ private static final Uint32[] EMPTY_PATHS_ID = new Uint32[0];
private static final Route[] EMPTY_VALUES = new Route[0];
- protected OffsetMap offsets = OffsetMap.EMPTY;
- protected R[] values = (R[]) EMPTY_VALUES;
- protected Long[] pathsId = EMPTY_PATHS_ID;
+ private RouteKeyOffsets offsets = RouteKeyOffsets.EMPTY;
+ private R[] values = (R[]) EMPTY_VALUES;
+ private Uint32[] pathsId = EMPTY_PATHS_ID;
private List<AddPathBestPath> bestPath;
private List<AddPathBestPath> bestPathRemoved;
+ private List<AddPathBestPath> newBestPathToBeAdvertised;
+ private List<Uint32> removedPathsId;
+
private long pathIdCounter = 0L;
private boolean isNonAddPathBestPathNew;
- private List<AddPathBestPath> newBestPathToBeAdvertised;
- private List<Long> removedPathsId;
- private R createRoute(final RIBSupport<C, S, R, I> ribSup, final String routeKey,
- final long pathId, final AddPathBestPath path) {
- final OffsetMap map = getOffsets();
+ private R createRoute(final RIBSupport<C, S, R, I> ribSup, final String routeKey, final AddPathBestPath path) {
+ final RouteKeyOffsets map = this.offsets;
final R route = map.getValue(this.values, map.offsetOf(path.getRouteKey()));
- return ribSup.createRoute(route, routeKey, pathId, path.getAttributes());
+ return ribSup.createRoute(route, ribSup.createRouteListKey(pathIdObj(path.getPathIdLong()), routeKey),
+ path.getAttributes());
}
@Override
- public final int addRoute(final UnsignedInteger routerId, final long remotePathId, final R route) {
+ public final int addRoute(final RouterId routerId, final Uint32 remotePathId, final R route) {
final RouteKey key = new RouteKey(routerId, remotePathId);
int offset = this.offsets.offsetOf(key);
if (offset < 0) {
- final OffsetMap newOffsets = this.offsets.with(key);
+ final RouteKeyOffsets newOffsets = this.offsets.with(key);
offset = newOffsets.offsetOf(key);
final R[] newRoute = newOffsets.expand(this.offsets, this.values, offset);
- final Long[] newPathsId = newOffsets.expand(this.offsets, this.pathsId, offset);
+ final Uint32[] newPathsId = newOffsets.expand(this.offsets, this.pathsId, offset);
this.values = newRoute;
this.offsets = newOffsets;
this.pathsId = newPathsId;
- this.offsets.setValue(this.pathsId, offset, ++this.pathIdCounter);
+ this.offsets.setValue(this.pathsId, offset, Uint32.valueOf(++this.pathIdCounter));
}
this.offsets.setValue(this.values, offset, route);
LOG.trace("Added route {} from {}", route, routerId);
}
@Override
- public final boolean removeRoute(final UnsignedInteger routerId, final long remotePathId) {
+ public final boolean removeRoute(final RouterId routerId, final Uint32 remotePathId) {
final RouteKey key = new RouteKey(routerId, remotePathId);
- final int offset = getOffsets().offsetOf(key);
- final Long pathId = this.offsets.getValue(this.pathsId, offset);
+ final int offset = this.offsets.offsetOf(key);
+ final Uint32 pathId = this.offsets.getValue(this.pathsId, offset);
this.values = this.offsets.removeValue(this.values, offset, (R[]) EMPTY_VALUES);
this.pathsId = this.offsets.removeValue(this.pathsId, offset, EMPTY_PATHS_ID);
this.offsets = this.offsets.without(key);
this.removedPathsId = new ArrayList<>();
}
this.removedPathsId.add(pathId);
- return isEmpty();
+ return this.offsets.isEmpty();
}
@Override
public final Optional<StaleBestPathRoute<C, S, R, I>> removeStalePaths(final RIBSupport<C, S, R, I> ribSupport,
final String routeKey) {
- if ((this.bestPathRemoved == null || this.bestPathRemoved.isEmpty()) && this.removedPathsId == null) {
- return Optional.empty();
+ final List<PathId> stalePaths;
+ if (bestPathRemoved != null && !bestPathRemoved.isEmpty()) {
+ stalePaths = bestPathRemoved.stream().map(AddPathBestPath::getPathIdLong)
+ .map(AddPathAbstractRouteEntry::pathIdObj).collect(Collectors.toList());
+ bestPathRemoved = null;
+ } else {
+ stalePaths = Collections.emptyList();
}
- List<Long> stalePaths = Collections.emptyList();
- if (this.bestPathRemoved != null && !this.bestPathRemoved.isEmpty()) {
- stalePaths = this.bestPathRemoved.stream().map(AddPathBestPath::getPathId).collect(Collectors.toList());
- this.bestPathRemoved = null;
+
+ List<PathId> removedPaths;
+ if (removedPathsId != null) {
+ removedPaths = Lists.transform(removedPathsId, AddPathAbstractRouteEntry::pathIdObj);
+ this.removedPathsId = null;
+ } else {
+ removedPaths = Collections.emptyList();
}
- final StaleBestPathRoute<C, S, R, I> stale = new StaleBestPathRoute<>(ribSupport, routeKey, stalePaths,
- this.removedPathsId, this.isNonAddPathBestPathNew);
- this.removedPathsId = null;
- return Optional.of(stale);
+
+ return stalePaths.isEmpty() && removedPaths.isEmpty() ? Optional.empty()
+ : Optional.of(new Stale<>(ribSupport, routeKey, stalePaths, removedPaths, isNonAddPathBestPathNew));
}
@Override
final List<AdvertizedRoute<C, S, R, I>> advertized = new ArrayList<>(newBestPathToBeAdvertised.size());
final AddPathBestPath firstBestPath = this.bestPath.isEmpty() ? null : this.bestPath.get(0);
for (final AddPathBestPath path : this.newBestPathToBeAdvertised) {
- final R routeAddPath = createRoute(ribSupport, routeKey, path.getPathId(), path);
+ final R routeAddPath = createRoute(ribSupport, routeKey, path);
// FIXME: can we use identity check here?
final boolean isFirstBestPath = firstBestPath != null && firstBestPath.equals(path);
final AdvertizedRoute<C, S, R, I> adv = new AdvertizedRoute<>(ribSupport, isFirstBestPath,
}
final List<ActualBestPathRoutes<C, S, R, I>> preexistentRoutes = new ArrayList<>();
for (final AddPathBestPath path : this.bestPath) {
- final R route = createRoute(ribSupport, entryInfo.getRouteKey(), path.getPathId(), path);
+ final R route = createRoute(ribSupport, entryInfo.getRouteKey(), path);
final ActualBestPathRoutes<C, S, R, I> adv = new ActualBestPathRoutes<>(ribSupport, route, path.getPeerId(),
path.getAttributes(), path.isDepreferenced());
preexistentRoutes.add(adv);
return preexistentRoutes;
}
- private OffsetMap getOffsets() {
- return this.offsets;
+ @Override
+ public final boolean selectBest(final long localAs) {
+ final int size;
+ return isBestPathNew((size = offsets.size()) == 0 ? ImmutableList.of() : selectBest(localAs, size));
}
- public final boolean isEmpty() {
- return this.offsets.isEmpty();
- }
+ protected abstract ImmutableList<AddPathBestPath> selectBest(long localAs, int size);
- private void selectBest(final RouteKey key, final AddPathSelector selector) {
- final int offset = this.offsets.offsetOf(key);
- final R route = this.offsets.getValue(this.values, offset);
- final long pathId = this.offsets.getValue(this.pathsId, offset);
+ /**
+ * Process a specific route offset into specified selector.
+ *
+ * @param selector selector to update
+ * @param offset offset to process
+ */
+ protected final void processOffset(final AddPathSelector selector, final int offset) {
+ final RouteKey key = offsets.getKey(offset);
+ final R route = offsets.getValue(values, offset);
+ final Uint32 pathId = offsets.getValue(pathsId, offset);
LOG.trace("Processing router key {} route {}", key, route);
selector.processPath(route.getAttributes(), key, offset, pathId);
}
- /**
- * Process best path selection.
- *
- * @param localAs The local autonomous system number
- * @param keyList List of RouteKey
- * @return the best path inside offset map passed
- */
- protected AddPathBestPath selectBest(final long localAs, final List<RouteKey> keyList) {
- /*
- * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
- */
- final AddPathSelector selector = new AddPathSelector(localAs);
- Lists.reverse(keyList).forEach(key -> selectBest(key, selector));
- LOG.trace("Best path selected {}", this.bestPath);
- return selector.result();
+ protected final AddPathBestPath bestPathAt(final int offset) {
+ final Route route = verifyNotNull(offsets.getValue(values, offset));
+ return new AddPathBestPath(new BestPathStateImpl(route.getAttributes()), offsets.getKey(offset),
+ offsets.getValue(pathsId, offset), offset);
}
- protected boolean isBestPathNew(final ImmutableList<AddPathBestPath> newBestPathList) {
+ private boolean isBestPathNew(final ImmutableList<AddPathBestPath> newBestPathList) {
this.isNonAddPathBestPathNew = !isNonAddPathBestPathTheSame(newBestPathList);
filterRemovedPaths(newBestPathList);
if (this.bestPathRemoved != null && !this.bestPathRemoved.isEmpty()
present.ifPresent(addPathBestPath -> this.bestPathRemoved.remove(oldBest));
});
}
+
+ private static PathId pathIdObj(final Uint32 pathId) {
+ return NON_PATH_ID_VALUE.equals(pathId) ? NON_PATH_ID : new PathId(pathId);
+ }
}