HONEYCOMB-83: Add Gre Feature within HC 52/2052/8
authorHongjun Ni <hongjun.ni@intel.com>
Fri, 22 Jul 2016 11:20:18 +0000 (19:20 +0800)
committerHongjun Ni <hongjun.ni@intel.com>
Wed, 3 Aug 2016 11:31:16 +0000 (19:31 +0800)
PatchSet 7: refactor code
PatchSet 6: Refactor based on new code organization
PatchSet 5: consolidate review comments
PatchSet 4: refactor to support reader registry
PatchSet 3: rebuild
PatchSet 2: update outer-fib-id in v3po.yang.
PatchSet 1: Add GRE feature and support IPv6.

Change-Id: I1fc064b618f161eaef06395380949277e2f9f060
Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
v3po/api/src/main/yang/v3po.yang
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/initializers/InterfacesInitializer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/GreCustomizer.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfacesstate/GreCustomizer.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfacesstate/InterfaceUtils.java
v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java
v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/GreCustomizerTest.java [new file with mode: 0644]
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfacesstate/GreCustomizerTest.java [new file with mode: 0644]

index 5d58218..409da30 100644 (file)
@@ -49,6 +49,10 @@ module v3po {
     base if:interface-type;
   }
 
+  identity gre-tunnel {
+    base if:interface-type;
+  }
+
   identity vhost-user {
     base if:interface-type;
   }
@@ -291,6 +295,20 @@ module v3po {
     }
   }
 
