HONEYCOMB-246: allow mixing deny/permit rules
authorMarek Gradzki <[email protected]>
Fri, 7 Oct 2016 13:26:06 +0000 (15:26 +0200)
committerMarek Gradzki <[email protected]>
Mon, 10 Oct 2016 13:02:31 +0000 (13:02 +0000)
- adds classify table on the end of each of the 3 chains to enforce ordering
- updates v3po.yang with default-action leaf
- updates postman collection

Change-Id: If54abec1a6516eaf87aae0e5da9382a6e5dee1f3
Signed-off-by: Marek Gradzki <[email protected]>
14 files changed:
v3po/api/src/main/yang/v3po.yang
v3po/postman_rest_collection.json
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAClWriter.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java

index 69e2f73..3e981c3 100644 (file)
@@ -7,7 +7,7 @@ module v3po {
     description
       "This revision adds the following new features:
        - ingress/egress ACLs support
-       - interface-mode type as a part of ietf-acl configuration";
+       - default-action and interface-mode type as a part of ietf-acl configuration";
   }
 
   revision "2015-01-05" {
@@ -464,10 +464,10 @@ module v3po {
     container access-lists {
       description
         "Defines references to ietf-acl lists. Before assignment to interface,
-        ACL lists are merged into 3 type of acls (eth0, ip4 and ip6) that are supported by vpp.
-        Then corresponding tables and sessions are created and assigned to the interface.
-
-        All ACEs for all assigned ACLs have to use the same packet-handling action (either deny or parmit).
+        ACL lists are merged into 3 type of acls (l2, ip4 and ip6) that are supported by vpp.
+        Then 3 corresponding chains of tables and sessions are created and assigned to the interface
+        as l2, ip4 and ip6 classify table chains.
+        User ordering is preserved in each group separately.
 
         Assignment update/delete removes all created tables and sessions and repeats process described above.
         Update/delete of ACL lists referenced here is not permitted (assignment needs to be removed first).
@@ -484,6 +484,7 @@ module v3po {
         - vlan tags are supported only for sub-interfaces defined as exact-match";
       list acl {
         key "type name";
+        ordered-by user;
 
         leaf type {
           type acl:acl-type;
@@ -494,6 +495,17 @@ module v3po {
         }
       }
 
+      leaf default-action {
+        type enumeration {
+          enum "deny";
+          enum "permit";
+         }
+         default "deny";
+        description
+          "Default action applied to packet that does not match any of rules defined in assigned ACLs.
+           It is translated to single classify table and applied at the end of assigned chains.";
+      }
+
       leaf mode {
         type interface-mode;
         default l3;
index 4001952..923033d 100644 (file)
                        "description": "Creates chain of classfy tabless/sessions in VPP and assigns them to local0 interface.\n\nCan be verified with:\nvppctl show classify table verbose\n\nthen:\nvppctl show inacl type l2",
                        "collectionId": "5bad4634-e5cf-900e-9733-0976aa9bea64",
                        "responses": [],
-                       "rawModeData": "{\n    \"ingress\": {\n      \"access-lists\": {\n        \"acl\": [\n            {\n                \"type\" : \"ietf-access-control-list:eth-acl\",\n                \"name\" : \"acl1\"\n            },\n            {\n                \"type\" : \"ietf-access-control-list:eth-acl\",\n                \"name\" : \"acl2\"\n            }\n        ]\n      }\n    }\n}",
+                       "rawModeData": "{\n    \"ingress\": {\n      \"access-lists\": {\n        \"acl\": [\n            {\n                \"type\" : \"ietf-access-control-list:eth-acl\",\n                \"name\" : \"acl1\"\n            },\n            {\n                \"type\" : \"ietf-access-control-list:eth-acl\",\n                \"name\" : \"acl2\"\n            }\n        ],\n        \"default-action\": \"permit\",\n        \"mode\": \"l2\"\n      }\n    }\n}",
                        "folder": "c05d7211-11b0-5688-2079-afa51196045c"
                },
                {
index 70cfee8..2e2d164 100644 (file)
@@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.annotations.VisibleForTesting;
-import io.fd.honeycomb.translate.util.RWUtils;
 import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
 import io.fd.honeycomb.translate.write.WriteFailedException;
 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
@@ -30,8 +29,8 @@ import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
 import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.concurrent.CompletionStage;
-import java.util.stream.Collector;
 import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -40,6 +39,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.cont
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.Permit;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.ietf.acl.base.attributes.AccessLists;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 /**
@@ -59,8 +59,8 @@ abstract class AbstractAceWriter<T extends AceType> implements AceWriter, JvppRe
     @VisibleForTesting
     static final int VLAN_TAG_LEN = 4;
 
-    private static final Collector<PacketHandling, ?, PacketHandling> SINGLE_ITEM_COLLECTOR =
-        RWUtils.singleItemCollector();
+    private static final int PERMIT = -1;
+    private static final int DENY = 0;
 
     private final FutureJVppCore futureJVppCore;
 
@@ -71,15 +71,13 @@ abstract class AbstractAceWriter<T extends AceType> implements AceWriter, JvppRe
     /**
      * Creates classify table for given ACE.
      *
-     * @param action         packet handling action (permit/deny)
      * @param ace            ACE to be translated
      * @param mode           interface mode
      * @param nextTableIndex classify table index
      * @param vlanTags       number of vlan tags
      * @return classify table that represents given ACE
      */
-    protected abstract ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action,
-                                                               @Nonnull final T ace,
+    protected abstract ClassifyAddDelTable createClassifyTable(@Nonnull final T ace,
                                                                @Nullable final InterfaceMode mode,
                                                                final int nextTableIndex,
                                                                final int vlanTags);
@@ -110,26 +108,41 @@ abstract class AbstractAceWriter<T extends AceType> implements AceWriter, JvppRe
 
     @Override
     public final void write(@Nonnull final InstanceIdentifier<?> id, @Nonnull final List<Ace> aces,
-                            final InterfaceMode mode, @Nonnull final InputAclSetInterface request,
+                            final InterfaceMode mode, final AccessLists.DefaultAction defaultAction,
+                            @Nonnull final InputAclSetInterface request,
                             @Nonnegative final int vlanTags)
         throws WriteFailedException {
-        final PacketHandling action = aces.stream().map(ace -> ace.getActions().getPacketHandling()).distinct()
-            .collect(SINGLE_ITEM_COLLECTOR);
 
         checkArgument(vlanTags >= 0 && vlanTags <= 2, "Number of vlan tags %s is not in [0,2] range");
-        int nextTableIndex = -1;
-        for (final Ace ace : aces) {
-            // Create table + session per entry
+        int nextTableIndex = configureDefaltAction(id, defaultAction);
 
-            final ClassifyAddDelTable ctRequest =
-                createClassifyTable(action, (T) ace.getMatches().getAceType(), mode, nextTableIndex, vlanTags);
+        final ListIterator<Ace> iterator = aces.listIterator(aces.size());
+        while (iterator.hasPrevious()) {
+            // Create table + session per entry
+            final Ace ace = iterator.previous();
+            final PacketHandling action = ace.getActions().getPacketHandling();
+            final T type = (T)ace.getMatches().getAceType();
+            final ClassifyAddDelTable ctRequest = createClassifyTable(type, mode, nextTableIndex, vlanTags);
             nextTableIndex = createClassifyTable(id, ctRequest);
-            createClassifySession(id,
-                createClassifySession(action, (T) ace.getMatches().getAceType(), mode, nextTableIndex, vlanTags));
+            createClassifySession(id, createClassifySession(action, type, mode, nextTableIndex, vlanTags));
         }
         setClassifyTable(request, nextTableIndex);
     }
 
+    private int configureDefaltAction(@Nonnull final InstanceIdentifier<?> id, final AccessLists.DefaultAction defaultAction)
+        throws WriteFailedException {
+        ClassifyAddDelTable ctRequest = createClassifyTable(-1);
+        if (AccessLists.DefaultAction.Permit.equals(defaultAction)) {
+            ctRequest.missNextIndex = PERMIT;
+        } else {
+            ctRequest.missNextIndex = DENY;
+        }
+        ctRequest.mask = new byte[16];
+        ctRequest.skipNVectors = 0;
+        ctRequest.matchNVectors = 1;
+        return createClassifyTable(id, ctRequest);
+    }
+
     private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id,
                                     @Nonnull final ClassifyAddDelTable request)
         throws WriteFailedException {
@@ -147,22 +160,14 @@ abstract class AbstractAceWriter<T extends AceType> implements AceWriter, JvppRe
         getReplyForWrite(cs.toCompletableFuture(), id);
     }
 
-    protected ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action, final int nextTableIndex) {
+    protected ClassifyAddDelTable createClassifyTable(final int nextTableIndex) {
         final ClassifyAddDelTable request = new ClassifyAddDelTable();
         request.isAdd = 1;
         request.tableIndex = -1; // value not present
-
         request.nbuckets = 1; // we expect exactly one session per table
-
-        if (action instanceof Permit) {
-            request.missNextIndex = 0; // for list of permit rules, deny (0) should be default action
-        } else { // deny is default value
-            request.missNextIndex = -1; // for list of deny rules, permit (-1) should be default action
-        }
-
         request.nextTableIndex = nextTableIndex;
         request.memorySize = TABLE_MEM_SIZE;
-
+        request.missNextIndex = -1; // value not set, but anyway it is ignored for tables in chain
         return request;
     }
 
index 89a92f5..667d9cc 100644 (file)
@@ -48,14 +48,13 @@ final class AceEthWriter extends AbstractAceWriter<AceEth> implements MacTransla
     }
 
     @Override
-    public ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action,
-                                                   @Nonnull final AceEth aceEth,
+    public ClassifyAddDelTable createClassifyTable(@Nonnull final AceEth aceEth,
                                                    @Nullable final InterfaceMode mode,
                                                    final int nextTableIndex,
                                                    final int vlanTags) {
         checkInterfaceMode(mode);
 
-        final ClassifyAddDelTable request = createClassifyTable(action, nextTableIndex);
+        final ClassifyAddDelTable request = createClassifyTable(nextTableIndex);
 
         request.mask = new byte[16];
         boolean aceIsEmpty = true;
@@ -102,7 +101,7 @@ final class AceEthWriter extends AbstractAceWriter<AceEth> implements MacTransla
         request.skipNVectors = 0;
         request.matchNVectors = MATCH_N_VECTORS;
 
-        LOG.debug("ACE action={}, rule={} translated to table={}.", action, aceEth, request);
+        LOG.debug("ACE rule={} translated to table={}.", aceEth, request);
         return request;
     }
 
index e539d4e..e1d05f6 100644 (file)
@@ -78,15 +78,14 @@ final class AceIp4Writer extends AbstractAceWriter<AceIp> implements Ipv4Transla
     }
 
     @Override
-    public ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action,
-                                                   @Nonnull final AceIp aceIp,
+    public ClassifyAddDelTable createClassifyTable(@Nonnull final AceIp aceIp,
                                                    @Nullable final InterfaceMode mode,
                                                    final int nextTableIndex,
                                                    final int vlanTags) {
         checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp);
         final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion();
 
-        final ClassifyAddDelTable request = createClassifyTable(action, nextTableIndex);
+        final ClassifyAddDelTable request = createClassifyTable(nextTableIndex);
         request.skipNVectors = 0; // match entire L2 and L3 header
         request.matchNVectors = MATCH_N_VECTORS;
 
@@ -137,7 +136,7 @@ final class AceIp4Writer extends AbstractAceWriter<AceIp> implements Ipv4Transla
                     String.format("Ace %s does not define packet field match values", aceIp.toString()));
         }
 
-        LOG.debug("ACE action={}, rule={} translated to table={}.", action, aceIp, request);
+        LOG.debug("ACE rule={} translated to table={}.", aceIp, request);
         return request;
     }
 
index 2afc75c..18fa1ec 100644 (file)
@@ -92,15 +92,14 @@ final class AceIp6Writer extends AbstractAceWriter<AceIp> {
     }
 
     @Override
-    public ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action,
-                                                   @Nonnull final AceIp aceIp,
+    public ClassifyAddDelTable createClassifyTable(@Nonnull final AceIp aceIp,
                                                    @Nullable final InterfaceMode mode,
                                                    final int nextTableIndex,
                                                    final int vlanTags) {
         checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp);
         final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion();
 
-        final ClassifyAddDelTable request = createClassifyTable(action, nextTableIndex);
+        final ClassifyAddDelTable request = createClassifyTable(nextTableIndex);
         request.skipNVectors = 0; // match entire L2 and L3 header
         request.matchNVectors = MATCH_N_VECTORS;
 
@@ -160,7 +159,7 @@ final class AceIp6Writer extends AbstractAceWriter<AceIp> {
                 String.format("Ace %s does not define packet field match values", aceIp.toString()));
         }
 
