HONEYCOMB-248 Enable in/out NAT feature read
authorMaros Marsalek <[email protected]>
Mon, 24 Oct 2016 14:14:37 +0000 (16:14 +0200)
committerMaros Marsalek <[email protected]>
Wed, 2 Nov 2016 09:22:39 +0000 (09:22 +0000)
Change-Id: I6fe57b955437d0b0024323bcbac268f0ed4799f6
Signed-off-by: Maros Marsalek <[email protected]>
nat/nat2vpp/pom.xml
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/AbstractInterfaceNatCustomizer.java [new file with mode: 0644]
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/IfcNatReaderFactory.java
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/InterfaceInboundNatCustomizer.java
nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/InterfaceOutboundNatCustomizer.java
nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/read/ifc/InterfaceInboundNatCustomizerTest.java [new file with mode: 0644]
nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/read/ifc/InterfaceOutboundNatCustomizerTest.java [new file with mode: 0644]

index ba8204d..ad81e06 100644 (file)
@@ -30,6 +30,7 @@
 
     <properties>
         <honeycomb.infra.version>1.16.12-SNAPSHOT</honeycomb.infra.version>
+        <honeycomb.vpp.common.version>1.16.12-SNAPSHOT</honeycomb.vpp.common.version>
     </properties>
 
     <dependencies>
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.fd.honeycomb.vpp</groupId>
+            <artifactId>vpp-translate-test</artifactId>
+            <version>${honeycomb.vpp.common.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
diff --git a/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/AbstractInterfaceNatCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/honeycomb/nat/read/ifc/AbstractInterfaceNatCustomizer.java
new file mode 100644 (file)
index 0000000..441218a
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.nat.read.ifc;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.translate.read.ReadContext;
+import io.fd.honeycomb.translate.read.ReadFailedException;
+import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetails;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetailsReplyDump;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+
+abstract class AbstractInterfaceNatCustomizer<C extends DataObject, B extends Builder<C>>
+        implements InitializingReaderCustomizer<C, B> {
+
+    private final DumpCacheManager<SnatInterfaceDetailsReplyDump, Void> dumpMgr;
+    private final NamingContext ifcContext;
+
+    AbstractInterfaceNatCustomizer(@Nonnull final DumpCacheManager<SnatInterfaceDetailsReplyDump, Void> dumpMgr,
+                                   @Nonnull final NamingContext ifcContext) {
+        this.dumpMgr = dumpMgr;
+        this.ifcContext = ifcContext;
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id,
+                                      @Nonnull final B builder,
+                                      @Nonnull final ReadContext ctx) throws ReadFailedException {
+        final String ifcName = id.firstKeyOf(Interface.class).getName();
+        getLog().debug("Reading NAT features on interface: {}", ifcName);
+        final int index = ifcContext.getIndex(ifcName, ctx.getMappingContext());
+
+        // Cache dump for each interface under the same key since this is all ifc dump
+        final Optional<SnatInterfaceDetailsReplyDump> dump =
+                dumpMgr.getDump(id, getClass().getName(), ctx.getModificationCache(), null);
+
+        // Find entries for current ifc and if is marked as inside set the builder to return presence container
+        dump.or(new SnatInterfaceDetailsReplyDump()).snatInterfaceDetails.stream()
+                .filter(snatIfcDetail -> snatIfcDetail.swIfIndex == index)
+                .filter(this::isExpectedNatType)
+                .findFirst()
+                .ifPresent(snatIfcDetail -> setBuilderPresence(builder));
+        // Not setting data, just marking the builder to propagate empty container to indicate presence
+    }
+
+    protected abstract Logger getLog();
+
+    abstract void setBuilderPresence(@Nonnull final B builder);
+
+    abstract boolean isExpectedNatType(final SnatInterfaceDetails snatInterfaceDetails);
+}
index 293a3df..761986f 100644 (file)
 package io.fd.honeycomb.nat.read.ifc;
 
 
-import io.fd.honeycomb.translate.impl.read.GenericReader;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import io.fd.honeycomb.translate.impl.read.GenericInitReader;
+import io.fd.honeycomb.translate.read.ReadFailedException;
 import io.fd.honeycomb.translate.read.ReaderFactory;
 import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor;
+import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetailsReplyDump;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDump;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
 import javax.annotation.Nonnull;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
@@ -42,14 +52,45 @@ public final class IfcNatReaderFactory implements ReaderFactory {
             IFC_ID.augmentation(NatInterfaceStateAugmentation.class);
     private static final InstanceIdentifier<Nat> NAT_AUG_CONTAINER_ID = NAT_AUG_ID.child(Nat.class);
 
+    private final DumpCacheManager<SnatInterfaceDetailsReplyDump, Void> snatIfcDumpMgr;
+    private final NamingContext ifcContext;
+
+    @Inject
+    public IfcNatReaderFactory(final FutureJVppSnatFacade jvppSnat,
+                               @Named("interface-context") final NamingContext ifcContext) {
+        this.snatIfcDumpMgr = new DumpCacheManager.DumpCacheManagerBuilder<SnatInterfaceDetailsReplyDump, Void>()
+                .withExecutor(new SnatInterfaceExecutor(jvppSnat))
+                .build();
+        this.ifcContext = ifcContext;
+    }
+
     @Override
     public void init(@Nonnull final ModifiableReaderRegistryBuilder registry) {
         registry.addStructuralReader(NAT_AUG_ID, NatInterfaceStateAugmentationBuilder.class);
         registry.addStructuralReader(NAT_AUG_CONTAINER_ID, NatBuilder.class);
 
-        registry.addAfter(
-                new GenericReader<>(NAT_AUG_CONTAINER_ID.child(Inbound.class), new InterfaceInboundNatCustomizer()), IFC_ID);
-        registry.addAfter(
-                new GenericReader<>(NAT_AUG_CONTAINER_ID.child(Outbound.class), new InterfaceOutboundNatCustomizer()), IFC_ID);
+        registry.addAfter(new GenericInitReader<>(NAT_AUG_CONTAINER_ID.child(Inbound.class),
+                        new InterfaceInboundNatCustomizer(snatIfcDumpMgr, ifcContext)), IFC_ID);
+        registry.addAfter(new GenericInitReader<>(NAT_AUG_CONTAINER_ID.child(Outbound.class),
+                        new InterfaceOutboundNatCustomizer(snatIfcDumpMgr, ifcContext)), IFC_ID);
+    }
+
+    private static final class SnatInterfaceExecutor implements
+            EntityDumpExecutor<SnatInterfaceDetailsReplyDump, Void>,
+            JvppReplyConsumer {
+
+        private final FutureJVppSnatFacade jvppSnat;
+
+        SnatInterfaceExecutor(final FutureJVppSnatFacade jvppSnat) {
+            this.jvppSnat = jvppSnat;
+        }
+
+        @Nonnull
+        @Override
+        public SnatInterfaceDetailsReplyDump executeDump(final InstanceIdentifier<?> identifier, final Void params)
+                throws ReadFailedException {
+            return getReplyForRead(
+                    jvppSnat.snatInterfaceDump(new SnatInterfaceDump()).toCompletableFuture(), identifier);
+        }
     }
 }
index 5be35ce..52467a1 100644 (file)
 package io.fd.honeycomb.nat.read.ifc;
 
 import io.fd.honeycomb.translate.read.ReadContext;
-import io.fd.honeycomb.translate.read.ReadFailedException;
-import io.fd.honeycomb.translate.spi.read.ReaderCustomizer;
+import io.fd.honeycomb.translate.spi.read.Initialized;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetails;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetailsReplyDump;
 import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214.NatInterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.Nat;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.NatBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Inbound;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.InboundBuilder;
 import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class InterfaceInboundNatCustomizer implements ReaderCustomizer<Inbound, InboundBuilder> {
+final class InterfaceInboundNatCustomizer extends AbstractInterfaceNatCustomizer<Inbound, InboundBuilder> {
 
     private static final Logger LOG = LoggerFactory.getLogger(InterfaceInboundNatCustomizer.class);
 
-    @Nonnull
+    InterfaceInboundNatCustomizer(
+            @Nonnull final DumpCacheManager<SnatInterfaceDetailsReplyDump, Void> dumpMgr,
+            @Nonnull final NamingContext ifcContext) {
+        super(dumpMgr, ifcContext);
+    }
+
     @Override
-    public InboundBuilder getBuilder(@Nonnull final InstanceIdentifier<Inbound> id) {
-        return new InboundBuilder();
+    protected Logger getLog() {
+        return LOG;
     }
 
     @Override
-    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Inbound> id,
-                                      @Nonnull final InboundBuilder builder,
-                                      @Nonnull final ReadContext ctx)
-            throws ReadFailedException {
-        // FIXME HONEYCOMB-248 VPP-459 Implement when read is available in VPP/Snat
-        LOG.debug("Unable to read Inbound NAT feature state for an interface");
+    void setBuilderPresence(@Nonnull final InboundBuilder builder) {
+        ((PresenceInboundBuilder) builder).setPresent(true);
+    }
+
+    @Override
+    boolean isExpectedNatType(final SnatInterfaceDetails snatInterfaceDetails) {
+        return snatInterfaceDetails.isInside == 1;
+    }
+
+    @Nonnull
+    @Override
+    public InboundBuilder getBuilder(@Nonnull final InstanceIdentifier<Inbound> id) {
+        // Return not present value by default
+        return new PresenceInboundBuilder(false);
     }
 
     @Override
     public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Inbound readValue) {
         ((NatBuilder) parentBuilder).setInbound(readValue);
     }
+
+    @Nonnull
+    @Override
+    public Initialized<? extends DataObject> init(@Nonnull final InstanceIdentifier<Inbound> id,
+                                                  @Nonnull final Inbound readValue,
+                                                  @Nonnull final ReadContext ctx) {
+        final InstanceIdentifier<Inbound> cfgId =
+                InstanceIdentifier.create(Interfaces.class)
+                .child(Interface.class,
+                        new InterfaceKey(id.firstKeyOf(
+                        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.class).getName()))
+                .augmentation(NatInterfaceAugmentation.class)
+                .child(Nat.class)
+                .child(Inbound.class);
+        return Initialized.create(cfgId, readValue);
+    }
+
+    // TODO HONEYCOMB-270, make this better, having to fake a builder + value is just exploitation.
+
+    /**
+     * Special Builder to also propagate empty container into the resulting data.
+     */
+    private static final class PresenceInboundBuilder extends InboundBuilder {
+
+        private volatile boolean isPresent = false;
+
+        PresenceInboundBuilder(final boolean isPresent) {
+            this.isPresent = isPresent;
+        }
+
+        void setPresent(final boolean present) {
+            this.isPresent = present;
+        }
+
+        @Override
+        public Inbound build() {
+            return isPresent
+                    ? super.build()
+                    : NotPresentInbound.NOT_PRESENT_INBOUND;
+        }
+    }
+
+    /**
+     * Fake container that returns false on equals.
+     */
+    private static final class NotPresentInbound implements Inbound {
+
+        private static final NotPresentInbound NOT_PRESENT_INBOUND = new NotPresentInbound();
+
+        @Override
+        public <E extends Augmentation<Inbound>> E getAugmentation(final Class<E> augmentationType) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Class<? extends DataContainer> getImplementedInterface() {
+            return Inbound.class;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            // This is necessary to fake this.equals(something)
+            return obj == NOT_PRESENT_INBOUND;
+        }
+    }
 }
index cb01031..b08a636 100644 (file)
 package io.fd.honeycomb.nat.read.ifc;
 
 import io.fd.honeycomb.translate.read.ReadContext;
-import io.fd.honeycomb.translate.read.ReadFailedException;
-import io.fd.honeycomb.translate.spi.read.ReaderCustomizer;
+import io.fd.honeycomb.translate.spi.read.Initialized;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetails;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetailsReplyDump;
 import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214.NatInterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.Nat;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.NatBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Outbound;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.OutboundBuilder;
 import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class InterfaceOutboundNatCustomizer implements ReaderCustomizer<Outbound, OutboundBuilder> {
+final class InterfaceOutboundNatCustomizer extends AbstractInterfaceNatCustomizer<Outbound, OutboundBuilder> {
 
     private static final Logger LOG = LoggerFactory.getLogger(InterfaceOutboundNatCustomizer.class);
 
-    @Nonnull
+    InterfaceOutboundNatCustomizer(
+            @Nonnull final DumpCacheManager<SnatInterfaceDetailsReplyDump, Void> dumpMgr,
+            @Nonnull final NamingContext ifcContext) {
+        super(dumpMgr, ifcContext);
+    }
+
     @Override
-    public OutboundBuilder getBuilder(@Nonnull final InstanceIdentifier<Outbound> id) {
-        return new OutboundBuilder();
+    protected Logger getLog() {
+        return LOG;
     }
 
     @Override
-    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Outbound> id,
-                                      @Nonnull final OutboundBuilder builder, @Nonnull final ReadContext ctx)
-            throws ReadFailedException {
-        // FIXME HONEYCOMB-248 VPP-459 Implement when read is available in VPP/Snat
-        LOG.debug("Unable to read Outbound NAT feature state for an interface");
+    void setBuilderPresence(@Nonnull final OutboundBuilder builder) {
+        ((PresenceOutboundBuilder) builder).setPresent(true);
+    }
+
+    @Override
+    boolean isExpectedNatType(final SnatInterfaceDetails snatInterfaceDetails) {
+        return snatInterfaceDetails.isInside == 0;
+    }
+
+    @Nonnull
+    @Override
+    public OutboundBuilder getBuilder(@Nonnull final InstanceIdentifier<Outbound> id) {
+        return new PresenceOutboundBuilder(false);
     }
 
     @Override
     public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Outbound readValue) {
         ((NatBuilder) parentBuilder).setOutbound(readValue);
     }
+
+    @Nonnull
+    @Override
+    public Initialized<? extends DataObject> init(@Nonnull final InstanceIdentifier<Outbound> id,
+                                                  @Nonnull final Outbound readValue,
+                                                  @Nonnull final ReadContext ctx) {
+        final InstanceIdentifier<Outbound> cfgId =
+                InstanceIdentifier.create(Interfaces.class)
+                        .child(Interface.class,
+                                new InterfaceKey(id.firstKeyOf(
+                                org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.class).getName()))
+                        .augmentation(NatInterfaceAugmentation.class)
+                        .child(Nat.class)
+                        .child(Outbound.class);
+        return Initialized.create(cfgId, readValue);
+    }
+
+    // TODO HONEYCOMB-270, make this better, having to fake a builder + value is just exploitation.
+
+    /**
+     * Special Builder to also propagate empty container into the resulting data.
+     */
+    private static final class PresenceOutboundBuilder extends OutboundBuilder {
+
+        private volatile boolean isPresent = false;
+
+        PresenceOutboundBuilder(final boolean isPresent) {
+            this.isPresent = isPresent;
+        }
+
+        void setPresent(final boolean present) {
+            this.isPresent = present;
+        }
+
+        @Override
+        public Outbound build() {
+            final Outbound build = super.build();
+            return isPresent
+                    ? build
+                    : NotPresentOutbound.NOT_PRESENT_OUTBOUND;
+        }
+    }
+
+    /**
+     * Fake container that returns false on equals.
+     */
+    private static final class NotPresentOutbound implements Outbound {
+
+        private static final NotPresentOutbound NOT_PRESENT_OUTBOUND = new NotPresentOutbound();
+
+        @Override
+        public <E extends Augmentation<Outbound>> E getAugmentation(final Class<E> augmentationType) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Class<? extends DataContainer> getImplementedInterface() {
+            return Outbound.class;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            // This is necessary to fake this.equals(something)
+            return obj == NOT_PRESENT_OUTBOUND;
+        }
+    }
 }
diff --git a/nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/read/ifc/InterfaceInboundNatCustomizerTest.java b/nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/read/ifc/InterfaceInboundNatCustomizerTest.java
new file mode 100644 (file)
index 0000000..140d35f
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.nat.read.ifc;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.translate.impl.read.GenericReader;
+import io.fd.honeycomb.translate.spi.read.ReaderCustomizer;
+import io.fd.honeycomb.translate.util.RWUtils;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.honeycomb.vpp.test.read.ReaderCustomizerTest;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetails;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetailsReplyDump;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214.NatInterfaceStateAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.Nat;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.NatBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Inbound;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.InboundBuilder;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class InterfaceInboundNatCustomizerTest
+        extends ReaderCustomizerTest<Inbound, InboundBuilder> {
+
+    private static final String IFC_NAME = "a";
+    private static final int IFC_IDX = 0;
+    private static final String CTX_NAME = "ifc";
+
+    @Mock
+    private EntityDumpExecutor<SnatInterfaceDetailsReplyDump, Void> abc;
+    private DumpCacheManager<SnatInterfaceDetailsReplyDump, Void> dumpMgr;
+    private NamingContext ifcContext = new NamingContext(CTX_NAME, CTX_NAME);
+    private InstanceIdentifier<Inbound> id;
+
+    public InterfaceInboundNatCustomizerTest() {
+        super(Inbound.class, NatBuilder.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        id = getId(Inbound.class);
+        defineMapping(mappingContext, IFC_NAME, IFC_IDX, CTX_NAME);
+        // empty dump
+        Mockito.doReturn(new SnatInterfaceDetailsReplyDump()).when(abc).executeDump(id, null);
+        dumpMgr = new DumpCacheManager.DumpCacheManagerBuilder<SnatInterfaceDetailsReplyDump, Void>()
+                .withExecutor(abc)
+                .build();
+    }
+
+    static <T extends ChildOf<Nat>> InstanceIdentifier<T> getId(Class<T> boundType) {
+        return InstanceIdentifier.create(InterfacesState.class)
+                .child(Interface.class, new InterfaceKey(IFC_NAME))
+                .augmentation(NatInterfaceStateAugmentation.class)
+                .child(Nat.class)
+                .child(boundType);
+    }
+
+    @Test
+    public void testNoPresence() throws Exception {
+        assertFalse(getReader().read(id, ctx).isPresent());
+    }
+
+    private GenericReader<Inbound, InboundBuilder> getReader() {
+        return new GenericReader<>(RWUtils.makeIidWildcarded(id), customizer);
+    }
+
+    @Test
+    public void testPresence() throws Exception {
+        final SnatInterfaceDetailsReplyDump details = new SnatInterfaceDetailsReplyDump();
+        final SnatInterfaceDetails detail = new SnatInterfaceDetails();
+        detail.isInside = 1;
+        detail.swIfIndex = IFC_IDX;
+        details.snatInterfaceDetails = Lists.newArrayList(detail);
+        Mockito.doReturn(details).when(abc).executeDump(id, null);
+
+        assertTrue(getReader().read(id, ctx).isPresent());
+    }
+
+    @Override
+    protected ReaderCustomizer<Inbound, InboundBuilder> initCustomizer() {
+        return new InterfaceInboundNatCustomizer(dumpMgr, ifcContext);
+    }
+}
\ No newline at end of file
diff --git a/nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/read/ifc/InterfaceOutboundNatCustomizerTest.java b/nat/nat2vpp/src/test/java/io/fd/honeycomb/nat/read/ifc/InterfaceOutboundNatCustomizerTest.java
new file mode 100644 (file)
index 0000000..c33e040
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.nat.read.ifc;
+
+import static io.fd.honeycomb.nat.read.ifc.InterfaceInboundNatCustomizerTest.getId;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.translate.impl.read.GenericReader;
+import io.fd.honeycomb.translate.spi.read.ReaderCustomizer;
+import io.fd.honeycomb.translate.util.RWUtils;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor;
+import io.fd.honeycomb.translate.vpp.util.NamingContext;
+import io.fd.honeycomb.vpp.test.read.ReaderCustomizerTest;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetails;
+import io.fd.vpp.jvpp.snat.dto.SnatInterfaceDetailsReplyDump;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.NatBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.Outbound;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.nat.rev161214._interface.nat.attributes.nat.OutboundBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class InterfaceOutboundNatCustomizerTest
+        extends ReaderCustomizerTest<Outbound, OutboundBuilder> {
+
+    private static final String IFC_NAME = "a";
+    private static final int IFC_IDX = 0;
+    private static final String CTX_NAME = "ifc";
+
+    @Mock
+    private EntityDumpExecutor<SnatInterfaceDetailsReplyDump, Void> abc;
+    private DumpCacheManager<SnatInterfaceDetailsReplyDump, Void> dumpMgr;
+    private NamingContext ifcContext = new NamingContext(CTX_NAME, CTX_NAME);
+    private InstanceIdentifier<Outbound> id;
+
+    public InterfaceOutboundNatCustomizerTest() {
+        super(Outbound.class, NatBuilder.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        id = getId(Outbound.class);
+        defineMapping(mappingContext, IFC_NAME, IFC_IDX, CTX_NAME);
+        // empty dump
+        Mockito.doReturn(new SnatInterfaceDetailsReplyDump()).when(abc).executeDump(id, null);
+        dumpMgr = new DumpCacheManager.DumpCacheManagerBuilder<SnatInterfaceDetailsReplyDump, Void>()
+                .withExecutor(abc)
+                .build();
+    }
+
+    @Test
+    public void testNoPresence() throws Exception {
+        assertFalse(getReader().read(id, ctx).isPresent());
+    }
+
+    private GenericReader<Outbound, OutboundBuilder> getReader() {
+        return new GenericReader<>(RWUtils.makeIidWildcarded(id), customizer);
+    }
+
+    @Test
+    public void testPresence() throws Exception {
+        final SnatInterfaceDetailsReplyDump details = new SnatInterfaceDetailsReplyDump();
+        final SnatInterfaceDetails detail = new SnatInterfaceDetails();
+        detail.isInside = 0;
+        detail.swIfIndex = IFC_IDX;
+        details.snatInterfaceDetails = Lists.newArrayList(detail);
+        Mockito.doReturn(details).when(abc).executeDump(id, null);
+
+        assertTrue(getReader().read(id, ctx).isPresent());
+    }
+
+    @Override
+    protected ReaderCustomizer<Outbound, OutboundBuilder> initCustomizer() {
+        return new InterfaceOutboundNatCustomizer(dumpMgr, ifcContext);
+    }
+}
\ No newline at end of file