+  grouping gre-base-attributes {
+    leaf src {
+      /*mandatory true;*/
+      type inet:ip-address;
+    }
+    leaf dst {
+      /*mandatory true;*/
+      type inet:ip-address;
+    }
+    leaf outer-fib-id {
+      type uint32;
+    }
+  }
+
   grouping vxlan-gpe-base-attributes {
     leaf local {
       /*mandatory true;*/
@@ -423,6 +441,11 @@ module v3po {
       uses vxlan-base-attributes;
     }
 
+    container gre {
+      when "../if:type = 'v3po:gre-tunnel'";
+      uses gre-base-attributes;
+    }
+
     container l2 {
       must "(not (../if:ipv4[if:enabled = 'true']/if:address/if:ip) and " +
       "not (../if:ipv6[if:enabled = 'true']/if:address/if:ip))";
@@ -496,6 +519,11 @@ module v3po {
       uses vxlan-gpe-base-attributes;
     }
 
+    container gre {
+      when "../if:type = 'gre-tunnel'";
+      uses gre-base-attributes;
+    }
+
     container l2 {
       must "(not (../if:ipv4[if:enabled = 'true']/if:address/if:ip) and " +
       "not (../if:ipv6[if:enabled = 'true']/if:address/if:ip))";
index 1d58bd1..c218e54 100644 (file)
@@ -52,6 +52,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUserBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VxlanBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VxlanGpeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.GreBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Acl;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Ethernet;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.L2;
@@ -59,6 +60,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VhostUser;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Vxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VxlanGpe;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Gre;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.Interconnection;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.interconnection.BridgeBased;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.interconnection.BridgeBasedBuilder;
@@ -128,6 +130,11 @@ public class InterfacesInitializer extends AbstractDataTreeConverter<InterfacesS
                 setVxlanGpe(augmentBuilder, vxlanGpe);
             }
 
+            final Gre gre = vppIfcAugmentation.getGre();
+            if (gre != null) {
+                setGre(augmentBuilder, gre);
+            }
+
             final Tap tap = vppIfcAugmentation.getTap();
             if (tap != null) {
                 setTap(input, augmentBuilder, tap);
@@ -279,4 +286,12 @@ public class InterfacesInitializer extends AbstractDataTreeConverter<InterfacesS
         vxlanGpeBuilder.setDecapVrfId(vxlanGpe.getDecapVrfId());
         augmentBuilder.setVxlanGpe(vxlanGpeBuilder.build());
     }
+
+    private static void setGre(final VppInterfaceAugmentationBuilder augmentBuilder, final Gre gre) {
+        final GreBuilder greBuilder = new GreBuilder();
+        greBuilder.setDst(gre.getDst());
+        greBuilder.setSrc(gre.getSrc());
+        greBuilder.setOuterFibId(gre.getOuterFibId());
+        augmentBuilder.setGre(greBuilder.build());
+    }
 }
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/GreCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/GreCustomizer.java
new file mode 100644 (file)
index 0000000..673eef9
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2016 Intel 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.translate.v3po.interfaces;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.net.InetAddresses;
+import io.fd.honeycomb.translate.v3po.util.AbstractInterfaceTypeCustomizer;
+import io.fd.honeycomb.translate.v3po.util.NamingContext;
+import io.fd.honeycomb.translate.v3po.util.TranslateUtils;
+import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import java.net.InetAddress;
+import java.util.concurrent.CompletionStage;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.GreTunnel;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Gre;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppBaseCallException;
+import org.openvpp.jvpp.dto.GreAddDelTunnel;
+import org.openvpp.jvpp.dto.GreAddDelTunnelReply;
+import org.openvpp.jvpp.future.FutureJVpp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GreCustomizer extends AbstractInterfaceTypeCustomizer<Gre> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(GreCustomizer.class);
+    private final NamingContext interfaceContext;
+
+    public GreCustomizer(final FutureJVpp vppApi, final NamingContext interfaceContext) {
+        super(vppApi);
+        this.interfaceContext = interfaceContext;
+    }
+
+    @Override
+    protected Class<? extends InterfaceType> getExpectedInterfaceType() {
+        return GreTunnel.class;
+    }
+
+    @Override
+    protected final void writeInterface(@Nonnull final InstanceIdentifier<Gre> id, @Nonnull final Gre dataAfter,
+                                       @Nonnull final WriteContext writeContext)
+            throws WriteFailedException {
+        final String swIfName = id.firstKeyOf(Interface.class).getName();
+        try {
+            createGreTunnel(id, swIfName, dataAfter, writeContext);
+        } catch (VppBaseCallException | IllegalInterfaceTypeException e) {
+            LOG.warn("Failed to set gre tunnel for interface: {}, gre: {}", swIfName, dataAfter, e);
+            throw new WriteFailedException.CreateFailedException(id, dataAfter, e);
+        }
+    }
+
+    @Override
+    public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Gre> id, @Nonnull final Gre dataBefore,
+                                        @Nonnull final Gre dataAfter, @Nonnull final WriteContext writeContext)
+            throws WriteFailedException.UpdateFailedException {
+        throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter,
+                new UnsupportedOperationException("Gre tunnel update is not supported"));
+    }
+
+    @Override
+    public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Gre> id, @Nonnull final Gre dataBefore,
+                                        @Nonnull final WriteContext writeContext)
+            throws WriteFailedException {
+        final String swIfName = id.firstKeyOf(Interface.class).getName();
+        try {
+            deleteGreTunnel(id, swIfName, dataBefore, writeContext);
+        } catch (VppBaseCallException e) {
+            LOG.debug("Failed to delete gre tunnel for interface: {}, gre: {}", swIfName, dataBefore);
+            throw new WriteFailedException.DeleteFailedException(id, e);
+        }
+    }
+
+    private void createGreTunnel(final InstanceIdentifier<Gre> id, final String swIfName, final Gre gre,
+                                   final WriteContext writeContext) throws VppBaseCallException, WriteTimeoutException {
+        final byte isIpv6 = (byte) (isIpv6(gre) ? 1 : 0);
+        final InetAddress srcAddress = InetAddresses.forString(getAddressString(gre.getSrc()));
+        final InetAddress dstAddress = InetAddresses.forString(getAddressString(gre.getDst()));
+
+        int outerFibId = gre.getOuterFibId().intValue();
+
+        LOG.debug("Setting gre tunnel for interface: {}. Gre: {}", swIfName, gre);
+        final CompletionStage<GreAddDelTunnelReply> greAddDelTunnelReplyCompletionStage =
+                getFutureJVpp().greAddDelTunnel(getGreTunnelRequest((byte) 1 /* is add */, srcAddress.getAddress(),
+                        dstAddress.getAddress(), outerFibId, isIpv6));
+
+        final GreAddDelTunnelReply reply =
+                TranslateUtils.getReplyForWrite(greAddDelTunnelReplyCompletionStage.toCompletableFuture(), id);
+        LOG.debug("Gre tunnel set successfully for: {}, gre: {}", swIfName, gre);
+        if(interfaceContext.containsName(reply.swIfIndex, writeContext.getMappingContext())) {
+            // VPP keeps gre tunnels present even after they are delete(reserving ID for next tunnel)
+            // This may cause inconsistencies in mapping context when configuring tunnels like this:
+            // 1. Add tunnel 2. Delete tunnel 3. Read interfaces (reserved mapping e.g. gre_tunnel0 -> 6
+            // will get into mapping context) 4. Add tunnel (this will add another mapping with the same
+            // reserved ID and context is invalid)
+            // That's why a check has to be performed here removing mapping gre_tunnel0 -> 6 mapping and storing
+            // new name for that ID
+            final String formerName = interfaceContext.getName(reply.swIfIndex, writeContext.getMappingContext());
+            LOG.debug("Removing updated mapping of a gre tunnel, id: {}, former name: {}, new name: {}",
+                reply.swIfIndex, formerName, swIfName);
+            interfaceContext.removeName(formerName, writeContext.getMappingContext());
+        }
+        // Add new interface to our interface context
+        interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext());
+    }
+
+    private boolean isIpv6(final Gre gre) {
+        if (gre.getSrc().getIpv4Address() == null) {
+            checkArgument(gre.getDst().getIpv4Address() == null, "Inconsistent ip addresses: %s, %s", gre.getSrc(),
+                gre.getDst());
+            return true;
+        } else {
+            checkArgument(gre.getDst().getIpv6Address() == null, "Inconsistent ip addresses: %s, %s", gre.getSrc(),
+                gre.getDst());
+            return false;
+        }
+    }
+
+    private String getAddressString(final IpAddress addr) {
+        return addr.getIpv4Address() == null ? addr.getIpv6Address().getValue() : addr.getIpv4Address().getValue();
+    }
+
+    private void deleteGreTunnel(final InstanceIdentifier<Gre> id, final String swIfName, final Gre gre,
+                                   final WriteContext writeContext) throws VppBaseCallException, WriteTimeoutException {
+        final byte isIpv6 = (byte) (isIpv6(gre) ? 1 : 0);
+        final InetAddress srcAddress = InetAddresses.forString(getAddressString(gre.getSrc()));
+        final InetAddress dstAddress = InetAddresses.forString(getAddressString(gre.getDst()));
+
+        int outerFibId = gre.getOuterFibId().intValue();
+
+        LOG.debug("Deleting gre tunnel for interface: {}. Gre: {}", swIfName, gre);
+        final CompletionStage<GreAddDelTunnelReply> greAddDelTunnelReplyCompletionStage =
+                getFutureJVpp().greAddDelTunnel(getGreTunnelRequest((byte) 0 /* is add */, srcAddress.getAddress(),
+                        dstAddress.getAddress(), outerFibId, isIpv6));
+
+        TranslateUtils.getReplyForWrite(greAddDelTunnelReplyCompletionStage.toCompletableFuture(), id);
+        LOG.debug("Gre tunnel deleted successfully for: {}, gre: {}", swIfName, gre);
+        // Remove interface from our interface context
+        interfaceContext.removeName(swIfName, writeContext.getMappingContext());
+    }
+
+    private static GreAddDelTunnel getGreTunnelRequest(final byte isAdd, final byte[] srcAddr, final byte[] dstAddr,
+                                                    final int outerFibId, final byte isIpv6) {
+        final GreAddDelTunnel greAddDelTunnel = new GreAddDelTunnel();
+        greAddDelTunnel.isAdd = isAdd;
+        greAddDelTunnel.srcAddress = srcAddr;
+        greAddDelTunnel.dstAddress = dstAddr;
+        greAddDelTunnel.outerFibId = outerFibId;
+        greAddDelTunnel.isIpv6 = isIpv6;
+        return greAddDelTunnel;
+    }
+}
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfacesstate/GreCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfacesstate/GreCustomizer.java
new file mode 100644 (file)
index 0000000..a17a1f1
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016 Intel 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.translate.v3po.interfacesstate;
+
+import static com.google.common.base.Preconditions.checkState;
+import static io.fd.honeycomb.translate.v3po.interfacesstate.InterfaceUtils.isInterfaceOfType;
+
+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.v3po.util.FutureJVppCustomizer;
+import io.fd.honeycomb.translate.v3po.util.NamingContext;
+import io.fd.honeycomb.translate.v3po.util.TranslateUtils;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.concurrent.CompletionStage;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+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.v3po.rev150105.VppInterfaceStateAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.GreTunnel;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Gre;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.GreBuilder;
+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.GreTunnelDetails;
+import org.openvpp.jvpp.dto.GreTunnelDetailsReplyDump;
+import org.openvpp.jvpp.dto.GreTunnelDump;
+import org.openvpp.jvpp.future.FutureJVpp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GreCustomizer extends FutureJVppCustomizer
+        implements ReaderCustomizer<Gre, GreBuilder> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(GreCustomizer.class);
+    private NamingContext interfaceContext;
+
+    public GreCustomizer(@Nonnull final FutureJVpp jvpp, @Nonnull final NamingContext interfaceContext) {
+        super(jvpp);
+        this.interfaceContext = interfaceContext;
+    }
+
+    @Override
+    public void merge(@Nonnull Builder<? extends DataObject> parentBuilder,
+                      @Nonnull Gre readValue) {
+        ((VppInterfaceStateAugmentationBuilder) parentBuilder).setGre(readValue);
+    }
+
+    @Nonnull
+    @Override
+    public GreBuilder getBuilder(@Nonnull InstanceIdentifier<Gre> id) {
+        return new GreBuilder();
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<Gre> id,
+                                      @Nonnull final GreBuilder builder,
+                                      @Nonnull final ReadContext ctx) throws ReadFailedException {
+        try {
+            final InterfaceKey key = id.firstKeyOf(Interface.class);
+            final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext());
+            if (!isInterfaceOfType(getFutureJVpp(), ctx.getModificationCache(), id, index, GreTunnel.class)) {
+                return;
+            }
+
+            LOG.debug("Reading attributes for gre tunnel: {}", key.getName());
+            // Dump just a single
+            final GreTunnelDump request = new GreTunnelDump();
+            request.swIfIndex = index;
+
+            final CompletionStage<GreTunnelDetailsReplyDump> swInterfaceGreDetailsReplyDumpCompletionStage =
+                getFutureJVpp().greTunnelDump(request);
+            final GreTunnelDetailsReplyDump reply =
+                TranslateUtils.getReplyForRead(swInterfaceGreDetailsReplyDumpCompletionStage.toCompletableFuture(), id);
+
+            // VPP keeps gre tunnel interfaces even after they were deleted (optimization)
+            // However there ar no longer any gre tunnel specific fields assigned to it and this call
+            // returns nothing
+            if (reply == null || reply.greTunnelDetails == null || reply.greTunnelDetails.isEmpty()) {
+                LOG.debug(
+                    "Gre tunnel {}, id {} has no attributes assigned in VPP. Probably is a leftover interface placeholder" +
+                        "after delete", key.getName(), index);
+                return;
+            }
+
+            checkState(reply.greTunnelDetails.size() == 1,
+                "Unexpected number of returned gre tunnels: {} for tunnel: {}", reply.greTunnelDetails, key.getName());
+            LOG.trace("Gre tunnel: {} attributes returned from VPP: {}", key.getName(), reply);
+
+            final GreTunnelDetails swInterfaceGreDetails = reply.greTunnelDetails.get(0);
+            if (swInterfaceGreDetails.isIpv6 == 1) {
+                final Ipv6Address dstIpv6 =
+                    new Ipv6Address(parseAddress(swInterfaceGreDetails.dstAddress).getHostAddress());
+                builder.setDst(new IpAddress(dstIpv6));
+                final Ipv6Address srcIpv6 =
+                    new Ipv6Address(parseAddress(swInterfaceGreDetails.srcAddress).getHostAddress());
+                builder.setSrc(new IpAddress(srcIpv6));
+            } else {
+                final byte[] dstBytes = Arrays.copyOfRange(swInterfaceGreDetails.dstAddress, 0, 4);
+                final Ipv4Address dstIpv4 = new Ipv4Address(parseAddress(dstBytes).getHostAddress());
+                builder.setDst(new IpAddress(dstIpv4));
+                final byte[] srcBytes = Arrays.copyOfRange(swInterfaceGreDetails.srcAddress, 0, 4);
+                final Ipv4Address srcIpv4 = new Ipv4Address(parseAddress(srcBytes).getHostAddress());
+                builder.setSrc(new IpAddress(srcIpv4));
+            }
+            builder.setOuterFibId((long) swInterfaceGreDetails.outerFibId);
+            LOG.debug("Gre tunnel: {}, id: {} attributes read as: {}", key.getName(), index, builder);
+        } catch (VppBaseCallException e) {
+            LOG.warn("Failed to readCurrentAttributes for: {}", id, e);
+            throw new ReadFailedException( id, e );
+        }
+    }
+
+    @Nonnull
+    private static InetAddress parseAddress(@Nonnull final byte[] addr) {
+        try {
+            return InetAddress.getByAddress(addr);
+        } catch (UnknownHostException e) {
+            throw new IllegalArgumentException("Cannot create InetAddress from " + Arrays.toString(addr), e);
+        }
+    }
+}
index b7c1e51..e47fc05 100644 (file)
@@ -38,6 +38,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUser;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanGpeTunnel;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.GreTunnel;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.openvpp.jvpp.VppBaseCallException;
 import org.openvpp.jvpp.dto.SwInterfaceDetails;