-        LOG.debug("ACE action={}, rule={} translated to table={}.", action, aceIp, request);
+        LOG.debug("ACE rule={} translated to table={}.", aceIp, request);
         return request;
     }
 
index 63bd6a8..c2c0248 100644 (file)
 package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress;
 
 import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
 import java.util.List;
 import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.ietf.acl.base.attributes.AccessLists;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
 
 /**
  * Writer responsible for translation of ietf-acl model ACEs to VPP's classify tables and sessions.
@@ -34,12 +35,15 @@ interface AceWriter {
      * Translates list of ACEs to chain of classify tables. Each ACE is translated into one classify table with single
      * classify session. Also initializes input_acl_set_interface request message DTO with first classify table of the
      * chain that was created.
-     *  @param id      uniquely identifies ietf-acl container
-     * @param aces    list of access control entries
-     * @param mode
-     * @param request input_acl_set_interface request DTO
+     *
+     * @param id            uniquely identifies ietf-acl container
+     * @param aces          list of access control entries
+     * @param mode          interface mode (L2/L3)
+     * @param defaultAction to be taken when packet that does not match any of rules defined in
+     * @param request       input_acl_set_interface request DTO
      */
     void write(@Nonnull final InstanceIdentifier<?> id, @Nonnull final List<Ace> aces,
-               final InterfaceMode mode, @Nonnull final InputAclSetInterface request, @Nonnegative final int vlanTags)
+               final InterfaceMode mode, final AccessLists.DefaultAction defaultAction,
+               @Nonnull final InputAclSetInterface request, @Nonnegative final int vlanTags)
         throws WriteFailedException;
 }
