HONEYCOMB-49: VPP classifier model. Translation layer implementation
authorMarek Gradzki <[email protected]>
Wed, 13 Jul 2016 06:48:14 +0000 (08:48 +0200)
committerMarek Gradzki <[email protected]>
Thu, 14 Jul 2016 08:53:40 +0000 (10:53 +0200)
Change-Id: I7d91f29f621fbb7ede9b3dd5d49a9b5c3ba58565
Signed-off-by: Marek Gradzki <[email protected]>
16 files changed:
v3po/v3po2vpp/src/main/config/default-config.xml
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionReader.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionWriter.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableReader.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableWriter.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/VppNodeReader.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombReaderModule.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombReaderModuleFactory.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombWriterModule.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombWriterModuleFactory.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/yang/v3po2vpp.yang
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionReaderTest.java [new file with mode: 0644]
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionWriterTest.java [new file with mode: 0644]
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableReaderTest.java [new file with mode: 0644]
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableWriterTest.java [new file with mode: 0644]

index 6b487a1..09c6c54 100644 (file)
                     <name>bridge-domain-context</name>
                     <artificial-name-prefix>bridge-domain-</artificial-name-prefix>
                 </module>
+                <module>
+                    <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:util">prefix:naming-context-impl</type>
+                    <name>classify-table-context</name>
+                    <artificial-name-prefix>classify-table-</artificial-name-prefix>
+                </module>
 
                 <module>
                     <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:v3po2vpp">prefix:vpp-state-honeycomb-reader</type>
                         <name>bridge-domain-context</name>
                     </bridge-domain-context-ifc-state>
                 </module>
+                <module>
+                    <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:v3po2vpp">prefix:vpp-classifier-honeycomb-reader</type>
+                    <name>vpp-classifier-honeycomb-reader</name>
+                    <vpp-jvpp>
+                        <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:jvpp:cfg">prefix:vpp-jvpp</type>
+                        <name>vpp-jvpp</name>
+                    </vpp-jvpp>
+                    <classify-table-context>
+                        <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:util">prefix:naming-context</type>
+                        <name>classify-table-context</name>
+                    </classify-table-context>
+                </module>
 
                 <module>
                     <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:v3po2vpp">prefix:vpp-honeycomb-writer</type>
                     </bridge-domain-context>
                 </module>
 
+                <module>
+                    <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:v3po2vpp">prefix:vpp-classifier-honeycomb-writer</type>
+                    <name>vpp-classifier-honeycomb-writer</name>
+                    <vpp-jvpp>
+                        <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:jvpp:cfg">prefix:vpp-jvpp</type>
+                        <name>vpp-jvpp</name>
+                    </vpp-jvpp>
+                    <classify-table-context>
+                        <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:util">prefix:naming-context</type>
+                        <name>classify-table-context</name>
+                    </classify-table-context>
+                </module>
+
                 <module>
                     <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:delegating-reader-registry</type>
                     <name>read-registry</name>
                         <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-reader</type>
                         <name>interfaces-state-honeycomb-reader</name>
                     </root-readers>
+                    <root-readers>
+                        <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-reader</type>
+                        <name>vpp-classifier-honeycomb-reader</name>
+                    </root-readers>
                 </module>
 
                 <module>
                         <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-factory</type>
                         <name>interfaces-honeycomb-writer</name>
                     </writer-factory>
+                    <writer-factory>
+                        <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-factory</type>
+                        <name>vpp-classifier-honeycomb-writer</name>
+                    </writer-factory>
                 </module>
             </modules>
 
                         <name>interfaces-state-honeycomb-reader</name>
                         <provider>/modules/module[type='interfaces-state-honeycomb-reader'][name='interfaces-state-honeycomb-reader']</provider>
                     </instance>
+                    <instance>
+                        <name>vpp-classifier-honeycomb-reader</name>
+                        <provider>/modules/module[type='vpp-classifier-honeycomb-reader'][name='vpp-classifier-honeycomb-reader']</provider>
+                    </instance>
                 </service>
                 <service>
                     <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:honeycomb-notification-producer</type>
                         <name>bridge-domain-context</name>
                         <provider>/modules/module[type='naming-context-impl'][name='bridge-domain-context']</provider>
                     </instance>
+                    <instance>
+                        <name>classify-table-context</name>
+                        <provider>/modules/module[type='naming-context-impl'][name='classify-table-context']</provider>
+                    </instance>
                 </service>
                 <service>
                     <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-factory</type>
                         <provider>/modules/module[type='interfaces-honeycomb-writer'][name='interfaces-honeycomb-writer']
                         </provider>
                     </instance>
+                    <instance>
+                        <name>vpp-classifier-honeycomb-writer</name>
+                        <provider>/modules/module[type='vpp-classifier-honeycomb-writer'][name='vpp-classifier-honeycomb-writer']
+                        </provider>
+                    </instance>
                 </service>
             </services>
         </data>
index 7b4b2d0..f902f89 100644 (file)
@@ -117,21 +117,30 @@ public final class InterfaceUtils {
         return vppPhysAddrToYang(vppPhysAddress, 0);
     }
 