@@ -255,6 +256,10 @@ public final class InterfaceUtils {
             return VxlanTunnel.class;
         }
 
+        if (interfaceName.startsWith("gre")) {
+            return GreTunnel.class;
+        }
+
         if (interfaceName.startsWith("VirtualEthernet")) {
             return VhostUser.class;
         }
index c3e56ef..d0a8683 100644 (file)
@@ -8,6 +8,7 @@ import io.fd.honeycomb.translate.impl.write.GenericListWriter;
 import io.fd.honeycomb.translate.impl.write.GenericWriter;
 import io.fd.honeycomb.translate.v3po.interfaces.AclCustomizer;
 import io.fd.honeycomb.translate.v3po.interfaces.EthernetCustomizer;
+import io.fd.honeycomb.translate.v3po.interfaces.GreCustomizer;
 import io.fd.honeycomb.translate.v3po.interfaces.InterfaceCustomizer;
 import io.fd.honeycomb.translate.v3po.interfaces.L2Customizer;
 import io.fd.honeycomb.translate.v3po.interfaces.RoutingCustomizer;
@@ -42,6 +43,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUser;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Vxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VxlanGpe;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Gre;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.openvpp.jvpp.future.FutureJVpp;
 
@@ -151,6 +153,12 @@ public class InterfacesHoneycombWriterModule extends
             registry.addBefore(new GenericWriter<>(tapId, new TapCustomizer(jvpp, ifcContext)),
                     ifcId);
 