index 7105009..526fdd7 100644 (file)
@@ -50,6 +50,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.cont
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.AceIpVersion;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.ietf.acl.base.attributes.AccessLists;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.ietf.acl.base.attributes.access.lists.Acl;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
@@ -134,13 +135,13 @@ public final class IetfAClWriter implements JvppReplyConsumer {
     }
 
     void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, @Nonnull final List<Acl> acls,
-               @Nullable final InterfaceMode mode, @Nonnull final WriteContext writeContext)
+               final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode, @Nonnull final WriteContext writeContext)
             throws WriteFailedException {
-        write(id, swIfIndex, mode, acls, writeContext, 0);
+        write(id, swIfIndex, mode, acls, defaultAction, writeContext, 0);
     }
 
     void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, final InterfaceMode mode,
-               @Nonnull final List<Acl> acls,
+               @Nonnull final List<Acl> acls, final AccessLists.DefaultAction defaultAction,
                @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags)
             throws WriteFailedException {
 
@@ -166,7 +167,7 @@ public final class IetfAClWriter implements JvppReplyConsumer {
             if (aceWriter == null) {
                 LOG.warn("AceProcessor for {} not registered. Skipping ACE.", aceType);
             } else {
-                aceWriter.write(id, aces, mode, request, numberOfTags);
+                aceWriter.write(id, aces, mode, defaultAction, request, numberOfTags);
             }
         }
 
index a98ee30..3a55e00 100644 (file)
@@ -61,7 +61,8 @@ public class IetfAclCustomizer implements WriterCustomizer<Ingress> {
         checkArgument(accessLists != null && accessLists.getAcl() != null,
                 "ietf-acl container does not define acl list");
 
-        aclWriter.write(id, ifIndex, accessLists.getAcl(), accessLists.getMode(), writeContext);
+        aclWriter.write(id, ifIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(),
+            writeContext);
     }
 
     @Override
index d5f1eb2..79ee728 100644 (file)
@@ -80,8 +80,8 @@ public class SubInterfaceIetfAclCustomizer implements WriterCustomizer<Ingress>
         checkState(subInterfaceOptional.isPresent(), "Could not read SubInterface data object for %s", id);
         final SubInterface subInterface = subInterfaceOptional.get();
 
-        aclWriter.write(id, subInterfaceIndex, accessLists.getMode(), accessLists.getAcl(), writeContext,
-                getNumberOfTags(subInterface.getTags()));
+        aclWriter.write(id, subInterfaceIndex, accessLists.getMode(), accessLists.getAcl(),
+            accessLists.getDefaultAction(), writeContext, getNumberOfTags(subInterface.getTags()));
     }
 
     @Override