-    public static String vppPhysAddrToYang(@Nonnull final byte[] vppPhysAddress, int startIndex) {
+    public static String vppPhysAddrToYang(@Nonnull final byte[] vppPhysAddress, final int startIndex) {
         Objects.requireNonNull(vppPhysAddress, "Empty physical address bytes");
         final int endIndex = startIndex + PHYSICAL_ADDRESS_LENGTH;
         checkArgument(endIndex <= vppPhysAddress.length,
             "Invalid physical address size (%s) for given startIndex (%s), expected >= %s", vppPhysAddress.length,
             startIndex, endIndex);
-        StringBuilder physAddr = new StringBuilder();
+        return printHexBinary(vppPhysAddress, startIndex, endIndex);
+    }
+
+    public static String printHexBinary(@Nonnull final byte[] bytes) {
+        Objects.requireNonNull(bytes, "bytes array should not be null");
+        return printHexBinary(bytes, 0, bytes.length);
+    }
+
+    private static String printHexBinary(@Nonnull final byte[] bytes, final int startIndex, final int endIndex) {
+        StringBuilder str = new StringBuilder();
 
-        appendHexByte(physAddr, vppPhysAddress[startIndex]);
+        appendHexByte(str, bytes[startIndex]);
         for (int i = startIndex + 1; i < endIndex; i++) {
-            physAddr.append(":");
-            appendHexByte(physAddr, vppPhysAddress[i]);
+            str.append(":");
+            appendHexByte(str, bytes[i]);
         }
 
-        return physAddr.toString();
+        return str.toString();
     }
 
     /**
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionReader.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionReader.java
new file mode 100644 (file)
index 0000000..002fc62
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceUtils.printHexBinary;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedInts;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.spi.read.ListReaderCustomizer;
+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.util.TranslateUtils;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.xml.bind.DatatypeConverter;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.OpaqueIndex;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySession;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppBaseCallException;
+import org.openvpp.jvpp.dto.ClassifySessionDetails;
+import org.openvpp.jvpp.dto.ClassifySessionDetailsReplyDump;
+import org.openvpp.jvpp.dto.ClassifySessionDump;
+import org.openvpp.jvpp.future.FutureJVpp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Reader customizer responsible for classify session read.<br> to VPP.<br> Equivalent to invoking {@code vppctl show
+ * class table verbose} command.
+ */
+public class ClassifySessionReader extends FutureJVppCustomizer
+    implements ListReaderCustomizer<ClassifySession, ClassifySessionKey, ClassifySessionBuilder>, VppNodeReader {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ClassifySessionReader.class);
+    static final String CACHE_KEY = ClassifySessionReader.class.getName();
+
+    private final NamingContext classifyTableContext;
+
+    public ClassifySessionReader(@Nonnull final FutureJVpp futureJvpp,
+                                 @Nonnull final NamingContext classifyTableContext) {
+        super(futureJvpp);
+        this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null");
+    }
+
+    @Override
+    public void merge(@Nonnull final Builder<? extends DataObject> builder,
+                      @Nonnull final List<ClassifySession> readData) {
+        ((ClassifyTableBuilder) builder).setClassifySession(readData);
+    }
+
+    @Nonnull
+    @Override
+    public ClassifySessionBuilder getBuilder(@Nonnull final InstanceIdentifier<ClassifySession> id) {
+        return new ClassifySessionBuilder();
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id,
+                                      @Nonnull final ClassifySessionBuilder builder, @Nonnull final ReadContext ctx)
+        throws ReadFailedException {
+        LOG.debug("Reading attributes for classify session: {}", id);
+
+        final ClassifySessionKey key = id.firstKeyOf(ClassifySession.class);
+        checkArgument(key != null, "could not find ClassifySession key in {}", id);
+
+        final ClassifySessionDetailsReplyDump classifySessionDump = dumpClassifySessions(id, ctx);
+        final byte[] match = DatatypeConverter.parseHexBinary(key.getMatch().getValue().replace(":", ""));
+        final Optional<ClassifySessionDetails> classifySession =
+            findClassifySessionDetailsByMatch(classifySessionDump, match);
+
+        if (classifySession.isPresent()) {
+            final ClassifySessionDetails detail = classifySession.get();
+            builder.setHitNextIndex(readVppNode(detail.hitNextIndex, LOG));
+            if (detail.opaqueIndex != ~0) {
+                // value is specified:
+                builder.setOpaqueIndex(readOpaqueIndex(detail.opaqueIndex));
+            }
+            builder.setAdvance(detail.advance);
+            builder.setMatch(key.getMatch());
+
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Attributes for classify session {} successfully read: {}", id, builder.build());
+            }
+        }
+    }
+
+    private OpaqueIndex readOpaqueIndex(final int opaqueIndex) {
+        // We first try to map the value to a vpp node, if that fails, simply wrap the u32 value
+        // FIXME: the approach might fail if the opaqueIndex contains small value that collides
+        // with some of the adjacent nodes
+        final VppNode node = readVppNode(opaqueIndex, LOG);
+        if (node != null) {
+            return new OpaqueIndex(node);
+        } else {
+            return new OpaqueIndex(UnsignedInts.toLong(opaqueIndex));
+        }
+    }
+
+    @Nullable
+    private ClassifySessionDetailsReplyDump dumpClassifySessions(@Nonnull final InstanceIdentifier<?> id,
+                                                                 @Nonnull final ReadContext ctx)
+        throws ReadFailedException {
+        final ClassifyTableKey tableKey = id.firstKeyOf(ClassifyTable.class);
+        checkArgument(tableKey != null, "could not find ClassifyTable key in {}", id);
+
+        final String cacheKey = CACHE_KEY + tableKey;
+
+        ClassifySessionDetailsReplyDump classifySessionDump =
+            (ClassifySessionDetailsReplyDump) ctx.getModificationCache().get(cacheKey);
+        if (classifySessionDump != null) {
+            LOG.debug("Classify sessions is present in cache: {}", cacheKey);
+            return classifySessionDump;
+        }
+
+        final String tableName = tableKey.getName();
+        checkState(classifyTableContext.containsIndex(tableName, ctx.getMappingContext()),
+            "Reading classify sessions for table {}, but table index could not be found in the classify table context",
+            tableName);
+        final int tableId = classifyTableContext.getIndex(tableName, ctx.getMappingContext());
+        LOG.debug("Dumping classify sessions for classify table id={}", tableId);
+
+        try {
+            final ClassifySessionDump dumpRequest = new ClassifySessionDump();
+            dumpRequest.tableId = tableId;
+            classifySessionDump = TranslateUtils
+                .getReplyForRead(getFutureJVpp().classifySessionDump(dumpRequest).toCompletableFuture(), id);
+
+            // update the cache:
+            ctx.getModificationCache().put(cacheKey, classifySessionDump);
+            return classifySessionDump;
+        } catch (VppBaseCallException e) {
+            throw new ReadFailedException(id, e);
+        }
+    }
+
+    private static Optional<ClassifySessionDetails> findClassifySessionDetailsByMatch(
+        @Nullable final ClassifySessionDetailsReplyDump classifySessionDump, @Nonnull final byte[] match) {
+        if (classifySessionDump != null && classifySessionDump.classifySessionDetails != null) {
+            final List<ClassifySessionDetails> details = classifySessionDump.classifySessionDetails;
+            final List<ClassifySessionDetails> filteredSessions = details.stream()
+                .filter(singleDetail -> Arrays.equals(singleDetail.match, match)).collect(Collectors.toList());
+            if (filteredSessions.isEmpty()) {
+                return Optional.absent();
+            } else if (filteredSessions.size() == 1) {
+                return Optional.of(filteredSessions.get(0));
+            } else {
+                throw new IllegalStateException(String.format(
+                    "Found %d classify sessions witch given match. Single session expected.",
+                    filteredSessions.size()));
+            }
+        }
+        return Optional.absent();
+    }
+
+    @Nonnull
+    @Override
+    public List<ClassifySessionKey> getAllIds(@Nonnull final InstanceIdentifier<ClassifySession> id,
+                                              @Nonnull final ReadContext ctx) throws ReadFailedException {
+        LOG.debug("Reading list of keys for classify sessions: {}", id);
+
+        final ClassifySessionDetailsReplyDump classifySessionDump = dumpClassifySessions(id, ctx);
+        if (classifySessionDump != null && classifySessionDump.classifySessionDetails != null) {
+            return classifySessionDump.classifySessionDetails.stream()
+                .map(detail -> new ClassifySessionKey(new HexString(printHexBinary(detail.match))))
+                .collect(Collectors.toList());
+        } else {
+            return Collections.emptyList();
+        }
+    }
+}
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionWriter.java
new file mode 100644 (file)
index 0000000..08989ce
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static io.fd.honeycomb.v3po.translate.v3po.util.TranslateUtils.booleanToByte;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer;
+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.util.TranslateUtils;
+import io.fd.honeycomb.v3po.translate.v3po.util.WriteTimeoutException;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import javax.annotation.Nonnull;
+import javax.xml.bind.DatatypeConverter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.OpaqueIndex;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySession;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppBaseCallException;
+import org.openvpp.jvpp.dto.ClassifyAddDelSession;
+import org.openvpp.jvpp.dto.ClassifyAddDelSessionReply;
+import org.openvpp.jvpp.future.FutureJVpp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Writer customizer responsible for classify session create/delete.<br> Sends {@code classify_add_del_session} message
+ * to VPP.<br> Equivalent to invoking {@code vppctl classify table} command.
+ */
+public class ClassifySessionWriter extends FutureJVppCustomizer
+    implements ListWriterCustomizer<ClassifySession, ClassifySessionKey> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ClassifySessionWriter.class);
+    private final NamingContext classifyTableContext;
+
+    public ClassifySessionWriter(@Nonnull final FutureJVpp futureJvpp,
+                                 @Nonnull final NamingContext classifyTableContext) {
+        super(futureJvpp);
+        this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null");
+    }
+
+    @Nonnull
+    @Override
+    public Optional<List<ClassifySession>> extract(@Nonnull final InstanceIdentifier<ClassifySession> currentId,
+                                                   @Nonnull final DataObject parentData) {
+        return Optional.fromNullable(((ClassifyTable) parentData).getClassifySession());
+    }
+
+    @Override
+    public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id,
+                                       @Nonnull final ClassifySession dataAfter,
+                                       @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        LOG.debug("Creating classify session: iid={} dataAfter={}", id, dataAfter);
+        try {
+            classifyAddDelSession(true, id, dataAfter, writeContext);
+            LOG.debug("Successfully created classify session: iid={} dataAfter={}", id, dataAfter);
+        } catch (VppBaseCallException e) {
+            throw new WriteFailedException.CreateFailedException(id, dataAfter, e);
+        }
+    }
+
+    @Override
+    public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id,
+                                        @Nonnull final ClassifySession dataBefore,
+                                        @Nonnull final ClassifySession dataAfter,
+                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        throw new UnsupportedOperationException("Classify session update is not supported");
+    }
+
+    @Override
+    public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifySession> id,
+                                        @Nonnull final ClassifySession dataBefore,
+                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        LOG.debug("Removing classify session: iid={} dataBefore={}", id, dataBefore);
+        try {
+            classifyAddDelSession(false, id, dataBefore, writeContext);
+            LOG.debug("Successfully removed classify session: iid={} dataBefore={}", id, dataBefore);
+        } catch (VppBaseCallException e) {
+            throw new WriteFailedException.DeleteFailedException(id, e);
+        }
+    }
+
+    private void classifyAddDelSession(final boolean isAdd, @Nonnull final InstanceIdentifier<ClassifySession> id,
+                                       @Nonnull final ClassifySession classifySession,
+                                       @Nonnull final WriteContext writeContext)
+        throws VppBaseCallException, WriteTimeoutException {
+        final ClassifyTableKey tableKey = id.firstKeyOf(ClassifyTable.class);
+        checkArgument(tableKey != null, "could not find classify table key in {}", id);
+
+        final String tableName = tableKey.getName();
+        checkState(classifyTableContext.containsIndex(tableName, writeContext.getMappingContext()),
+            "Could not find classify table index for {} in the classify table context", tableName);
+        final int tableIndex = classifyTableContext.getIndex(tableName, writeContext.getMappingContext());
+
+        final CompletionStage<ClassifyAddDelSessionReply> createClassifyTableReplyCompletionStage = getFutureJVpp()
+            .classifyAddDelSession(
+                getClassifyAddDelSessionRequest(isAdd, tableIndex, classifySession));
+
+        TranslateUtils.getReplyForWrite(createClassifyTableReplyCompletionStage.toCompletableFuture(), id);
+    }
+
+    private static ClassifyAddDelSession getClassifyAddDelSessionRequest(final boolean isAdd, final int tableIndex,
+                                                                         @Nonnull final ClassifySession classifySession) {
+        ClassifyAddDelSession request = new ClassifyAddDelSession();
+        request.isAdd = booleanToByte(isAdd);
+        request.tableIndex = tableIndex;
+
+        // mandatory:
+        // TODO implement node name to index conversion after https://jira.fd.io/browse/VPP-203 is fixed
+        request.hitNextIndex = classifySession.getHitNextIndex().getPacketHandlingAction().getIntValue();
+
+        if (classifySession.getOpaqueIndex() != null) {
+            request.opaqueIndex = getOpaqueIndexValue(classifySession.getOpaqueIndex());
+        } else {
+            request.opaqueIndex = ~0; // value not specified
+        }
+
+        // default 0:
+        request.advance = classifySession.getAdvance();
+
+        request.match = DatatypeConverter.parseHexBinary(classifySession.getMatch().getValue().replace(":", ""));
+        return request;
+    }
+
+    private static int getOpaqueIndexValue(@Nonnull final OpaqueIndex opaqueIndex) {
+        if (opaqueIndex.getUint32() != null) {
+            return opaqueIndex.getUint32().intValue();
+        } else {
+            // TODO: implement node name to index conversion after https://jira.fd.io/browse/VPP-203 is fixed
+            return opaqueIndex.getVppNode().getPacketHandlingAction().getIntValue();
+        }
+    }
+}
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableReader.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableReader.java
new file mode 100644 (file)
index 0000000..7f877ca
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceUtils.printHexBinary;
+
+import com.google.common.primitives.UnsignedInts;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.spi.read.ListReaderCustomizer;
+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.util.TranslateUtils;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifierBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppBaseCallException;
+import org.openvpp.jvpp.dto.ClassifyTableIds;
+import org.openvpp.jvpp.dto.ClassifyTableIdsReply;
+import org.openvpp.jvpp.dto.ClassifyTableInfo;
+import org.openvpp.jvpp.dto.ClassifyTableInfoReply;
+import org.openvpp.jvpp.future.FutureJVpp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Reader customizer responsible for classify table read.<br> to VPP.<br> Equivalent to invoking {@code vppctl show
+ * class table} command.
+ */
+public class ClassifyTableReader extends FutureJVppCustomizer
+    implements ListReaderCustomizer<ClassifyTable, ClassifyTableKey, ClassifyTableBuilder>, VppNodeReader {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ClassifyTableReader.class);
+    private final NamingContext classifyTableContext;
+
+    public ClassifyTableReader(@Nonnull final FutureJVpp futureJvpp,
+                               @Nonnull final NamingContext classifyTableContext) {
+        super(futureJvpp);
+        this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null");
+    }
+
+
+    @Override
+    public void merge(@Nonnull final Builder<? extends DataObject> builder,
+                      @Nonnull final List<ClassifyTable> readData) {
+        ((VppClassifierBuilder) builder).setClassifyTable(readData);
+    }
+
+    @Nonnull
+    @Override
+    public ClassifyTableBuilder getBuilder(@Nonnull final InstanceIdentifier<ClassifyTable> id) {
+        return new ClassifyTableBuilder();
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifyTable> id,
+                                      @Nonnull final ClassifyTableBuilder builder, @Nonnull final ReadContext ctx)
+        throws ReadFailedException {
+        LOG.debug("Reading attributes for classify table: {}", id);
+
+        final ClassifyTableKey key = id.firstKeyOf(ClassifyTable.class);
+        checkArgument(key != null, "could not find ClassifyTable key in {}", id);
+        final ClassifyTableInfo request = new ClassifyTableInfo();
+
+        final String tableName = key.getName();
+        if (!classifyTableContext.containsIndex(tableName, ctx.getMappingContext())) {
+            LOG.debug("Could not find classify table {} in the naming context", tableName);
+            return;
+        }
+        request.tableId = classifyTableContext.getIndex(tableName, ctx.getMappingContext());
+
+        try {
+            final ClassifyTableInfoReply reply =
+                TranslateUtils.getReplyForRead(getFutureJVpp().classifyTableInfo(request).toCompletableFuture(), id);
+
+            // mandatory values:
+            builder.setName(tableName);
+            builder.setKey(key);
+            builder.setNbuckets(UnsignedInts.toLong(reply.nbuckets));
+            builder.setSkipNVectors(UnsignedInts.toLong(reply.skipNVectors));
+
+
+            builder.setMissNextIndex(readVppNode(reply.missNextIndex, LOG));
+            builder.setMask(new HexString(printHexBinary(reply.mask)));
+            builder.setActiveSessions(UnsignedInts.toLong(reply.activeSessions));
+
+            if (reply.nextTableIndex != ~0) {
+                // next table index is present:
+                builder.setNextTable(classifyTableContext.getName(reply.nextTableIndex, ctx.getMappingContext()));
+            }
+
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Attributes for classify table {} successfully read: {}", id, builder.build());
+            }
+        } catch (VppBaseCallException e) {
+            throw new ReadFailedException(id, e);
+        }
+    }
+
+    @Nonnull
+    @Override
+    public List<ClassifyTableKey> getAllIds(@Nonnull final InstanceIdentifier<ClassifyTable> id,
+                                            @Nonnull final ReadContext context) throws ReadFailedException {
+        LOG.debug("Reading list of keys for classify tables: {}", id);
+        try {
+            final ClassifyTableIdsReply classifyTableIdsReply = TranslateUtils
+                .getReplyForRead(getFutureJVpp().classifyTableIds(new ClassifyTableIds()).toCompletableFuture(), id);
+            if (classifyTableIdsReply.ids != null) {
+                return Arrays.stream(classifyTableIdsReply.ids).mapToObj(i -> {
+                    final String tableName = classifyTableContext.getName(i, context.getMappingContext());
+                    LOG.trace("Classify table with name: {} and index: {} found in VPP", tableName, i);
+                    return new ClassifyTableKey(tableName);
+                }).collect(Collectors.toList());
+            } else {
+                return Collections.emptyList();
+            }
+        } catch (VppBaseCallException e) {
+            throw new ReadFailedException(id, e);
+        }
+    }
+}
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableWriter.java
new file mode 100644 (file)
index 0000000..4f59ffe
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static io.fd.honeycomb.v3po.translate.v3po.util.TranslateUtils.booleanToByte;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer;
+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.util.TranslateUtils;
+import io.fd.honeycomb.v3po.translate.v3po.util.WriteTimeoutException;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import javax.annotation.Nonnull;
+import javax.xml.bind.DatatypeConverter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppBaseCallException;
+import org.openvpp.jvpp.dto.ClassifyAddDelTable;
+import org.openvpp.jvpp.dto.ClassifyAddDelTableReply;
+import org.openvpp.jvpp.future.FutureJVpp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Writer customizer responsible for classify table create/delete. <br> Sends {@code classify_add_del_table} message to
+ * VPP.<br> Equivalent to invoking {@code vppctl classify table} command.
+ */
+public class ClassifyTableWriter extends FutureJVppCustomizer
+    implements ListWriterCustomizer<ClassifyTable, ClassifyTableKey> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ClassifyTableWriter.class);
+    private final NamingContext classifyTableContext;
+
+    public ClassifyTableWriter(@Nonnull final FutureJVpp futureJvpp,
+                               @Nonnull final NamingContext classifyTableContext) {
+        super(futureJvpp);
+        this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null");
+    }
+
+    @Nonnull
+    @Override
+    public Optional<List<ClassifyTable>> extract(@Nonnull final InstanceIdentifier<ClassifyTable> currentId,
+                                                 @Nonnull final DataObject parentData) {
+        return Optional.fromNullable(((VppClassifier) parentData).getClassifyTable());
+    }
+
+    @Override
+    public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifyTable> id,
+                                       @Nonnull final ClassifyTable dataAfter, @Nonnull final WriteContext writeContext)
+        throws WriteFailedException {
+        LOG.debug("Creating classify table: iid={} dataAfter={}", id, dataAfter);
+        try {
+            final int newTableIndex =
+                classifyAddDelTable(true, id, dataAfter, ~0 /* value not present */, writeContext.getMappingContext());
+
+            // Add classify table name <-> vpp index mapping to the naming context:
+            classifyTableContext.addName(newTableIndex, dataAfter.getName(), writeContext.getMappingContext());
+            LOG.debug("Successfully created classify table(id={]): iid={} dataAfter={}", newTableIndex, id, dataAfter);
+        } catch (VppBaseCallException e) {
+            throw new WriteFailedException.CreateFailedException(id, dataAfter, e);
+        }
+    }
+
+    @Override
+    public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifyTable> id,
+                                        @Nonnull final ClassifyTable dataBefore, @Nonnull final ClassifyTable dataAfter,
+                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        LOG.warn("ClassifyTable update is not supported, ignoring configuration {}", dataAfter);
+        // TODO if only leaves were updated (but not child/aug nodes), we should throw exception to deny config change
+    }
+
+    @Override
+    public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<ClassifyTable> id,
+                                        @Nonnull final ClassifyTable dataBefore,
+                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
+        LOG.debug("Removing classify table: iid={} dataBefore={}", id, dataBefore);
+        final String tableName = dataBefore.getName();
+        checkState(classifyTableContext.containsIndex(tableName, writeContext.getMappingContext()),
+            "Removing classify table {}, but index could not be found in the classify table context", tableName);
+
+        final int tableIndex = classifyTableContext.getIndex(tableName, writeContext.getMappingContext());
+        try {
+            classifyAddDelTable(false, id, dataBefore, tableIndex, writeContext.getMappingContext());
+
+            // Remove deleted interface from interface context:
+            classifyTableContext.removeName(dataBefore.getName(), writeContext.getMappingContext());
+            LOG.debug("Successfully removed classify table(id={]): iid={} dataAfter={}", tableIndex, id, dataBefore);
+        } catch (VppBaseCallException e) {
+            throw new WriteFailedException.DeleteFailedException(id, e);
+        }
+    }
+
+    private int classifyAddDelTable(final boolean isAdd, @Nonnull final InstanceIdentifier<ClassifyTable> id,
+                                    @Nonnull final ClassifyTable table, final int tableId, final MappingContext ctx)
+        throws VppBaseCallException, WriteTimeoutException {
+        final CompletionStage<ClassifyAddDelTableReply> createClassifyTableReplyCompletionStage =
+            getFutureJVpp().classifyAddDelTable(getClassifyAddDelTableRequest(isAdd, tableId, table, ctx));
+
+        final ClassifyAddDelTableReply reply =
+            TranslateUtils.getReplyForWrite(createClassifyTableReplyCompletionStage.toCompletableFuture(), id);
+        return reply.newTableIndex;
+
+    }
+
+    private ClassifyAddDelTable getClassifyAddDelTableRequest(final boolean isAdd, final int tableIndex,
+                                                              @Nonnull final ClassifyTable table,
+                                                              @Nonnull final MappingContext ctx) {
+        final ClassifyAddDelTable request = new ClassifyAddDelTable();
+        request.isAdd = booleanToByte(isAdd);
+        request.tableIndex = tableIndex;
+
+        // mandatory, all u32 values are permitted:
+        request.nbuckets = table.getNbuckets().intValue();
+        request.memorySize = table.getMemorySize().intValue();
+        request.skipNVectors = table.getSkipNVectors().intValue();
+
+        // mandatory
+        // TODO implement node name to index conversion after https://jira.fd.io/browse/VPP-203 is fixed
+        request.missNextIndex = table.getMissNextIndex().getPacketHandlingAction().getIntValue();
+
+        final String nextTable = table.getNextTable();
+        if (nextTable != null) {
+            request.nextTableIndex = classifyTableContext.getIndex(nextTable, ctx);
+        } else {
+            request.nextTableIndex = ~0; // value not specified
+        }
+        request.mask = DatatypeConverter.parseHexBinary(table.getMask().getValue().replace(":", ""));
+        checkArgument(request.mask.length % 16 == 0, "Number of mask bytes must be multiple of 16.");
+        request.matchNVectors = request.mask.length / 16;
+
+        return request;
+    }
+}
\ No newline at end of file
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/VppNodeReader.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/VppNodeReader.java
new file mode 100644 (file)
index 0000000..aa8b4f9
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.PacketHandlingAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppNode;
+import org.slf4j.Logger;
+
+interface VppNodeReader {
+
+    /**
+     * Converts vpp node index to YANG representation of vpp node.
+     *
+     * @param nodeIndex index of vpp node treated as signed integer.
+     * @return vpp node representation
+     */
+    default VppNode readVppNode(final int nodeIndex, @Nonnull final Logger log) {
+        final PacketHandlingAction action = PacketHandlingAction.forValue(nodeIndex);
+        if (action == null) {
+            // TODO: implement node index to name conversion after https://jira.fd.io/browse/VPP-203 is fixed
+            log.debug("VPP node index {} cannot be mapped to PacketHandlingAction", nodeIndex);
+            return null;
+        }
+        return new VppNode(action);
+    }
+}
diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombReaderModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombReaderModule.java
new file mode 100644 (file)
index 0000000..55e86f8
--- /dev/null
@@ -0,0 +1,59 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406;
+
+import io.fd.honeycomb.v3po.translate.impl.read.CompositeListReader;
+import io.fd.honeycomb.v3po.translate.impl.read.CompositeRootReader;
+import io.fd.honeycomb.v3po.translate.read.ChildReader;
+import io.fd.honeycomb.v3po.translate.util.read.CloseableReader;
+import io.fd.honeycomb.v3po.translate.util.read.ReflexiveRootReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.vppclassifier.ClassifySessionReader;
+import io.fd.honeycomb.v3po.translate.v3po.vppclassifier.ClassifyTableReader;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifierBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySession;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+
+public class VppClassifierHoneycombReaderModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractVppClassifierHoneycombReaderModule {
+
+    public VppClassifierHoneycombReaderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public VppClassifierHoneycombReaderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.VppClassifierHoneycombReaderModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        // add custom validation form module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        final CompositeListReader<ClassifySession, ClassifySessionKey, ClassifySessionBuilder> classifySessionReader =
+                new CompositeListReader<>(ClassifySession.class,
+                        new ClassifySessionReader(getVppJvppDependency(), getClassifyTableContextDependency()));
+
+        final List<ChildReader<? extends ChildOf<ClassifyTable>>> classifyTableChildReaders = new ArrayList<>();
+        classifyTableChildReaders.add((ChildReader)classifySessionReader);
+        final CompositeListReader<ClassifyTable, ClassifyTableKey, ClassifyTableBuilder> classifyTableReader =
+                new CompositeListReader<>(
+                        ClassifyTable.class,
+                        classifyTableChildReaders,
+                        new ClassifyTableReader(getVppJvppDependency(), getClassifyTableContextDependency()));
+
+        final List<ChildReader<? extends ChildOf<VppClassifier>>> vppClassifierChildReaders = new ArrayList<>();
+        vppClassifierChildReaders.add(classifyTableReader);
+        return new CloseableReader<>(new CompositeRootReader<>(
+                VppClassifier.class,
+                vppClassifierChildReaders,
+                new ReflexiveRootReaderCustomizer<>(VppClassifierBuilder.class)));
+    }
+
+}
diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombReaderModuleFactory.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombReaderModuleFactory.java
new file mode 100644 (file)
index 0000000..34212f5
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: v3po2vpp yang module local name: vpp-classifier-honeycomb-reader
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri Jun 03 14:05:48 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406;
+public class VppClassifierHoneycombReaderModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractVppClassifierHoneycombReaderModuleFactory {
+
+}
diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombWriterModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombWriterModule.java
new file mode 100644 (file)
index 0000000..52e99b2
--- /dev/null
@@ -0,0 +1,77 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406;
+
+import io.fd.honeycomb.v3po.translate.impl.write.GenericListWriter;
+import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
+import io.fd.honeycomb.v3po.translate.v3po.vppclassifier.ClassifySessionWriter;
+import io.fd.honeycomb.v3po.translate.v3po.vppclassifier.ClassifyTableWriter;
+import io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.WriterFactory;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySession;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.future.FutureJVpp;
+
+public class VppClassifierHoneycombWriterModule extends
+    org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractVppClassifierHoneycombWriterModule {
+
+
+    public static final InstanceIdentifier<ClassifyTable> CLASSIFY_TABLE_ID =
+        InstanceIdentifier.create(VppClassifier.class).child(ClassifyTable.class);
+
+    public static final InstanceIdentifier<ClassifySession> CLASSIFY_SESSION_ID =
+        CLASSIFY_TABLE_ID.child(ClassifySession.class);
+
+
+    public VppClassifierHoneycombWriterModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+                                              org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public VppClassifierHoneycombWriterModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+                                              org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+                                              org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.VppClassifierHoneycombWriterModule oldModule,
+                                              java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        // add custom validation form module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new VppClassifierHoneycombWriterFactory(
+            getVppJvppDependency(),
+            getClassifyTableContextDependency());
+    }
+
+    private static final class VppClassifierHoneycombWriterFactory implements WriterFactory, AutoCloseable {
+        private final FutureJVpp jvpp;
+        private final NamingContext classifyTableContext;
+
+        public VppClassifierHoneycombWriterFactory(@Nonnull final FutureJVpp jvpp,
+                                                   @Nonnull final NamingContext classifyTableContext) {
+            this.jvpp = jvpp;
+            this.classifyTableContext = classifyTableContext;
+        }
+
+        @Override
+        public void close() throws Exception {
+            // unregister is not supported in ModifiableWriterRegistry (not really needed though)
+        }
+
+        @Override
+        public void init(final ModifiableWriterRegistry registry) {
+
+            registry.addWriter(
+                new GenericListWriter<>(CLASSIFY_TABLE_ID, new ClassifyTableWriter(jvpp, classifyTableContext)));
+
+            registry.addWriterAfter(
+                new GenericListWriter<>(CLASSIFY_SESSION_ID, new ClassifySessionWriter(jvpp, classifyTableContext)),
+                CLASSIFY_TABLE_ID);
+        }
+    }
+}
diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombWriterModuleFactory.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppClassifierHoneycombWriterModuleFactory.java
new file mode 100644 (file)
index 0000000..c4ff690
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: v3po2vpp yang module local name: vpp-classifier-honeycomb-writer
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri Jun 03 14:05:48 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406;
+public class VppClassifierHoneycombWriterModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractVppClassifierHoneycombWriterModuleFactory {
+
+}
index 8a55272..0c21570 100644 (file)
@@ -146,6 +146,35 @@ module v3po2vpp {
         }
     }
 