+            // Gre(Needs to be executed before Interface customizer) =
+            final InstanceIdentifier<Gre> greId = VPP_IFC_AUG_ID.child(Gre.class);
+            registry.addBefore(new GenericWriter<>(greId, new GreCustomizer(jvpp, ifcContext)),
+                ifcId);
+
+
             final Set<InstanceIdentifier<?>> specificIfcTypes = Sets.newHashSet(vhostId, vxlanGpeId, vxlanGpeId, tapId);
 
             // Ethernet(No dependency, customizer not finished TODO) =
index e2fd01c..5b4450a 100644 (file)
@@ -7,6 +7,7 @@ import io.fd.honeycomb.translate.read.ReaderFactory;
 import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder;
 import io.fd.honeycomb.translate.v3po.interfacesstate.AclCustomizer;
 import io.fd.honeycomb.translate.v3po.interfacesstate.EthernetCustomizer;
+import io.fd.honeycomb.translate.v3po.interfacesstate.GreCustomizer;
 import io.fd.honeycomb.translate.v3po.interfacesstate.InterfaceCustomizer;
 import io.fd.honeycomb.translate.v3po.interfacesstate.L2Customizer;
 import io.fd.honeycomb.translate.v3po.interfacesstate.TapCustomizer;
@@ -41,6 +42,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VxlanGpe;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.openvpp.jvpp.future.FutureJVpp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Gre;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
 
 public class InterfacesStateHoneycombReaderModule extends
         org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractInterfacesStateHoneycombReaderModule {
@@ -141,6 +145,8 @@ public class InterfacesStateHoneycombReaderModule extends
             registry.add(new GenericReader<>(vppIfcAugId.child(Vxlan.class), new VxlanCustomizer(jvpp, ifcCtx)));
             //    VxlanGpe
             registry.add(new GenericReader<>(vppIfcAugId.child(VxlanGpe.class), new VxlanGpeCustomizer(jvpp, ifcCtx)));
+            //    Gre
+            registry.add(new GenericReader<>(vppIfcAugId.child(Gre.class), new GreCustomizer(jvpp, ifcCtx)));
             //    L2
             registry.add(new GenericReader<>(vppIfcAugId.child(L2.class), new L2Customizer(jvpp, ifcCtx, bdCtx)));
             //    Acl(Subtree)
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/GreCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/GreCustomizerTest.java
new file mode 100644 (file)
index 0000000..cc4854d
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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.translate.v3po.interfaces;
+
+import static io.fd.honeycomb.translate.v3po.test.ContextTestUtils.getMapping;
+import static io.fd.honeycomb.translate.v3po.test.ContextTestUtils.getMappingIid;
+import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+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 static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import com.google.common.net.InetAddresses;
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.honeycomb.translate.ModificationCache;
+import io.fd.honeycomb.translate.v3po.test.TestHelperUtils;
+import io.fd.honeycomb.translate.v3po.util.NamingContext;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+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;
+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.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+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.v3po.rev150105.VppInterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Gre;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.GreBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppBaseCallException;
+import org.openvpp.jvpp.VppInvocationException;
+import org.openvpp.jvpp.dto.GreAddDelTunnel;
+import org.openvpp.jvpp.dto.GreAddDelTunnelReply;
+import org.openvpp.jvpp.future.FutureJVpp;
+
+public class GreCustomizerTest {
+
+    private static final byte ADD_GRE = 1;
+    private static final byte DEL_GRE = 0;
+
+    @Mock
+    private FutureJVpp api;
+    @Mock
+    private WriteContext writeContext;
+    @Mock
+    private MappingContext mappingContext;
+
+    private GreCustomizer customizer;
+    private String ifaceName;
+    private InstanceIdentifier<Gre> id;
+
+    @Before
+    public void setUp() throws Exception {
+        initMocks(this);
+        InterfaceTypeTestUtils.setupWriteContext(writeContext,
+            org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.GreTunnel.class);
+        // TODO create base class for tests using vppApi
+        NamingContext namingContext = new NamingContext("generateInterfaceNAme", "test-instance");
+        final ModificationCache toBeReturned = new ModificationCache();
+        doReturn(toBeReturned).when(writeContext).getModificationCache();
+        doReturn(mappingContext).when(writeContext).getMappingContext();
+
+        customizer = new GreCustomizer(api, namingContext);
+
+        ifaceName = "eth0";
+        id = InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(ifaceName))
+                        .augmentation(VppInterfaceAugmentation.class).child(Gre.class);
+    }
+
+    private void whenGreAddDelTunnelThenSuccess()
+        throws ExecutionException, InterruptedException, VppInvocationException, TimeoutException {
+        final CompletionStage<GreAddDelTunnelReply> replyCS = mock(CompletionStage.class);
+        final CompletableFuture<GreAddDelTunnelReply> replyFuture = mock(CompletableFuture.class);
+        when(replyCS.toCompletableFuture()).thenReturn(replyFuture);
+        final GreAddDelTunnelReply reply = new GreAddDelTunnelReply();
+        when(replyFuture.get(anyLong(), eq(TimeUnit.SECONDS))).thenReturn(reply);
+        when(api.greAddDelTunnel(any(GreAddDelTunnel.class))).thenReturn(replyCS);
+    }
+
+    /**
+     * Failure response send
+     */
+    private void whenGreAddDelTunnelThenFailure()
+            throws ExecutionException, InterruptedException, VppInvocationException {
+        doReturn(TestHelperUtils.<GreAddDelTunnelReply>createFutureException()).when(api)
+                .greAddDelTunnel(any(GreAddDelTunnel.class));
+    }
+
+    private GreAddDelTunnel verifyGreAddDelTunnelWasInvoked(final Gre gre) throws VppInvocationException {
+        ArgumentCaptor<GreAddDelTunnel> argumentCaptor = ArgumentCaptor.forClass(GreAddDelTunnel.class);
+        verify(api).greAddDelTunnel(argumentCaptor.capture());
+        final GreAddDelTunnel actual = argumentCaptor.getValue();
+        assertEquals(0, actual.isIpv6);
+        assertArrayEquals(InetAddresses.forString(gre.getSrc().getIpv4Address().getValue()).getAddress(),
+                actual.srcAddress);
+        assertArrayEquals(InetAddresses.forString(gre.getDst().getIpv4Address().getValue()).getAddress(),
+                actual.dstAddress);
+        assertEquals(gre.getOuterFibId().intValue(), actual.outerFibId);
+        return actual;
+    }
+
+    private void verifyGreAddWasInvoked(final Gre gre) throws VppInvocationException {
+        final GreAddDelTunnel actual = verifyGreAddDelTunnelWasInvoked(gre);
+        assertEquals(ADD_GRE, actual.isAdd);
+    }
+
+    private void verifyGreDeleteWasInvoked(final Gre gre) throws VppInvocationException {
+        final GreAddDelTunnel actual = verifyGreAddDelTunnelWasInvoked(gre);
+        assertEquals(DEL_GRE, actual.isAdd);
+    }
+
+    private static Gre generateGre(long vni) {
+        final GreBuilder builder = new GreBuilder();
+        builder.setSrc(new IpAddress(new Ipv4Address("192.168.20.10")));
+        builder.setDst(new IpAddress(new Ipv4Address("192.168.20.11")));
+        builder.setOuterFibId(Long.valueOf(123));
+        return builder.build();
+    }
+
+    private static Gre generateGre() {
+        return generateGre(Long.valueOf(11));
+    }
+
+    @Test
+    public void testWriteCurrentAttributes() throws Exception {
+        final Gre gre = generateGre();
+
+        whenGreAddDelTunnelThenSuccess();
+
+        doReturn(Optional.absent())
+            .when(mappingContext).read(getMappingIid(ifaceName, "test-instance").firstIdentifierOf(Mappings.class));
+
+        customizer.writeCurrentAttributes(id, gre, writeContext);
+        verifyGreAddWasInvoked(gre);
+        verify(mappingContext).put(eq(getMappingIid(ifaceName, "test-instance")), eq(getMapping(ifaceName, 0).get()));
+    }
+
+    @Test
+    public void testWriteCurrentAttributesMappingAlreadyPresent() throws Exception {
+        final Gre gre = generateGre();
+
+        whenGreAddDelTunnelThenSuccess();
+        final Optional<Mapping> ifcMapping = getMapping(ifaceName, 0);
+
+        doReturn(Optional.of(new MappingsBuilder().setMapping(singletonList(ifcMapping.get())).build()))
+            .when(mappingContext).read(getMappingIid(ifaceName, "test-instance").firstIdentifierOf(Mappings.class));
+
+        customizer.writeCurrentAttributes(id, gre, writeContext);
+        verifyGreAddWasInvoked(gre);
+
+        // Remove the first mapping before putting in the new one
+        verify(mappingContext).delete(eq(getMappingIid(ifaceName, "test-instance")));
+        verify(mappingContext).put(eq(getMappingIid(ifaceName, "test-instance")), eq(ifcMapping.get()));
+    }
+
+    @Test
+    public void testWriteCurrentAttributesFailed() throws Exception {
+        final Gre gre = generateGre();
+
+        whenGreAddDelTunnelThenFailure();
+
+        try {
+            customizer.writeCurrentAttributes(id, gre, writeContext);
+        } catch (WriteFailedException.CreateFailedException e) {
+            assertTrue(e.getCause() instanceof VppBaseCallException);
+            verifyGreAddWasInvoked(gre);
+            // Mapping not stored due to failure
+            verify(mappingContext, times(0)).put(eq(getMappingIid(ifaceName, "test-instance")), eq(getMapping(ifaceName, 0).get()));
+            return;
+        }
+        fail("WriteFailedException.CreateFailedException was expected");
+    }
+
+    @Test
+    public void testUpdateCurrentAttributes() throws Exception {
+        try {
+            customizer.updateCurrentAttributes(id, generateGre(10), generateGre(11), writeContext);
+        } catch (WriteFailedException.UpdateFailedException e) {
+            assertEquals(UnsupportedOperationException.class, e.getCause().getClass());
+            return;
+        }
+        fail("WriteFailedException.UpdateFailedException was expected");
+    }
+
+    @Test
+    public void testDeleteCurrentAttributes() throws Exception {
+        final Gre gre = generateGre();
+
+        whenGreAddDelTunnelThenSuccess();
+        doReturn(getMapping(ifaceName, 1)).when(mappingContext).read(getMappingIid(ifaceName, "test-instance"));
+
+        customizer.deleteCurrentAttributes(id, gre, writeContext);
+        verifyGreDeleteWasInvoked(gre);
+        verify(mappingContext).delete(eq(getMappingIid(ifaceName, "test-instance")));
+    }
+
+    @Test
+    public void testDeleteCurrentAttributesaFailed() throws Exception {
+        final Gre gre = generateGre();
+
+        whenGreAddDelTunnelThenFailure();
+        doReturn(getMapping(ifaceName, 1)).when(mappingContext).read(getMappingIid(ifaceName, "test-instance"));
+
+        try {
+            customizer.deleteCurrentAttributes(id, gre, writeContext);
+        } catch (WriteFailedException.DeleteFailedException e) {
+            assertTrue(e.getCause() instanceof VppBaseCallException);
+            verifyGreDeleteWasInvoked(gre);
+            verify(mappingContext, times(0)).delete(eq(getMappingIid(ifaceName, "test-instance")));
+            return;
+        }
+        fail("WriteFailedException.DeleteFailedException was expected");
+    }
+}
\ No newline at end of file
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfacesstate/GreCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfacesstate/GreCustomizerTest.java
new file mode 100644 (file)
index 0000000..8ac13e4
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.translate.v3po.interfacesstate;
+
+import static io.fd.honeycomb.translate.v3po.test.ContextTestUtils.getMapping;
+import static io.fd.honeycomb.translate.v3po.test.ContextTestUtils.getMappingIid;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.translate.spi.read.ReaderCustomizer;
+import io.fd.honeycomb.translate.v3po.test.ReaderCustomizerTest;
+import io.fd.honeycomb.translate.v3po.util.NamingContext;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import org.junit.Test;
+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.v3po.rev150105.VppInterfaceStateAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Gre;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.GreBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.openvpp.jvpp.VppInvocationException;
+import org.openvpp.jvpp.dto.SwInterfaceDetails;
+import org.openvpp.jvpp.dto.GreTunnelDetails;
+import org.openvpp.jvpp.dto.GreTunnelDetailsReplyDump;
+import org.openvpp.jvpp.dto.GreTunnelDump;
+
+public class GreCustomizerTest extends ReaderCustomizerTest<Gre, GreBuilder> {
+
+    private NamingContext interfacesContext;
+    static final InstanceIdentifier<Gre> IID =
+        InstanceIdentifier.create(InterfacesState.class).child(Interface.class, new InterfaceKey("ifc1"))
+            .augmentation(VppInterfaceStateAugmentation.class).child(Gre.class);
+
+    public GreCustomizerTest() {
+        super(Gre.class);
+    }
+
+    @Override
+    public void setUpBefore() {
+        interfacesContext = new NamingContext("gre-tunnel", "test-instance");
+        doReturn(getMapping("ifc1", 0)).when(mappingContext).read(getMappingIid("ifc1", "test-instance"));
+
+        final SwInterfaceDetails v = new SwInterfaceDetails();
+        v.interfaceName = "gre-tunnel4".getBytes();
+        final Map<Integer, SwInterfaceDetails> map = new HashMap<>();
+        map.put(0, v);
+        cache.put(InterfaceCustomizer.DUMPED_IFCS_CONTEXT_KEY, map);
+    }
+
+    @Override
+    protected void setUpAfter() throws UnknownHostException, VppInvocationException {
+        final CompletableFuture<GreTunnelDetailsReplyDump> greTunnelDetailsReplyDumpCompletionStage =
+            new CompletableFuture<>();
+
+        final GreTunnelDetailsReplyDump value = new GreTunnelDetailsReplyDump();
+        final GreTunnelDetails greTunnelDetails = new GreTunnelDetails();
+        greTunnelDetails.isIpv6 = 0;
+        greTunnelDetails.dstAddress = InetAddress.getByName("1.2.3.4").getAddress();
+        greTunnelDetails.srcAddress = InetAddress.getByName("1.2.3.5").getAddress();
+        greTunnelDetails.outerFibId = 55;
+        greTunnelDetails.swIfIndex = 0;
+
+        value.greTunnelDetails = Lists.newArrayList(greTunnelDetails);
+        greTunnelDetailsReplyDumpCompletionStage.complete(value);
+
+        doReturn(greTunnelDetailsReplyDumpCompletionStage).when(api).greTunnelDump(any(GreTunnelDump.class));
+    }
+
+    @Test
+    public void testReadCurrentAttributes() throws Exception {
+        final GreBuilder builder = getCustomizer().getBuilder(IID);
+        getCustomizer().readCurrentAttributes(IID, builder, ctx);
+
+        assertEquals(55, builder.getOuterFibId().intValue());
+
+        assertNull(builder.getSrc().getIpv6Address());
+        assertNotNull(builder.getSrc().getIpv4Address());
+        assertEquals("1.2.3.5", builder.getSrc().getIpv4Address().getValue());
+
+        assertNull(builder.getDst().getIpv6Address());
+        assertNotNull(builder.getDst().getIpv4Address());
+        assertEquals("1.2.3.4", builder.getDst().getIpv4Address().getValue());
+
+        verify(api).greTunnelDump(any(GreTunnelDump.class));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testReadCurrentAttributesVppNameNotCached() throws Exception {
+        InterfaceCustomizer.getCachedInterfaceDump(cache).remove(0);
+
+        final GreBuilder builder = getCustomizer().getBuilder(IID);
+        getCustomizer().readCurrentAttributes(IID, builder, ctx);
+    }
+
+    @Test
+    public void testReadCurrentAttributesWrongType() throws Exception {
+        final SwInterfaceDetails v = new SwInterfaceDetails();
+        v.interfaceName = "tap-2".getBytes();
+        InterfaceCustomizer.getCachedInterfaceDump(cache).put(0, v);
+
+        final GreBuilder builder = getCustomizer().getBuilder(IID);
+        getCustomizer().readCurrentAttributes(IID, builder, ctx);
+
+        // Should be ignored
+        verifyZeroInteractions(api);
+    }
+
+    @Override
+    protected ReaderCustomizer<Gre, GreBuilder> initCustomizer() {
+        return new GreCustomizer(api, interfacesContext);
+    }
+}
\ No newline at end of file