index e1f813e..759c774 100644 (file)
@@ -58,12 +58,11 @@ public class AceEthWriterTest {
     @Test
     public void testCreateClassifyTable() {
         final int nextTableIndex = 42;
-        final ClassifyAddDelTable request = writer.createClassifyTable(action, aceEth, InterfaceMode.L2, nextTableIndex, 0);
+        final ClassifyAddDelTable request = writer.createClassifyTable(aceEth, InterfaceMode.L2, nextTableIndex, 0);
 
         assertEquals(1, request.isAdd);
         assertEquals(-1, request.tableIndex);
         assertEquals(1, request.nbuckets);
-        assertEquals(-1, request.missNextIndex);
         assertEquals(nextTableIndex, request.nextTableIndex);
         assertEquals(0, request.skipNVectors);
         assertEquals(AceEthWriter.MATCH_N_VECTORS, request.matchNVectors);
@@ -81,7 +80,7 @@ public class AceEthWriterTest {
 
     @Test(expected = IllegalArgumentException.class)
     public void testCreateClassifyTableForL3Interface() {
-        writer.createClassifyTable(action, aceEth, InterfaceMode.L3, 42, 0);
+        writer.createClassifyTable(aceEth, InterfaceMode.L3, 42, 0);
     }
 
     @Test
index 9597c16..b454acc 100644 (file)
@@ -64,7 +64,6 @@ public class AceIp4WriterTest {
         assertEquals(1, request.isAdd);
         assertEquals(-1, request.tableIndex);
         assertEquals(1, request.nbuckets);
-        assertEquals(-1, request.missNextIndex);
         assertEquals(nextTableIndex, request.nextTableIndex);
         assertEquals(0, request.skipNVectors);
         assertEquals(AceIp4Writer.MATCH_N_VECTORS, request.matchNVectors);
@@ -107,14 +106,14 @@ public class AceIp4WriterTest {
     @Test
     public void testCreateClassifyTable() throws Exception {
         final int nextTableIndex = 42;
-        final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, InterfaceMode.L3, nextTableIndex, 0);
+        final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, 0);
         verifyTableRequest(request, nextTableIndex, 0, false);
     }
 
     @Test
     public void testCreateClassifyTableForL2Interface() throws Exception {
         final int nextTableIndex = 42;
-        final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, InterfaceMode.L2, nextTableIndex, 0);
+        final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L2, nextTableIndex, 0);
         verifyTableRequest(request, nextTableIndex, 0, true);
     }
 
@@ -122,7 +121,7 @@ public class AceIp4WriterTest {
     public void testCreateClassifyTable1VlanTag() throws Exception {
         final int nextTableIndex = 42;
         final int vlanTags = 1;
-        final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, InterfaceMode.L3, nextTableIndex, vlanTags);
+        final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags);
         verifyTableRequest(request, nextTableIndex, vlanTags, false);
     }
 
