Fix interface dump caching
authorMarek Gradzki <mgradzki@cisco.com>
Mon, 16 May 2016 12:51:53 +0000 (14:51 +0200)
committerMarek Gradzki <mgradzki@cisco.com>
Wed, 18 May 2016 06:02:33 +0000 (08:02 +0200)
Change-Id: I18d29bebf754c34bbc05e5c9cfb78d8aba87c205
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/EthernetCustomizer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceCustomizer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtilsTest.java

index eca13c1..74ea45c 100644 (file)
@@ -16,8 +16,6 @@
 
 package io.fd.honeycomb.v3po.translate.v3po.interfacesstate;
 
-import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceCustomizer.getCachedInterfaceDump;
-
 import io.fd.honeycomb.v3po.translate.Context;
 import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
 import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer;
@@ -70,7 +68,7 @@ public class EthernetCustomizer extends FutureJVppCustomizer
 
         final InterfaceKey key = id.firstKeyOf(Interface.class);
         final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key,
-                interfaceContext.getIndex(key.getName()), getCachedInterfaceDump(ctx));
+                interfaceContext.getIndex(key.getName()), ctx);
 
         builder.setMtu((int) iface.linkMtu);
         switch (iface.linkDuplex) {
index 82e1146..51d4022 100644 (file)
@@ -23,6 +23,7 @@ import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer;
 import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
 import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
@@ -72,11 +73,9 @@ public class InterfaceCustomizer extends FutureJVppCustomizer
         LOG.debug("Reading attributes for interface: {}", id);
         final InterfaceKey key = id.firstKeyOf(id.getTargetType());
 
-        final Map<Integer, SwInterfaceDetails> cachedDump = getCachedInterfaceDump(ctx);
-
         // Pass cached details from getAllIds to getDetails to avoid additional dumps
         final SwInterfaceDetails iface = InterfaceUtils.getVppInterfaceDetails(getFutureJVpp(), key,
-            interfaceContext.getIndex(key.getName()), cachedDump);
+            interfaceContext.getIndex(key.getName()), ctx);
         LOG.debug("Interface details for interface: {}, details: {}", key.getName(), iface);
 
         builder.setName(key.getName());
@@ -97,7 +96,7 @@ public class InterfaceCustomizer extends FutureJVppCustomizer
     @SuppressWarnings("unchecked")
     public static Map<Integer, SwInterfaceDetails> getCachedInterfaceDump(final @Nonnull Context ctx) {
         return ctx.get(DUMPED_IFCS_CONTEXT_KEY) == null
-            ? Collections.emptyMap()
+            ? new HashMap<>() // allow customizers to update the cache
             : (Map<Integer, SwInterfaceDetails>) ctx.get(DUMPED_IFCS_CONTEXT_KEY);
     }
 
index 036018e..1ee4727 100644 (file)
@@ -17,6 +17,7 @@
 package io.fd.honeycomb.v3po.translate.v3po.interfacesstate;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceCustomizer.getCachedInterfaceDump;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Iterables;
@@ -26,6 +27,7 @@ import java.math.BigInteger;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.CompletionStage;
+import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
@@ -92,15 +94,15 @@ public final class InterfaceUtils {
     }
 
     // TODO rename and move to V3poUtils