+    identity vpp-classifier-honeycomb-reader {
+        base config:module-type;
+        config:provided-service tapi:honeycomb-reader;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case vpp-classifier-honeycomb-reader {
+            when "/config:modules/config:module/config:type = 'vpp-classifier-honeycomb-reader'";
+
+            container vpp-jvpp {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity vjvppc:vpp-jvpp;
+                    }
+                }
+            }
+
+            container classify-table-context {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity vpp-u:naming-context;
+                    }
+                }
+            }
+        }
+    }
+
 
     identity vpp-honeycomb-writer {
         base config:module-type;
@@ -224,6 +253,35 @@ module v3po2vpp {
         }
     }
 
+    identity vpp-classifier-honeycomb-writer {
+        base config:module-type;
+        config:provided-service tapi:honeycomb-writer-factory;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case vpp-classifier-honeycomb-writer {
+            when "/config:modules/config:module/config:type = 'vpp-classifier-honeycomb-writer'";
+
+            container vpp-jvpp {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity vjvppc:vpp-jvpp;
+                    }
+                }
+            }
+
+            container classify-table-context {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity vpp-u:naming-context;
+                    }
+                }
+            }
+        }
+    }
+
     identity vpp-cfg-initializer {
         base config:module-type;
         config:provided-service init:cfg-initializer;
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionReaderTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionReaderTest.java
new file mode 100644 (file)
index 0000000..ce0d3f6
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMapping;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.ModificationCache;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.test.ListReaderCustomizerTest;
+import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySession;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.dto.ClassifySessionDetails;
+import org.openvpp.jvpp.dto.ClassifySessionDetailsReplyDump;
+import org.openvpp.jvpp.dto.ClassifySessionDump;
+
+public class ClassifySessionReaderTest extends
+    ListReaderCustomizerTest<ClassifySession, ClassifySessionKey, ClassifySessionBuilder> {
+
+    private static final String MATCH_1 = "00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00";
+    private static final String MATCH_2 = "00:00:00:00:00:00:01:02:03:04:05:07:00:00:00:00";
+
+    private static final int TABLE_INDEX = 1;
+    private static final String TABLE_NAME = "table1";
+
+    private NamingContext classifyTableContext;
+
+    public ClassifySessionReaderTest() {
+        super(ClassifySession.class);
+    }
+
+    @Override
+    public void setUpBefore() {
+        classifyTableContext = new NamingContext("classifyTableContext", "test-instance");
+
+        final Optional<Mapping> ifcMapping = getMapping(TABLE_NAME, TABLE_INDEX);
+        doReturn(ifcMapping).when(mappingContext).read(any());
+    }
+
+    @Override
+    protected RootReaderCustomizer<ClassifySession, ClassifySessionBuilder> initCustomizer() {
+        return new ClassifySessionReader(api, classifyTableContext);
+    }
+
+    private static InstanceIdentifier<ClassifySession> getClassifySessionId(final String tableName,
+                                                                            final String match) {
+        return InstanceIdentifier.create(VppClassifier.class)
+            .child(ClassifyTable.class, new ClassifyTableKey(tableName))
+            .child(ClassifySession.class, new ClassifySessionKey(new HexString(match)));
+    }
+
+    @Test
+    public void testMerge() {
+        final ClassifyTableBuilder builder = mock(ClassifyTableBuilder.class);
+        final List<ClassifySession> value = mock(List.class);
+        getCustomizer().merge(builder, value);
+        verify(builder).setClassifySession(value);
+    }
+
+    @Test
+    public void testReadWithCache() throws ReadFailedException {
+        final InstanceIdentifier<ClassifySession> id = getClassifySessionId(TABLE_NAME, MATCH_1);
+        final ClassifySessionBuilder builder = mock(ClassifySessionBuilder.class);
+        final ModificationCache cache = new ModificationCache();
+        final ClassifySessionDetailsReplyDump dump = new ClassifySessionDetailsReplyDump();
+        final ClassifySessionDetails details = new ClassifySessionDetails();
+        details.match =
+            new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+                (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00};
+        dump.classifySessionDetails = Collections.singletonList(details);
+        cache.put(ClassifySessionReader.CACHE_KEY + id.firstKeyOf(ClassifyTable.class), dump);
+        when(ctx.getModificationCache()).thenReturn(cache);
+
+        getCustomizer().readCurrentAttributes(id, builder, ctx);
+    }
+
+    @Test
+    public void testGetAllIds() throws ReadFailedException {
+        final InstanceIdentifier<ClassifySession> id = getClassifySessionId(TABLE_NAME, MATCH_1);
+        final ClassifySessionDetailsReplyDump dump = new ClassifySessionDetailsReplyDump();
+        final ClassifySessionDetails details1 = new ClassifySessionDetails();
+        details1.match =
+            new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+                (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00};
+        final ClassifySessionDetails details2 = new ClassifySessionDetails();
+        details2.match =
+            new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+                (byte) 0x05, (byte) 0x07, 0x00, 0x00, 0x00, 0x00};
+        dump.classifySessionDetails = Arrays.asList(details1, details2);
+
+        final CompletableFuture<ClassifySessionDetailsReplyDump> replyFuture = new CompletableFuture<>();
+        replyFuture.complete(dump);
+        doReturn(replyFuture).when(api).classifySessionDump(any(ClassifySessionDump.class));
+
+        final List<ClassifySessionKey> allIds = getCustomizer().getAllIds(id, ctx);
+        assertEquals(2, allIds.size());
+        assertEquals(MATCH_1, allIds.get(0).getMatch().getValue());
+        assertEquals(MATCH_2, allIds.get(1).getMatch().getValue());
+    }
+
+}
\ No newline at end of file
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionWriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifySessionWriterTest.java
new file mode 100644 (file)
index 0000000..04a766f
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMapping;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.v3po.test.TestHelperUtils;
+import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.OpaqueIndex;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.PacketHandlingAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySession;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.classify.table.attributes.ClassifySessionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppBaseCallException;
+import org.openvpp.jvpp.dto.ClassifyAddDelSession;
+import org.openvpp.jvpp.dto.ClassifyAddDelSessionReply;
+import org.openvpp.jvpp.dto.L2InterfaceVlanTagRewriteReply;
+import org.openvpp.jvpp.future.FutureJVpp;
+
+public class ClassifySessionWriterTest {
+
+    private static final int TABLE_INDEX = 123;
+    private static final String TABLE_NAME = "table123";
+
+    @Mock
+    private FutureJVpp api;
+    @Mock
+    private WriteContext writeContext;
+    @Mock
+    private MappingContext mappingContext;
+
+    private NamingContext classifyTableContext;
+    private ClassifySessionWriter customizer;
+    private static final int SESSION_INDEX = 456;
+
+    @Before
+    public void setUp() throws Exception {
+        initMocks(this);
+        classifyTableContext = new NamingContext("generatedClassifyTableName", "test-instance");
+        doReturn(mappingContext).when(writeContext).getMappingContext();
+        customizer = new ClassifySessionWriter(api, classifyTableContext);
+
+        final Optional<Mapping> ifcMapping = getMapping(TABLE_NAME, TABLE_INDEX);
+        doReturn(ifcMapping).when(mappingContext).read(any());
+    }
+
+    private static ClassifySession generateClassifySession(final long opaqueIndex, final String match) {
+        final ClassifySessionBuilder builder = new ClassifySessionBuilder();
+        builder.setOpaqueIndex(new OpaqueIndex(opaqueIndex));
+        builder.setHitNextIndex(new VppNode(PacketHandlingAction.Deny));
+        builder.setAdvance(123);
+        builder.setMatch(new HexString(match));
+        return builder.build();
+    }
+
+    private static InstanceIdentifier<ClassifySession> getClassifySessionId(final String tableName,
+                                                                            final String match) {
+        return InstanceIdentifier.create(VppClassifier.class)
+            .child(ClassifyTable.class, new ClassifyTableKey(tableName))
+            .child(ClassifySession.class, new ClassifySessionKey(new HexString(match)));
+    }
+
+    private void whenClassifyAddDelSessionThenSuccess() throws ExecutionException, InterruptedException {
+        final CompletableFuture<ClassifyAddDelSessionReply> replyFuture = new CompletableFuture<>();
+        replyFuture.complete(new ClassifyAddDelSessionReply());
+        doReturn(replyFuture).when(api).classifyAddDelSession(any(ClassifyAddDelSession.class));
+    }
+
+    private void whenClassifyAddDelSessionThenFailure() throws ExecutionException, InterruptedException {
+        doReturn(TestHelperUtils.<L2InterfaceVlanTagRewriteReply>createFutureException()).when(api)
+            .classifyAddDelSession(any(ClassifyAddDelSession.class));
+    }
+
+    private void verifyClassifyAddDelSessionWasInvoked(final ClassifyAddDelSession expected) {
+        ArgumentCaptor<ClassifyAddDelSession> argumentCaptor = ArgumentCaptor.forClass(ClassifyAddDelSession.class);
+        verify(api).classifyAddDelSession(argumentCaptor.capture());
+        final ClassifyAddDelSession actual = argumentCaptor.getValue();
+        assertEquals(expected.opaqueIndex, actual.opaqueIndex);
+        assertEquals(expected.isAdd, actual.isAdd);
+        assertEquals(expected.tableIndex, actual.tableIndex);
+        assertEquals(expected.hitNextIndex, actual.hitNextIndex);
+        assertArrayEquals(expected.match, actual.match);
+        assertEquals(expected.advance, actual.advance);
+    }
+
+    private void verifyClassifyAddDelSessionDeleteWasInvoked(final ClassifyAddDelSession expected) {
+        ArgumentCaptor<ClassifyAddDelSession> argumentCaptor = ArgumentCaptor.forClass(ClassifyAddDelSession.class);
+        verify(api).classifyAddDelSession(argumentCaptor.capture());
+        final ClassifyAddDelSession actual = argumentCaptor.getValue();
+        assertEquals(expected.opaqueIndex, actual.opaqueIndex);
+        assertEquals(expected.isAdd, actual.isAdd);
+        assertEquals(expected.tableIndex, actual.tableIndex);
+    }
+
+    private static ClassifyAddDelSession generateClassifyAddDelSession(final byte isAdd, final int tableIndex,
+                                                                       final int sessionIndex) {
+        final ClassifyAddDelSession request = new ClassifyAddDelSession();
+        request.isAdd = isAdd;
+        request.tableIndex = tableIndex;
+        request.opaqueIndex = sessionIndex;
+        request.hitNextIndex = 0;
+        request.advance = 123;
+        request.match =
+            new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+                (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00};
+        return request;
+    }
+
+    @Test
+    public void testCreate() throws Exception {
+        final String match = "00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00";
+        final ClassifySession classifySession = generateClassifySession(SESSION_INDEX, match);
+        final InstanceIdentifier<ClassifySession> id = getClassifySessionId(TABLE_NAME, match);
+
+        whenClassifyAddDelSessionThenSuccess();
+
+        customizer.writeCurrentAttributes(id, classifySession, writeContext);
+
+        verifyClassifyAddDelSessionWasInvoked(generateClassifyAddDelSession((byte) 1, TABLE_INDEX, SESSION_INDEX));
+    }
+
+    @Test
+    public void testCreateFailed() throws Exception {
+        final String match = "00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00";
+        final ClassifySession classifySession = generateClassifySession(SESSION_INDEX, match);
+        final InstanceIdentifier<ClassifySession> id = getClassifySessionId(TABLE_NAME, match);
+
+        whenClassifyAddDelSessionThenFailure();
+
+        try {
+            customizer.writeCurrentAttributes(id, classifySession, writeContext);
+        } catch (WriteFailedException.CreateFailedException e) {
+            assertTrue(e.getCause() instanceof VppBaseCallException);
+            verifyClassifyAddDelSessionWasInvoked(generateClassifyAddDelSession((byte) 1, TABLE_INDEX, SESSION_INDEX));
+            return;
+        }
+        fail("WriteFailedException.CreateFailedException was expected");
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testUpdate() throws Exception {
+        customizer.updateCurrentAttributes(null, null, null, writeContext);
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        final String match = "00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00";
+        final ClassifySession classifySession = generateClassifySession(SESSION_INDEX, match);
+        final InstanceIdentifier<ClassifySession> id = getClassifySessionId(TABLE_NAME, match);
+
+        whenClassifyAddDelSessionThenSuccess();
+
+        customizer.deleteCurrentAttributes(id, classifySession, writeContext);
+
+        verifyClassifyAddDelSessionDeleteWasInvoked(
+            generateClassifyAddDelSession((byte) 0, TABLE_INDEX, SESSION_INDEX));
+    }
+
+    @Test
+    public void testDeleteFailed() throws Exception {
+        final String match = "00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00";
+        final ClassifySession classifySession = generateClassifySession(SESSION_INDEX, match);
+        final InstanceIdentifier<ClassifySession> id = getClassifySessionId(TABLE_NAME, match);
+
+        whenClassifyAddDelSessionThenFailure();
+
+        try {
+            customizer.deleteCurrentAttributes(id, classifySession, writeContext);
+        } catch (WriteFailedException.DeleteFailedException e) {
+            assertTrue(e.getCause() instanceof VppBaseCallException);
+            verifyClassifyAddDelSessionDeleteWasInvoked(
+                generateClassifyAddDelSession((byte) 0, TABLE_INDEX, SESSION_INDEX));
+            return;
+        }
+        fail("WriteFailedException.DeleteFailedException was expected");
+
+        customizer.deleteCurrentAttributes(id, classifySession, writeContext);
+    }
+}
\ No newline at end of file
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableReaderTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableReaderTest.java
new file mode 100644 (file)
index 0000000..14d6c55
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMapping;
+import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMappingIid;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.v3po.test.ListReaderCustomizerTest;
+import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.MappingsBuilder;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.PacketHandlingAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifierBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.openvpp.jvpp.dto.ClassifyTableIds;
+import org.openvpp.jvpp.dto.ClassifyTableIdsReply;
+import org.openvpp.jvpp.dto.ClassifyTableInfo;
+import org.openvpp.jvpp.dto.ClassifyTableInfoReply;
+
+public class ClassifyTableReaderTest extends
+    ListReaderCustomizerTest<ClassifyTable, ClassifyTableKey, ClassifyTableBuilder> {
+
+    private static final int TABLE_INDEX_1 = 1;
+    private static final String TABLE_NAME_1 = "table1";
+    private static final int TABLE_INDEX_2 = 2;
+    private static final String TABLE_NAME_2 = "table2";
+
+    private NamingContext classifyTableContext;
+
+    public ClassifyTableReaderTest() {
+        super(ClassifyTable.class);
+    }
+
+    @Override
+    public void setUpBefore() {
+        classifyTableContext = new NamingContext("classifyTableContext", "test-instance");
+
+        final KeyedInstanceIdentifier<Mapping, MappingKey> t0Id = getMappingIid(TABLE_NAME_1, "test-instance");
+        final KeyedInstanceIdentifier<Mapping, MappingKey> t1Id = getMappingIid(TABLE_NAME_2, "test-instance");
+        final Optional<Mapping> t0 = getMapping(TABLE_NAME_1, TABLE_INDEX_1);
+        final Optional<Mapping> t1 = getMapping(TABLE_NAME_2, TABLE_INDEX_2);
+        final List<Mapping> allMappings = Lists.newArrayList(t0.get(), t1.get());
+        final Mappings allMappingsBaObject = new MappingsBuilder().setMapping(allMappings).build();
+        doReturn(Optional.of(allMappingsBaObject)).when(mappingContext).read(t0Id.firstIdentifierOf(Mappings.class));
+        doReturn(t0).when(mappingContext).read(t0Id);
+        doReturn(t1).when(mappingContext).read(t1Id);
+    }
+
+    @Override
+    protected RootReaderCustomizer<ClassifyTable, ClassifyTableBuilder> initCustomizer() {
+        return new ClassifyTableReader(api, classifyTableContext);
+    }
+
+    private static InstanceIdentifier<ClassifyTable> getClassifyTableId(final String name) {
+        return InstanceIdentifier.create(VppClassifier.class)
+            .child(ClassifyTable.class, new ClassifyTableKey(name));
+    }
+
+    private static ClassifyTableInfoReply generateClassifyTableInfoReply() {
+        final ClassifyTableInfoReply reply = new ClassifyTableInfoReply();
+        reply.tableId = TABLE_INDEX_1;
+        reply.nbuckets = 2;
+        reply.skipNVectors = 0;
+        reply.matchNVectors = 1;
+        reply.nextTableIndex = ~0;
+        reply.missNextIndex = ~0;
+        reply.mask =
+            new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+                (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00};
+        return reply;
+    }
+
+    private void verifyClasifyTableRead(final ClassifyTableBuilder builder) {
+        verify(builder).setName(TABLE_NAME_1);
+        verify(builder).setNbuckets(2L);
+        verify(builder, times(0)).setNextTable(anyString());
+        verify(builder).setMissNextIndex(new VppNode(PacketHandlingAction.Permit));
+        verify(builder).setMask(new HexString("00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00"));
+        verify(builder).setActiveSessions(0L);
+    }
+
+    @Test
+    public void testMerge() {
+        final VppClassifierBuilder builder = mock(VppClassifierBuilder.class);
+        final List<ClassifyTable> value = mock(List.class);
+        getCustomizer().merge(builder, value);
+        verify(builder).setClassifyTable(value);
+    }
+
+    @Test
+    public void testRead() throws ReadFailedException {
+        final CompletableFuture<ClassifyTableInfoReply> replyFuture = new CompletableFuture<>();
+        replyFuture.complete(generateClassifyTableInfoReply());
+        doReturn(replyFuture).when(api).classifyTableInfo(any(ClassifyTableInfo.class));
+
+        final ClassifyTableBuilder builder = mock(ClassifyTableBuilder.class);
+        getCustomizer().readCurrentAttributes(getClassifyTableId(TABLE_NAME_1), builder, ctx);
+
+        verifyClasifyTableRead(builder);
+    }
+
+    @Test
+    public void testGetAllIds() throws ReadFailedException {
+        final CompletableFuture<ClassifyTableIdsReply> replyFuture = new CompletableFuture<>();
+        final ClassifyTableIdsReply reply = new ClassifyTableIdsReply();
+        reply.ids = new int[] {1, 2};
+        replyFuture.complete(reply);
+        doReturn(replyFuture).when(api).classifyTableIds(any(ClassifyTableIds.class));
+
+        final List<ClassifyTableKey> allIds = getCustomizer().getAllIds(getClassifyTableId(TABLE_NAME_1), ctx);
+
+        assertEquals(reply.ids.length, allIds.size());
+        assertEquals(TABLE_NAME_1, allIds.get(0).getName());
+        assertEquals(TABLE_NAME_2, allIds.get(1).getName());
+    }
+}
\ No newline at end of file
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableWriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/vppclassifier/ClassifyTableWriterTest.java
new file mode 100644 (file)
index 0000000..683bd69
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.v3po.vppclassifier;
+
+import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMapping;
+import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMappingIid;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.v3po.test.TestHelperUtils;
+import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.PacketHandlingAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppClassifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.VppNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev150603.vpp.classifier.ClassifyTableKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppBaseCallException;
+import org.openvpp.jvpp.dto.ClassifyAddDelTable;
+import org.openvpp.jvpp.dto.ClassifyAddDelTableReply;
+import org.openvpp.jvpp.dto.L2InterfaceVlanTagRewriteReply;
+import org.openvpp.jvpp.future.FutureJVpp;
+
+public class ClassifyTableWriterTest {
+
+    private static final int TABLE_INDEX = 123;
+    private static final String TABLE_NAME = "table123";
+
+    @Mock
+    private FutureJVpp api;
+    @Mock
+    private WriteContext writeContext;
+    @Mock
+    private MappingContext mappingContext;
+
+    private NamingContext classifyTableContext;
+    private ClassifyTableWriter customizer;
+
+    @Before
+    public void setUp() throws Exception {
+        initMocks(this);
+        classifyTableContext = new NamingContext("generatedClassifyTableName", "test-instance");
+        doReturn(mappingContext).when(writeContext).getMappingContext();
+        customizer = new ClassifyTableWriter(api, classifyTableContext);
+    }
+
+    private static ClassifyTable generateClassifyTable(final String name) {
+        final ClassifyTableBuilder builder = new ClassifyTableBuilder();
+        builder.setName(name);
+        builder.setKey(new ClassifyTableKey(name));
+        builder.setSkipNVectors(0L);
+        builder.setNbuckets(2L);
+        builder.setMemorySize(2L << 20);
+        builder.setMissNextIndex(new VppNode(PacketHandlingAction.Permit));
+        builder.setMask(new HexString("00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00"));
+        return builder.build();
+    }
+
+    private static InstanceIdentifier<ClassifyTable> getClassifyTableId(final String name) {
+        return InstanceIdentifier.create(VppClassifier.class)
+            .child(ClassifyTable.class, new ClassifyTableKey(name));
+    }
+
+    private void whenClassifyAddDelTableThenSuccess() throws ExecutionException, InterruptedException {
+        final CompletableFuture<ClassifyAddDelTableReply> replyFuture = new CompletableFuture<>();
+        final ClassifyAddDelTableReply reply = new ClassifyAddDelTableReply();
+        reply.newTableIndex = TABLE_INDEX;
+        replyFuture.complete(reply);
+        doReturn(replyFuture).when(api).classifyAddDelTable(any(ClassifyAddDelTable.class));
+    }
+
+    private void whenClassifyAddDelTableThenFailure() throws ExecutionException, InterruptedException {
+        doReturn(TestHelperUtils.<L2InterfaceVlanTagRewriteReply>createFutureException()).when(api)
+            .classifyAddDelTable(any(ClassifyAddDelTable.class));
+    }
+
+    private void verifyClassifyAddDelTableAddWasInvoked(final ClassifyAddDelTable expected) {
+        ArgumentCaptor<ClassifyAddDelTable> argumentCaptor = ArgumentCaptor.forClass(ClassifyAddDelTable.class);
+        verify(api).classifyAddDelTable(argumentCaptor.capture());
+        final ClassifyAddDelTable actual = argumentCaptor.getValue();
+        assertEquals(expected.isAdd, actual.isAdd);
+        assertEquals(~0, actual.tableIndex);
+        assertEquals(expected.nbuckets, actual.nbuckets);
+        assertEquals(expected.memorySize, actual.memorySize);
+        assertEquals(expected.skipNVectors, actual.skipNVectors);
+        assertEquals(expected.matchNVectors, actual.matchNVectors);
+        assertEquals(expected.nextTableIndex, actual.nextTableIndex);
+        assertEquals(expected.missNextIndex, actual.missNextIndex);
+        assertArrayEquals(expected.mask, actual.mask);
+    }
+
+    private void verifyClassifyAddDelTableDeleteWasInvoked(final ClassifyAddDelTable expected) {
+        ArgumentCaptor<ClassifyAddDelTable> argumentCaptor = ArgumentCaptor.forClass(ClassifyAddDelTable.class);
+        verify(api).classifyAddDelTable(argumentCaptor.capture());
+        final ClassifyAddDelTable actual = argumentCaptor.getValue();
+        assertEquals(expected.isAdd, actual.isAdd);
+        assertEquals(expected.tableIndex, actual.tableIndex);
+    }
+
+    private static ClassifyAddDelTable generateClassifyAddDelTable(final byte isAdd, final int tableIndex) {
+        final ClassifyAddDelTable request = new ClassifyAddDelTable();
+        request.isAdd = isAdd;
+        request.tableIndex = tableIndex;
+        request.nbuckets = 2;
+        request.memorySize = 2 << 20;
+        request.skipNVectors = 0;
+        request.matchNVectors = 1;
+        request.nextTableIndex = ~0;
+        request.missNextIndex = ~0;
+        request.mask =
+            new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+                (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00};
+        return request;
+    }
+
+    @Test
+    public void testCreate() throws Exception {
+        final ClassifyTable classifyTable = generateClassifyTable(TABLE_NAME);
+        final InstanceIdentifier<ClassifyTable> id = getClassifyTableId(TABLE_NAME);
+
+        whenClassifyAddDelTableThenSuccess();
+
+        customizer.writeCurrentAttributes(id, classifyTable, writeContext);
+
+        verifyClassifyAddDelTableAddWasInvoked(generateClassifyAddDelTable((byte) 1, TABLE_INDEX));
+        verify(mappingContext)
+            .put(eq(getMappingIid(TABLE_NAME, "test-instance")), eq(getMapping(TABLE_NAME, TABLE_INDEX).get()));
+    }
+
+    @Test
+    public void testCreateFailed() throws Exception {
+        final ClassifyTable classifyTable = generateClassifyTable(TABLE_NAME);
+        final InstanceIdentifier<ClassifyTable> id = getClassifyTableId(TABLE_NAME);
+
+        whenClassifyAddDelTableThenFailure();
+
+        try {
+            customizer.writeCurrentAttributes(id, classifyTable, writeContext);
+        } catch (WriteFailedException.CreateFailedException e) {
+            assertTrue(e.getCause() instanceof VppBaseCallException);
+            verifyClassifyAddDelTableAddWasInvoked(generateClassifyAddDelTable((byte) 1, TABLE_INDEX));
+            verify(mappingContext, times(0)).put(
+                eq(getMappingIid(TABLE_NAME, "test-instance")),
+                eq(getMapping(TABLE_NAME, TABLE_INDEX).get()));
+            return;
+        }
+        fail("WriteFailedException.CreateFailedException was expected");
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        final ClassifyTable classifyTable = generateClassifyTable(TABLE_NAME);
+        final InstanceIdentifier<ClassifyTable> id = getClassifyTableId(TABLE_NAME);
+
+        final Optional<Mapping> ifcMapping = getMapping(TABLE_NAME, TABLE_INDEX);
+        doReturn(ifcMapping).when(mappingContext).read(any());
+
+        whenClassifyAddDelTableThenSuccess();
+
+        customizer.deleteCurrentAttributes(id, classifyTable, writeContext);
+
+        verifyClassifyAddDelTableDeleteWasInvoked(generateClassifyAddDelTable((byte) 0, TABLE_INDEX));
+    }
+
+    @Test
+    public void testDeleteFailed() throws Exception {
+        final ClassifyTable classifyTable = generateClassifyTable(TABLE_NAME);
+        final InstanceIdentifier<ClassifyTable> id = getClassifyTableId(TABLE_NAME);
+
+        final Optional<Mapping> ifcMapping = getMapping(TABLE_NAME, TABLE_INDEX);
+        doReturn(ifcMapping).when(mappingContext).read(any());
+
+        whenClassifyAddDelTableThenFailure();
+
+        try {
+            customizer.deleteCurrentAttributes(id, classifyTable, writeContext);
+        } catch (WriteFailedException.DeleteFailedException e) {
+            assertTrue(e.getCause() instanceof VppBaseCallException);
+            verifyClassifyAddDelTableDeleteWasInvoked(generateClassifyAddDelTable((byte) 0, TABLE_INDEX));
+            return;
+        }
+        fail("WriteFailedException.DeleteFailedException was expected");
+
+        customizer.deleteCurrentAttributes(id, classifyTable, writeContext);
+    }
+
+    @Test
+    public void testUpdate() throws Exception {
+        final ClassifyTable classifyTableBefore = generateClassifyTable(TABLE_NAME);
+        final InstanceIdentifier<ClassifyTable> id = getClassifyTableId(TABLE_NAME);
+        customizer.updateCurrentAttributes(id, classifyTableBefore, new ClassifyTableBuilder().build(), writeContext);
+
+        verifyZeroInteractions(api);
+    }
+}
\ No newline at end of file