@@ -130,7 +129,7 @@ public class AceIp4WriterTest {
     public void testCreateClassifyTable2VlanTags() throws Exception {
         final int nextTableIndex = 42;
         final int vlanTags = 2;
-        final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, InterfaceMode.L3, nextTableIndex, vlanTags);
+        final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags);
         verifyTableRequest(request, nextTableIndex, vlanTags, false);
     }
 
index 504d502..6ac469f 100644 (file)
@@ -67,7 +67,6 @@ public class AceIp6WriterTest {
         assertEquals(1, request.isAdd);
         assertEquals(-1, request.tableIndex);
         assertEquals(1, request.nbuckets);
-        assertEquals(-1, request.missNextIndex);
         assertEquals(nextTableIndex, request.nextTableIndex);
         assertEquals(0, request.skipNVectors);
         assertEquals(AceIp6Writer.MATCH_N_VECTORS, request.matchNVectors);
@@ -130,7 +129,7 @@ public class AceIp6WriterTest {
     public void testCreateClassifyTable() {
         final int nextTableIndex = 42;
         final ClassifyAddDelTable request =
-            writer.createClassifyTable(action, aceIp, InterfaceMode.L3, nextTableIndex, 0);
+            writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, 0);
         verifyTableRequest(request, nextTableIndex, 0, false);
     }
 
@@ -138,7 +137,7 @@ public class AceIp6WriterTest {
     public void testCreateClassifyTableForL2Interface() {
         final int nextTableIndex = 42;
         final ClassifyAddDelTable request =
-            writer.createClassifyTable(action, aceIp, InterfaceMode.L2, nextTableIndex, 0);
+            writer.createClassifyTable(aceIp, InterfaceMode.L2, nextTableIndex, 0);
         verifyTableRequest(request, nextTableIndex, 0, true);
     }
 
@@ -147,7 +146,7 @@ public class AceIp6WriterTest {
         final int nextTableIndex = 42;
         final int vlanTags = 1;
         final ClassifyAddDelTable request =
-            writer.createClassifyTable(action, aceIp, InterfaceMode.L3, nextTableIndex, vlanTags);
+            writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags);
         verifyTableRequest(request, nextTableIndex, vlanTags, false);
     }
 
@@ -156,7 +155,7 @@ public class AceIp6WriterTest {
         final int nextTableIndex = 42;
         final int vlanTags = 2;
         final ClassifyAddDelTable request =
-            writer.createClassifyTable(action, aceIp, InterfaceMode.L3, nextTableIndex, vlanTags);
+            writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags);
         verifyTableRequest(request, nextTableIndex, vlanTags, false);
     }
 