+
     /**
-     * Reads first 6 bytes of supplied byte array and converts to string as Yang dictates
-     * <p> Replace later with
+     * Reads first 6 bytes of supplied byte array and converts to string as Yang dictates <p> Replace later with
      * https://git.opendaylight.org/gerrit/#/c/34869/10/model/ietf/ietf-type- util/src/main/
      * java/org/opendaylight/mdsal/model/ietf/util/AbstractIetfYangUtil.java
      *
      * @param vppPhysAddress byte array of bytes in big endian order, constructing the network IF physical address.
      * @return String like "aa:bb:cc:dd:ee:ff"
-     * @throws NullPointerException if vppPhysAddress is null
+     * @throws NullPointerException     if vppPhysAddress is null
      * @throws IllegalArgumentException if vppPhysAddress.length < 6
      */
     public static String vppPhysAddrToYang(@Nonnull final byte[] vppPhysAddress) {
@@ -110,7 +112,7 @@ public final class InterfaceUtils {
         StringBuilder physAddr = new StringBuilder();
 
         appendHexByte(physAddr, vppPhysAddress[0]);
-        for (int i=1; i < PHYSICAL_ADDRESS_LENGTH; i++) {
+        for (int i = 1; i < PHYSICAL_ADDRESS_LENGTH; i++) {
             physAddr.append(":");
             appendHexByte(physAddr, vppPhysAddress[i]);
         }
@@ -144,34 +146,34 @@ public final class InterfaceUtils {
     /**
      * Queries VPP for interface description given interface key.
      *
-     * @param futureJvpp VPP Java Future API
-     * @param key        interface key
-     * @param index      VPP index of the interface
-     * @param allInterfaces cached interfaces dump with all the interfaces. If interface not present, another dump all
-     *                      will be performed
-     *
+     * @param futureJvpp    VPP Java Future API
+     * @param key           interface key
+     * @param index         VPP index of the interface
+     * @param ctx           per-tx scope context containing cached dump with all the interfaces. If the cache is not
+     *                      available or outdated, another dump will be performed.
      * @return SwInterfaceDetails DTO or null if interface was not found
-     *
      * @throws IllegalArgumentException If interface cannot be found
      */
     @Nonnull
     public static SwInterfaceDetails getVppInterfaceDetails(@Nonnull final FutureJVpp futureJvpp,
                                                             @Nonnull InterfaceKey key, final int index,
-                                                            @Nonnull final Map<Integer, SwInterfaceDetails> allInterfaces) {
+                                                            @Nonnull final Context ctx) {
         final SwInterfaceDump request = new SwInterfaceDump();
         request.nameFilter = key.getName().getBytes();
         request.nameFilterValid = 1;
 
+        final Map<Integer, SwInterfaceDetails> allInterfaces = getCachedInterfaceDump(ctx);
+
+        // Returned cached if available
+        if (allInterfaces.containsKey(index)) {
+            return allInterfaces.get(index);
+        }
+
         CompletionStage<SwInterfaceDetailsReplyDump> requestFuture = futureJvpp.swInterfaceDump(request);
         SwInterfaceDetailsReplyDump ifaces = V3poUtils.getReply(requestFuture.toCompletableFuture());
         if (null == ifaces || null == ifaces.swInterfaceDetails || ifaces.swInterfaceDetails.isEmpty()) {
             request.nameFilterValid = 0;
 
-            // Returned cached if available
-            if(allInterfaces.containsKey(index)) {
-                return allInterfaces.get(index);
-            }
-
             LOG.warn("VPP returned null instead of interface by key {} and its not cached", key.getName());
             LOG.warn("Iterating through all the interfaces to find interface: {}", key.getName());
 
@@ -179,10 +181,20 @@ public final class InterfaceUtils {
             requestFuture = futureJvpp.swInterfaceDump(request);
             ifaces = V3poUtils.getReply(requestFuture.toCompletableFuture());
 
-            return ifaces.swInterfaceDetails.stream().filter((swIfc) -> swIfc.swIfIndex == index)
-                .findFirst().orElseThrow(() -> new IllegalArgumentException("Unable to find interface " + key.getName()));
+            // Update the cache
+            allInterfaces.clear();
+            allInterfaces
+                    .putAll(ifaces.swInterfaceDetails.stream().collect(Collectors.toMap(d -> d.swIfIndex, d -> d)));
+
+            if (allInterfaces.containsKey(index)) {
+                return allInterfaces.get(index);
+            }
+            throw new IllegalArgumentException("Unable to find interface " + key.getName());
         }
-        return Iterables.getOnlyElement(ifaces.swInterfaceDetails);
+
+        final SwInterfaceDetails iface = Iterables.getOnlyElement(ifaces.swInterfaceDetails);
+        allInterfaces.put(index, iface); // update the cache
+        return iface;
     }
 
     /**
@@ -193,15 +205,15 @@ public final class InterfaceUtils {
      */
     @Nonnull
     public static Class<? extends InterfaceType> getInterfaceType(@Nonnull final String interfaceName) {
-        if(interfaceName.startsWith("tap")) {
+        if (interfaceName.startsWith("tap")) {
             return Tap.class;
         }
 
-        if(interfaceName.startsWith("vxlan")) {
+        if (interfaceName.startsWith("vxlan")) {
             return VxlanTunnel.class;
         }
 
-        if(interfaceName.startsWith("VirtualEthernet")) {
+        if (interfaceName.startsWith("VirtualEthernet")) {
             return VhostUser.class;
         }
 
@@ -210,8 +222,9 @@ public final class InterfaceUtils {
 
     static boolean isInterfaceOfType(final Context ctx, final int index,
                                      final Class<? extends InterfaceType> ifcType) {
-        final SwInterfaceDetails cachedDetails = checkNotNull(InterfaceCustomizer.getCachedInterfaceDump(ctx).get(index),
-            "Interface {} cannot be found in context", index);
+        final SwInterfaceDetails cachedDetails =
+                checkNotNull(getCachedInterfaceDump(ctx).get(index),
+                        "Interface {} cannot be found in context", index);
         return ifcType.equals(getInterfaceType(V3poUtils.toString(cachedDetails.interfaceName)));
     }
 }
index ff6ed3d..0a0eff0 100644 (file)
@@ -19,6 +19,10 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate;
 import static org.junit.Assert.assertEquals;
 
 import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Tap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUser;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel;
 
 public class InterfaceUtilsTest {
 
@@ -37,4 +41,12 @@ public class InterfaceUtilsTest {
     public void testVppPhysAddrToYangInvalidByteArrayLength() throws Exception {
         InterfaceUtils.vppPhysAddrToYang(new byte[]{1, 2, 3, 4, 5});
     }
+
+    @Test
+    public void testGetInterfaceType() {
+        assertEquals(Tap.class, InterfaceUtils.getInterfaceType("tap0"));
+        assertEquals(VxlanTunnel.class, InterfaceUtils.getInterfaceType("vxlan0"));
+        assertEquals(VhostUser.class, InterfaceUtils.getInterfaceType("VirtualEthernet0/0/0"));
+        assertEquals(EthernetCsmacd.class, InterfaceUtils.getInterfaceType("local0"));
+    }
 }
\ No newline at end of file