YANG revision dates mass-update
[bgpcep.git] / bgp / path-selection-mode / src / main / java / org / opendaylight / protocol / bgp / mode / impl / add / AddPathAbstractRouteEntry.java
index 5c6b7cf7e9284242c1902f9696fad1ffaf27d8df..56eab86aeff32fc1cba342d1f7821663efb1e930 100644 (file)
@@ -7,21 +7,26 @@
  */
 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;
@@ -30,56 +35,94 @@ import org.opendaylight.yangtools.yang.binding.ChoiceIn;
 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);
@@ -87,10 +130,10 @@ public abstract class AddPathAbstractRouteEntry<C extends Routes & DataObject &
     }
 
     @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);
@@ -98,24 +141,31 @@ public abstract class AddPathAbstractRouteEntry<C extends Routes & DataObject &
             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
@@ -127,7 +177,7 @@ public abstract class AddPathAbstractRouteEntry<C extends Routes & DataObject &
         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,
@@ -146,7 +196,7 @@ public abstract class AddPathAbstractRouteEntry<C extends Routes & DataObject &
         }
         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);
@@ -154,40 +204,35 @@ public abstract class AddPathAbstractRouteEntry<C extends Routes & DataObject &
         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()
@@ -227,4 +272,8 @@ public abstract class AddPathAbstractRouteEntry<C extends Routes & DataObject &
             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);
+    }
 }