index e2c5f1c..d25d6c3 100644 (file)
@@ -17,6 +17,7 @@
 package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -24,6 +25,7 @@ import com.google.common.base.Optional;
 import io.fd.honeycomb.translate.vpp.util.NamingContext;
 import io.fd.honeycomb.translate.write.WriteFailedException;
 import io.fd.honeycomb.vpp.test.write.WriterCustomizerTest;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply;
 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
@@ -31,15 +33,26 @@ import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface;
 import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply;
 import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
 import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
+import java.util.Arrays;
 import java.util.Collections;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
 import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.EthAcl;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntriesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.AceBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.ActionsBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.MatchesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.Deny;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.DenyBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.Permit;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.PermitBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6Builder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
@@ -58,13 +71,16 @@ public class IetfAclCustomizerTest extends WriterCustomizerTest {
     private static final String IFC_TEST_INSTANCE = "ifc-test-instance";
     private static final String IF_NAME = "local0";
     private static final int IF_INDEX = 1;
-    private static final InstanceIdentifier<Ingress> IID = InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(IF_NAME)).augmentation(
-        VppInterfaceAugmentation.class).child(IetfAcl.class).child(Ingress.class);
+    private static final InstanceIdentifier<Ingress> IID =
+        InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(IF_NAME)).augmentation(
+            VppInterfaceAugmentation.class).child(IetfAcl.class).child(Ingress.class);
     private static final String ACL_NAME = "acl1";
     private static final Class<? extends AclBase> ACL_TYPE = EthAcl.class;
 
     private IetfAclCustomizer customizer;
     private Ingress acl;
+    private int DENY = 0;
+    private int PERMIT = -1;
 
     @Override
     protected void setUp() {
@@ -88,16 +104,7 @@ public class IetfAclCustomizerTest extends WriterCustomizerTest {
         when(writeContext.readAfter(any())).thenReturn(Optional.of(
             new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclBuilder()
                 .setAccessListEntries(
-                    new AccessListEntriesBuilder().setAce(Collections.singletonList(
-                        new AceBuilder()
-                            .setMatches(new MatchesBuilder().setAceType(
-                                new AceIpBuilder()
-                                    .setAceIpVersion(new AceIpv6Builder().build())
-                                    .setProtocol((short)1)
-                                    .build()
-                            ).build())
-                            .setActions(new ActionsBuilder().setPacketHandling(new DenyBuilder().build()).build())
-                            .build()
+                    new AccessListEntriesBuilder().setAce(Arrays.asList(ace(permit()), ace(permit()), ace(deny())
                     )).build()
                 ).build()
 
@@ -106,9 +113,75 @@ public class IetfAclCustomizerTest extends WriterCustomizerTest {
 
         customizer.writeCurrentAttributes(IID, acl, writeContext);
 
-        verify(api).classifyAddDelTable(any());
-        verify(api).classifyAddDelSession(any());
-        verify(api).inputAclSetInterface(inputAclSetInterfaceWriteRequest());
+        final InOrder inOrder = Mockito.inOrder(api);
+        inOrder.verify(api).classifyAddDelTable(argThat(actionOnMissEquals(DENY))); // default action
+        inOrder.verify(api).classifyAddDelTable(any());
+        inOrder.verify(api).classifyAddDelSession(argThat(actionOnHitEquals(DENY))); // last deny ACE
+        inOrder.verify(api).classifyAddDelTable(any());
+        inOrder.verify(api).classifyAddDelSession(argThat(actionOnHitEquals(PERMIT)));
+        inOrder.verify(api).classifyAddDelTable(any());
+        inOrder.verify(api).classifyAddDelSession(argThat(actionOnHitEquals(PERMIT)));
+        inOrder.verify(api).inputAclSetInterface(inputAclSetInterfaceWriteRequest()); // assignment
+    }
+
+    private Matcher<ClassifyAddDelTable> actionOnMissEquals(final int action) {
+        return new BaseMatcher<ClassifyAddDelTable>() {
+            public Object item;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText("Expected ClassifyAddDelTable[missNextIndex=" + action + "] but was " + item);
+            }
+
+            @Override
+            public boolean matches(final Object item) {
+                this.item = item;
+                if (item instanceof ClassifyAddDelTable) {
+                    return ((ClassifyAddDelTable) item).missNextIndex == action;
+                }
+                return false;
+            }
+        };
+    }
+
+    private Matcher<ClassifyAddDelSession> actionOnHitEquals(final int action) {
+        return new BaseMatcher<ClassifyAddDelSession>() {
+            public Object item;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText("Expected ClassifyAddDelSession[hitNextIndex=" + action + "] but was " + item);
+            }
+
+            @Override
+            public boolean matches(final Object item) {
+                this.item = item;
+                if (item instanceof ClassifyAddDelSession) {
+                    return ((ClassifyAddDelSession) item).hitNextIndex == action;
+                }
+                return false;
+            }
+        };
+    }
+
+    private Deny deny() {
+        return new DenyBuilder().build();
+    }
+
+    private Permit permit() {
+        return new PermitBuilder().build();
+    }
+
+    private static Ace ace(final PacketHandling action) {
+        return new AceBuilder()
+            .setMatches(new MatchesBuilder().setAceType(
+                new AceIpBuilder()
+                    .setAceIpVersion(new AceIpv6Builder().build())
+                    .setProtocol((short) 1)
+                    .build()
+            ).build())
+            .setActions(new ActionsBuilder().setPacketHandling(action).build())
+            .build();
     }
 
     @Test