HC2VPP-390: Tap v2 interface implementation 23/15123/8
authorTibor Král <[email protected]>
Wed, 3 Oct 2018 15:23:08 +0000 (17:23 +0200)
committerMichal Cmarada <[email protected]>
Wed, 17 Oct 2018 16:11:19 +0000 (18:11 +0200)
Change-Id: I92fe8adabbb770a45ad47c56f5cd21262c4bd6e1
Signed-off-by: Tibor Král <[email protected]>
Signed-off-by: Michal Cmarada <[email protected]>
v3po/postman_rest_collection.json
v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesStateReaderFactory.java
v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesWriterFactory.java
v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/TapV2Customizer.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslator.java
v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2Customizer.java [new file with mode: 0644]
v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfaces/TapV2CustomizerTest.java [new file with mode: 0644]
v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslatorTest.java
v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapCustomizerTest.java
v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2CustomizerTest.java [new file with mode: 0644]

index cdfa047..a653277 100644 (file)
@@ -1,6 +1,6 @@
 {
        "info": {
-               "_postman_id": "8204419d-8019-4b29-b758-4a0693efe2cb",
+               "_postman_id": "b7fd7461-3b63-40ed-9aee-dd9668109865",
                "name": "Honeycomb V3PO RESTCONF calls",
                "description": "Common mgmt operations on VPP core using Honeycomb REST interface",
                "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
@@ -8,7 +8,6 @@
        "item": [
                {
                        "name": "Loopback",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Add loopback ifc - cfg ",
@@ -88,7 +87,6 @@
                },
                {
                        "name": "Bridge Domain management",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Create bridge domain - cfg",
                },
                {
                        "name": "Context",
-                       "description": null,
                        "item": [
                                {
                                        "name": "List naming contexts - context",
                },
                {
                        "name": "ETH",
-                       "description": "Example requests for management of Ethernet type interfaces",
                        "item": [
                                {
                                        "name": "Set interface mtu",
                                        },
                                        "response": []
                                }
-                       ]
+                       ],
+                       "description": "Example requests for management of Ethernet type interfaces"
                },
                {
                        "name": "GRE-TUNNEL",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Add gre ifc - cfg",
                },
                {
                        "name": "PBB",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Enable local0 + configure Pbb",
                },
                {
                        "name": "IP",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Ip Neighbour Get",
                },
                {
                        "name": "SPAN",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Set port mirroring local0 -> GigabiteEthernet0/8/0",
                },
                {
                        "name": "TAP",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Add simple tap ifc -cfg",
                                }
                        ]
                },
+               {
+                       "name": "TAPV2",
+                       "item": [
+                               {
+                                       "name": "Add tapV2 ifc -cfg",
+                                       "request": {
+                                               "method": "PUT",
+                                               "header": [
+                                                       {
+                                                               "key": "Authorization",
+                                                               "value": "Basic YWRtaW46YWRtaW4="
+                                                       },
+                                                       {
+                                                               "key": "Content-Type",
+                                                               "value": "application/json"
+                                                       }
+                                               ],
+                                               "body": {
+                                                       "mode": "raw",
+                                                       "raw": "{\n    \"interface\": [\n        {\n            \"name\": \"tap-v2\",\n            \"description\": \"for testing purposes\",\n            \"type\": \"v3po:tap-v2\",\n            \"tap-v2\" :{\n                \"mac\" : \"00:ff:ff:ff:ff:ff\",\n                \"tx-ring-size\" : 512,\n                \"rx-ring-size\" : 512,\n                \"host-mac\" : \"00:ee:ee:ee:ee:ee\",\n                \"host-interface-name\" : \"vpp-tap-v2\",\n                \"host-bridge\" : \"testBridge\",\n                \"host-ipv4-address\" : \"192.168.20.100/24\",\n                \"host-ipv6-address\" : \"a::100/96\",\n                \"host-ipv4-gateway\" : \"192.168.20.1\",\n                \"host-ipv6-gateway\" : \"a::1\",\n                \"tag\": \"tap-v2_tag\"\n            }\n        }\n    ]\n\n}"
+                                               },
+                                               "url": {
+                                                       "raw": "http://localhost:8183/restconf/config/ietf-interfaces:interfaces/interface/tap-v2",
+                                                       "protocol": "http",
+                                                       "host": [
+                                                               "localhost"
+                                                       ],
+                                                       "port": "8183",
+                                                       "path": [
+                                                               "restconf",
+                                                               "config",
+                                                               "ietf-interfaces:interfaces",
+                                                               "interface",
+                                                               "tap-v2"
+                                                       ]
+                                               },
+                                               "description": "When adding Tap to a bridge it is needed that the bridge exists. You can create it with these commands:\n\nsudo brctl addbr testBridge\nsudo brctl stp   testBridge off\nsudo ip link set dev testBridge up"
+                                       },
+                                       "response": []
+                               },
+                               {
+                                       "name": "Delete tapV2 ifc - cfg",
+                                       "request": {
+                                               "method": "DELETE",
+                                               "header": [
+                                                       {
+                                                               "key": "Authorization",
+                                                               "value": "Basic YWRtaW46YWRtaW4="
+                                                       },
+                                                       {
+                                                               "key": "Content-Type",
+                                                               "value": "application/json"
+                                                       }
+                                               ],
+                                               "body": {
+                                                       "mode": "raw",
+                                                       "raw": ""
+                                               },
+                                               "url": {
+                                                       "raw": "http://localhost:8183/restconf/config/ietf-interfaces:interfaces/interface/tap-v2",
+                                                       "protocol": "http",
+                                                       "host": [
+                                                               "localhost"
+                                                       ],
+                                                       "port": "8183",
+                                                       "path": [
+                                                               "restconf",
+                                                               "config",
+                                                               "ietf-interfaces:interfaces",
+                                                               "interface",
+                                                               "tap-v2"
+                                                       ]
+                                               }
+                                       },
+                                       "response": []
+                               }
+                       ]
+               },
                {
                        "name": "VHOST",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Add vhost user ifc - cfg",
                },
                {
                        "name": "VLAN",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Add sub interface - cfg",
                },
                {
                        "name": "VXLAN-GPE-TUNNEL",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Add vxlan-gpe - cfg",
                },
                {
                        "name": "VXLAN-TUNNEL",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Add virtual ifc - cfg",
                },
                {
                        "name": "AF_PACKET",
-                       "description": null,
                        "item": [
                                {
                                        "name": "Add af_packet interface",
index 862bd44..b8e5a7b 100644 (file)
@@ -27,6 +27,7 @@ import io.fd.hc2vpp.v3po.interfacesstate.InterfaceCustomizer;
 import io.fd.hc2vpp.v3po.interfacesstate.InterfaceRoutingCustomizer;
 import io.fd.hc2vpp.v3po.interfacesstate.L2Customizer;
 import io.fd.hc2vpp.v3po.interfacesstate.TapCustomizer;
+import io.fd.hc2vpp.v3po.interfacesstate.TapV2Customizer;
 import io.fd.hc2vpp.v3po.interfacesstate.VhostUserCustomizer;
 import io.fd.hc2vpp.v3po.interfacesstate.AfPacketCustomizer;
 import io.fd.hc2vpp.v3po.interfacesstate.VxlanCustomizer;
@@ -52,6 +53,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.rev181008.interfaces.state._interface.Span;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.SpanBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.Tap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.TapV2;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.VhostUser;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.AfPacket;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.Vxlan;
@@ -118,6 +120,9 @@ public final class InterfacesStateReaderFactory implements ReaderFactory {
         //    Tap
         registry.add(new GenericInitReader<>(vppIfcAugId.child(Tap.class),
                 new TapCustomizer(jvpp, ifcNamingCtx, ifaceDumpManager)));
+        //    TapV2
+        registry.add(new GenericInitReader<>(vppIfcAugId.child(TapV2.class),
+                new TapV2Customizer(jvpp, ifcNamingCtx, ifaceDumpManager)));
         //    VhostUser
         registry.add(new GenericInitReader<>(vppIfcAugId.child(VhostUser.class),
                 new VhostUserCustomizer(jvpp, ifcNamingCtx, ifaceDumpManager)));
index ea8fd4c..cf982aa 100644 (file)
@@ -29,6 +29,7 @@ import io.fd.hc2vpp.v3po.interfaces.InterfaceUnnumberedCustomizer;
 import io.fd.hc2vpp.v3po.interfaces.L2Customizer;
 import io.fd.hc2vpp.v3po.interfaces.LoopbackCustomizer;
 import io.fd.hc2vpp.v3po.interfaces.TapCustomizer;
+import io.fd.hc2vpp.v3po.interfaces.TapV2Customizer;
 import io.fd.hc2vpp.v3po.interfaces.VhostUserCustomizer;
 import io.fd.hc2vpp.v3po.interfaces.AfPacketCustomizer;
 import io.fd.hc2vpp.v3po.interfaces.VxlanCustomizer;
@@ -53,6 +54,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.rev181008.interfaces._interface.Routing;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.Span;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.Tap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.TapV2;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.VhostUser;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.AfPacket;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.Vxlan;
@@ -120,6 +122,10 @@ public final class InterfacesWriterFactory implements WriterFactory {
         final InstanceIdentifier<Tap> tapId = VPP_IFC_AUG_ID.child(Tap.class);
         registry.addBefore(new GenericWriter<>(tapId, new TapCustomizer(jvpp, ifcNamingContext)),
                 ifcId);
+        // TapV2(Needs to be executed before Interface customizer) =
+        final InstanceIdentifier<TapV2> tapV2Id = VPP_IFC_AUG_ID.child(TapV2.class);
+        registry.addBefore(new GenericWriter<>(tapV2Id, new TapV2Customizer(jvpp, ifcNamingContext)),
+                ifcId);
         // Loopback(Needs to be executed before Interface customizer) =
         final InstanceIdentifier<Loopback> loopbackId = VPP_IFC_AUG_ID.child(Loopback.class);
         registry.addBefore(new GenericWriter<>(loopbackId, new LoopbackCustomizer(jvpp, ifcNamingContext)),
@@ -131,7 +137,7 @@ public final class InterfacesWriterFactory implements WriterFactory {
                 ifcId);
 
         final Set<InstanceIdentifier<?>> specificIfcTypes =
-            Sets.newHashSet(vhostId, afpacketId, vxlanId, vxlanGpeId, tapId, loopbackId);
+            Sets.newHashSet(vhostId, afpacketId, vxlanId, vxlanGpeId, tapId, tapV2Id, loopbackId);
 
         // Ethernet =
         registry.add(new GenericWriter<>(VPP_IFC_AUG_ID.child(Ethernet.class),
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/TapV2Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/TapV2Customizer.java
new file mode 100644 (file)
index 0000000..5f315c8
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies 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.hc2vpp.v3po.interfaces;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import io.fd.hc2vpp.common.translate.util.AbstractInterfaceTypeCustomizer;
+import io.fd.hc2vpp.common.translate.util.Ipv4Translator;
+import io.fd.hc2vpp.common.translate.util.Ipv6Translator;
+import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer;
+import io.fd.hc2vpp.common.translate.util.MacTranslator;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.dto.TapCreateV2;
+import io.fd.vpp.jvpp.core.dto.TapCreateV2Reply;
+import io.fd.vpp.jvpp.core.dto.TapDeleteV2;
+import io.fd.vpp.jvpp.core.dto.TapDeleteV2Reply;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import java.nio.charset.StandardCharsets;
+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.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+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.inet.types.rev130715.Ipv6Prefix;
+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.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.TapV2;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TapV2Customizer extends AbstractInterfaceTypeCustomizer<TapV2>
+        implements MacTranslator, Ipv4Translator, Ipv6Translator, JvppReplyConsumer {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TapV2Customizer.class);
+    private final NamingContext interfaceContext;
+
+    public TapV2Customizer(final FutureJVppCore vppApi, final NamingContext interfaceContext) {
+        super(vppApi);
+        this.interfaceContext = interfaceContext;
+    }
+
+    @Override
+    protected Class<? extends InterfaceType> getExpectedInterfaceType() {
+        return org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.TapV2.class;
+    }
+
+    @Override
+    protected final void writeInterface(@Nonnull final InstanceIdentifier<TapV2> id, @Nonnull final TapV2 dataAfter,
+                                        @Nonnull final WriteContext writeContext)
+            throws WriteFailedException {
+        final String ifcName = id.firstKeyOf(Interface.class).getName();
+        createTapV2(id, ifcName, dataAfter, writeContext);
+    }
+
+    @Override
+    public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<TapV2> id, @Nonnull final TapV2 dataBefore,
+                                        @Nonnull final WriteContext writeContext)
+            throws WriteFailedException {
+        final String ifcName = id.firstKeyOf(Interface.class).getName();
+
+        final int index;
+        try {
+            index = interfaceContext.getIndex(ifcName, writeContext.getMappingContext());
+        } catch (IllegalArgumentException e) {
+            throw new WriteFailedException.DeleteFailedException(id, e);
+        }
+
+        deleteTapV2(id, ifcName, index, dataBefore, writeContext);
+    }
+
+    private void createTapV2(final InstanceIdentifier<TapV2> id, final String swIfName, final TapV2 tapv2,
+                             final WriteContext writeContext) throws WriteFailedException {
+        LOG.debug("Setting TapV2 interface: {}. TapV2: {}", swIfName, tapv2);
+        final CompletionStage<TapCreateV2Reply> tapV2CreateFuture = getFutureJVpp()
+                .tapCreateV2(getTapV2CreateRequest(tapv2));
+        final TapCreateV2Reply reply = getReplyForCreate(tapV2CreateFuture.toCompletableFuture(), id, tapv2);
+        LOG.debug("TapV2 set successfully for: {}, TapV2: {}", swIfName, tapv2);
+        // Add new interface to our interface context
+        interfaceContext.addName(reply.swIfIndex, swIfName, writeContext.getMappingContext());
+    }
+
+    private void deleteTapV2(final InstanceIdentifier<TapV2> id, final String swIfName, final int index,
+                             final TapV2 dataBefore, final WriteContext writeContext)
+            throws WriteFailedException {
+        LOG.debug("Deleting TapV2 interface: {}. TapV2: {}", swIfName, dataBefore);
+        final CompletionStage<TapDeleteV2Reply> vxlanAddDelTunnelReplyCompletionStage =
+                getFutureJVpp().tapDeleteV2(getTapV2DeleteRequest(index));
+        getReplyForDelete(vxlanAddDelTunnelReplyCompletionStage.toCompletableFuture(), id);
+        LOG.debug("TapV2 deleted successfully for: {}, TapV2: {}", swIfName, dataBefore);
+        // Remove deleted interface from interface context
+        interfaceContext.removeName(swIfName, writeContext.getMappingContext());
+    }
+
+    private TapCreateV2 getTapV2CreateRequest(final TapV2 tapv2) {
+        final TapCreateV2 tapConnect = new TapCreateV2();
+        final PhysAddress mac = tapv2.getMac();
+        if (mac == null) {
+            tapConnect.useRandomMac = 1;
+            tapConnect.macAddress = new byte[6];
+        } else {
+            tapConnect.useRandomMac = 0;
+            tapConnect.macAddress = parseMac(mac.getValue());
+        }
+
+        final Integer rxRingSz = tapv2.getRxRingSize();
+        if (rxRingSz != null) {
+            tapConnect.rxRingSz = rxRingSz.shortValue();
+        }
+
+        final Integer txRingSz = tapv2.getTxRingSize();
+        if (txRingSz != null) {
+            tapConnect.txRingSz = txRingSz.shortValue();
+        }
+
+        final String tag = tapv2.getTag();
+        if (tag != null) {
+            tapConnect.tag = tag.getBytes(StandardCharsets.US_ASCII);
+        }
+
+        setHostProperties(tapConnect, tapv2);
+        return tapConnect;
+    }
+
+    private TapDeleteV2 getTapV2DeleteRequest(final int swIndex) {
+        final TapDeleteV2 tapDeleteV2 = new TapDeleteV2();
+        tapDeleteV2.swIfIndex = swIndex;
+        return tapDeleteV2;
+    }
+
+    private void setHostProperties(TapCreateV2 tapConnect, TapV2 tapv2) {
+
+        final PhysAddress hostMacAddress = tapv2.getHostMac();
+        if (hostMacAddress != null) {
+            tapConnect.hostMacAddr = parseMac(hostMacAddress.getValue());
+            tapConnect.hostMacAddrSet = 1;
+        } else {
+            tapConnect.hostMacAddr = new byte[6];
+            tapConnect.hostMacAddrSet = 0;
+        }
+
+        final String hostIfName = tapv2.getHostInterfaceName();
+        if (hostIfName != null) {
+            tapConnect.hostIfName = hostIfName.getBytes(UTF_8);
+            tapConnect.hostIfNameSet = 1;
+        } else {
+            tapConnect.hostIfNameSet = 0;
+            tapConnect.hostIfName = new byte[64];
+        }
+
+        final String hostBridge = tapv2.getHostBridge();
+        if (hostBridge != null) {
+            tapConnect.hostBridgeSet = 1;
+            tapConnect.hostBridge = hostBridge.getBytes(UTF_8);
+        } else {
+            tapConnect.hostBridgeSet = 0;
+            tapConnect.hostBridge = new byte[64];
+        }
+
+        final String hostNamespace = tapv2.getHostNamespace();
+        if (hostNamespace != null) {
+            tapConnect.hostNamespaceSet = 1;
+            tapConnect.hostNamespace = hostNamespace.getBytes(UTF_8);
+        } else {
+            tapConnect.hostNamespaceSet = 0;
+            tapConnect.hostNamespace = new byte[64];
+        }
+
+        final Ipv4Prefix hostIpv4address = tapv2.getHostIpv4Address();
+        if (hostIpv4address != null) {
+            tapConnect.hostIp4Addr = ipv4AddressPrefixToArray(hostIpv4address);
+            tapConnect.hostIp4AddrSet = 1;
+            tapConnect.hostIp4PrefixLen = extractPrefix(hostIpv4address);
+        } else {
+            tapConnect.hostIp4Addr = new byte[4];
+            tapConnect.hostIp4AddrSet = 0;
+            tapConnect.hostIp4PrefixLen = 0;
+        }
+
+        final Ipv4Address hostIpv4GW = tapv2.getHostIpv4Gateway();
+        if (hostIpv4GW != null) {
+            tapConnect.hostIp4Gw = ipv4AddressNoZoneToArray(new Ipv4AddressNoZone(hostIpv4GW));
+            tapConnect.hostIp4GwSet = 1;
+        } else {
+            tapConnect.hostIp4Gw = new byte[4];
+            tapConnect.hostIp4GwSet = 0;
+        }
+
+        final Ipv6Prefix hostIpv6address = tapv2.getHostIpv6Address();
+        if (hostIpv6address != null) {
+            tapConnect.hostIp6Addr = ipv6AddressPrefixToArray(hostIpv6address);
+            tapConnect.hostIp6AddrSet = 1;
+            tapConnect.hostIp6PrefixLen = extractPrefix(hostIpv6address);
+        } else {
+            tapConnect.hostIp6Addr = new byte[16];
+            tapConnect.hostIp6AddrSet = 0;
+            tapConnect.hostIp6PrefixLen = 0;
+        }
+
+        final Ipv6Address hostIpv6GW = tapv2.getHostIpv6Gateway();
+        if (hostIpv6GW != null) {
+            tapConnect.hostIp6Gw =  ipv6AddressNoZoneToArray(hostIpv6GW);
+            tapConnect.hostIp6GwSet = 1;
+        } else {
+            tapConnect.hostIp6Gw = new byte[16];
+            tapConnect.hostIp6GwSet = 0;
+        }
+    }
+}
index aa52d0b..188f83d 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.rev181008.GreTunnel;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.Loopback;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.Tap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.TapV2;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VhostUser;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VxlanGpeTunnel;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VxlanTunnel;
@@ -138,10 +139,14 @@ public interface InterfaceDataTranslator extends ByteDataTranslator, JvppReplyCo
      */
     @Nonnull
     default Class<? extends InterfaceType> getInterfaceType(@Nonnull final String interfaceName) {
-        if (interfaceName.startsWith("tap")) {
+        if (interfaceName.startsWith("tapcli-")) {
             return Tap.class;
         }
 
+        if (interfaceName.startsWith("tap")) {
+            return TapV2.class;
+        }
+
         if (interfaceName.startsWith("vxlan_gpe")) {
             return VxlanGpeTunnel.class;
         }
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2Customizer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2Customizer.java
new file mode 100644 (file)
index 0000000..934a5c5
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies 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.hc2vpp.v3po.interfacesstate;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+
+import io.fd.hc2vpp.common.translate.util.ByteDataTranslator;
+import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer;
+import io.fd.hc2vpp.common.translate.util.Ipv4Translator;
+import io.fd.hc2vpp.common.translate.util.Ipv6Translator;
+import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer;
+import io.fd.hc2vpp.common.translate.util.MacTranslator;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.hc2vpp.v3po.interfacesstate.cache.InterfaceCacheDumpManager;
+import io.fd.honeycomb.translate.read.ReadContext;
+import io.fd.honeycomb.translate.read.ReadFailedException;
+import io.fd.honeycomb.translate.spi.read.Initialized;
+import io.fd.honeycomb.translate.spi.read.InitializingReaderCustomizer;
+import io.fd.honeycomb.translate.util.RWUtils;
+import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager;
+import io.fd.honeycomb.translate.util.read.cache.StaticCacheKeyFactory;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceTapV2Details;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceTapV2DetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceTapV2Dump;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import java.util.Optional;
+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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VppInterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VppInterfaceStateAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.TapV2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.TapV2Builder;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class TapV2Customizer extends FutureJVppCustomizer
+        implements InitializingReaderCustomizer<TapV2, TapV2Builder>, InterfaceDataTranslator, JvppReplyConsumer,
+        MacTranslator, Ipv4Translator, Ipv6Translator{
+
+    private static final Logger LOG = LoggerFactory.getLogger(TapV2Customizer.class);
+    private NamingContext interfaceContext;
+    private final InterfaceCacheDumpManager dumpManager;
+    private final DumpCacheManager<SwInterfaceTapV2DetailsReplyDump, Void> tapV2DumpManager;
+
+    public TapV2Customizer(@Nonnull final FutureJVppCore jvpp,
+                           @Nonnull final NamingContext interfaceContext,
+                           @Nonnull final InterfaceCacheDumpManager dumpManager) {
+        super(jvpp);
+        this.interfaceContext = interfaceContext;
+        this.dumpManager = dumpManager;
+        this.tapV2DumpManager = new DumpCacheManager.DumpCacheManagerBuilder<SwInterfaceTapV2DetailsReplyDump, Void>()
+                .withCacheKeyFactory(new StaticCacheKeyFactory(TapV2Customizer.class.getName() + "_dump",
+                        SwInterfaceTapV2DetailsReplyDump.class))
+                .withExecutor((identifier, params) -> {
+                    // Full TapV2 dump has to be performed here, no filter or anything is here to help so at least we cache it
+                    return getReplyForRead(getFutureJVpp()
+                            .swInterfaceTapV2Dump(new SwInterfaceTapV2Dump()).toCompletableFuture(), identifier);
+                }).build();
+    }
+
+    @Override
+    public void merge(@Nonnull Builder<? extends DataObject> parentBuilder, @Nonnull TapV2 readValue) {
+        ((VppInterfaceStateAugmentationBuilder) parentBuilder).setTapV2(readValue);
+    }
+
+    @Nonnull
+    @Override
+    public TapV2Builder getBuilder(@Nonnull InstanceIdentifier<TapV2> id) {
+        return new TapV2Builder();
+    }
+
+    @Override
+    public void readCurrentAttributes(@Nonnull final InstanceIdentifier<TapV2> id,
+                                      @Nonnull final TapV2Builder builder,
+                                      @Nonnull final ReadContext ctx) throws ReadFailedException {
+
+        final InterfaceKey key = id.firstKeyOf(Interface.class);
+        final int index = interfaceContext.getIndex(key.getName(), ctx.getMappingContext());
+        if (!isInterfaceOfType(dumpManager, id, ctx,
+                org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.TapV2.class)) {
+            return;
+        }
+
+        LOG.debug("Reading attributes for tapV2 interface: {}", key.getName());
+        final SwInterfaceTapV2DetailsReplyDump reply = tapV2DumpManager.getDump(id, ctx.getModificationCache())
+                .or(new SwInterfaceTapV2DetailsReplyDump());
+
+        final Optional<SwInterfaceTapV2Details> detail = reply.swInterfaceTapV2Details.stream()
+                .filter(d -> d.swIfIndex == index)
+                .findAny();
+
+        checkState(detail.isPresent(), "TapV2 interface for index %s not found", index);
+        final SwInterfaceTapV2Details swInterfaceTapV2Details = detail.get();
+
+        LOG.trace("TapV2 interface: {} attributes returned from VPP: {}", key.getName(), swInterfaceTapV2Details);
+        if (swInterfaceTapV2Details.devName != null && swInterfaceTapV2Details.devName[0] != 0) {
+            builder.setDeviceName(toString(swInterfaceTapV2Details.devName));
+        } else {
+            builder.setDeviceName(null);
+        }
+
+        if (swInterfaceTapV2Details.hostBridge != null && swInterfaceTapV2Details.hostBridge[0] != 0) {
+            builder.setHostBridge(toString(swInterfaceTapV2Details.hostBridge));
+        } else {
+            builder.setHostBridge(null);
+        }
+
+        if (swInterfaceTapV2Details.hostMacAddr != null &&
+                !ByteDataTranslator.INSTANCE.isArrayZeroed(swInterfaceTapV2Details.hostMacAddr)) {
+            builder.setHostMac(toPhysAddress(swInterfaceTapV2Details.hostMacAddr));
+        } else {
+            builder.setHostMac(null);
+        }
+
+        if (swInterfaceTapV2Details.hostIfName != null && swInterfaceTapV2Details.hostIfName[0] != 0) {
+            builder.setHostInterfaceName(toString(swInterfaceTapV2Details.hostIfName));
+        } else {
+            builder.setHostInterfaceName(null);
+        }
+
+        if (swInterfaceTapV2Details.hostIp4Addr != null && swInterfaceTapV2Details.hostIp4PrefixLen != 0) {
+            builder.setHostIpv4Address(
+                    toIpv4Prefix(swInterfaceTapV2Details.hostIp4Addr, swInterfaceTapV2Details.hostIp4PrefixLen));
+        } else {
+            builder.setHostIpv4Address(null);
+        }
+
+        if (swInterfaceTapV2Details.hostIp6Addr != null && swInterfaceTapV2Details.hostIp6PrefixLen != 0) {
+            builder.setHostIpv6Address(
+                    toIpv6Prefix(swInterfaceTapV2Details.hostIp6Addr,
+                            Byte.toUnsignedInt(swInterfaceTapV2Details.hostIp6PrefixLen)));
+        } else {
+            builder.setHostIpv6Address(null);
+        }
+
+        if (swInterfaceTapV2Details.hostNamespace != null && swInterfaceTapV2Details.hostNamespace[0] != 0) {
+            builder.setHostNamespace(toString(swInterfaceTapV2Details.hostNamespace));
+        } else {
+            builder.setHostNamespace(null);
+        }
+
+
+        builder.setRxRingSize(Short.toUnsignedInt(swInterfaceTapV2Details.rxRingSz));
+        builder.setTxRingSize(Short.toUnsignedInt(swInterfaceTapV2Details.txRingSz));
+        final SwInterfaceDetails ifcDetails = dumpManager.getInterfaceDetail(id, ctx, key.getName());
+
+        if (ifcDetails.tag[0] != 0) { // tag supplied
+            builder.setTag(toString(ifcDetails.tag));
+        }
+        LOG.debug("TapV2 interface: {}, id: {} attributes read as: {}", key.getName(), index, builder);
+    }
+
+    @Override
+    public Initialized<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.TapV2> init(
+            @Nonnull final InstanceIdentifier<TapV2> id, @Nonnull final TapV2 readValue,
+            @Nonnull final ReadContext ctx) {
+        // The MAC address & tag is set from interface details, those details are retrieved from cache
+        final InterfaceKey key = id.firstKeyOf(Interface.class);
+
+        final SwInterfaceDetails ifcDetails;
+        try {
+            ifcDetails = dumpManager.getInterfaceDetail(id, ctx, key.getName());
+        } catch (ReadFailedException e) {
+            throw new IllegalStateException(format("Unable to read interface %s", key.getName()), e);
+        }
+
+        return Initialized.create(getCfgId(id),
+                new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.TapV2Builder()
+                        .setMac(new PhysAddress(vppPhysAddrToYang(ifcDetails.l2Address)))
+                        .setHostInterfaceName(readValue.getHostInterfaceName())
+                        .setTag(ifcDetails.tag[0] == 0
+                                ? null
+                                : toString(ifcDetails.tag))
+                        .setHostBridge(readValue.getHostBridge())
+                        .setHostIpv4Address(readValue.getHostIpv4Address())
+                        .setHostIpv6Address(readValue.getHostIpv6Address())
+                        .setRxRingSize(readValue.getRxRingSize())
+                        .setTxRingSize(readValue.getTxRingSize())
+                        .setHostMac(readValue.getHostMac())
+                        .setHostNamespace(readValue.getHostNamespace())
+                        .build());
+    }
+
+    private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.TapV2> getCfgId(
+            final InstanceIdentifier<TapV2> id) {
+        return InterfaceCustomizer.getCfgId(RWUtils.cutId(id, Interface.class))
+                .augmentation(VppInterfaceAugmentation.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.TapV2.class);
+    }
+}
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfaces/TapV2CustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfaces/TapV2CustomizerTest.java
new file mode 100644 (file)
index 0000000..12dd434
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies 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.hc2vpp.v3po.interfaces;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import io.fd.hc2vpp.common.test.write.WriterCustomizerTest;
+import io.fd.hc2vpp.common.translate.util.Ipv4Translator;
+import io.fd.hc2vpp.common.translate.util.Ipv6Translator;
+import io.fd.hc2vpp.common.translate.util.MacTranslator;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.vpp.jvpp.core.dto.TapCreateV2;
+import io.fd.vpp.jvpp.core.dto.TapCreateV2Reply;
+import io.fd.vpp.jvpp.core.dto.TapDeleteV2;
+import io.fd.vpp.jvpp.core.dto.TapDeleteV2Reply;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.Assert;
+import org.junit.Test;
+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.Ipv4Prefix;
+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.inet.types.rev130715.Ipv6Prefix;
+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.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VppInterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.TapV2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces._interface.TapV2Builder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class TapV2CustomizerTest extends WriterCustomizerTest
+        implements Ipv4Translator, Ipv6Translator, MacTranslator {
+
+    private static final String IFC_TEST_INSTANCE = "ifc-test-instance";
+    private TapV2Customizer tapCustomizer;
+    private static final String HOST_IF_NAME = "tapV21";
+    private static final String HOST_BRIDGE = "TestBridge";
+    private static final String HOST_IPV4_PREFIX = "192.168.255.100";
+    private static final byte HOST_IPV4_PREFIX_LEN = 24;
+    private static final String HOST_IPV4_GW = "192.168.255.1";
+    private static final String HOST_IPV6_PREFIX = "a::100";
+    private static final String HOST_IPV6_GW = "a::1";
+    private static final byte HOST_IPV6_PREFIX_LEN = -128;
+    private static final int HOST_IPV6_PREFIX_LEN_EXP = 128;
+    private static final int RX_TX_RING_SIZE = 512;
+    private static final String HOST_MAC = "00:ee:ee:ee:ee:ee";
+    private static final String HOST_NAMESPACE = "testHostNS";
+
+    @Override
+    public void setUpTest() throws Exception {
+        InterfaceTypeTestUtils.setupWriteContext(writeContext,
+                org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.TapV2.class);
+        tapCustomizer = new TapV2Customizer(api, new NamingContext("ifcintest", IFC_TEST_INSTANCE));
+    }
+
+    @Test
+    public void testCreate() throws Exception {
+        final AtomicInteger idx = new AtomicInteger(0);
+        doAnswer((i) -> {
+            final TapCreateV2 tapData = i.getArgument(0);
+            if (tapData == null) {
+                return failedFuture();
+            }
+
+            Assert.assertArrayEquals(tapData.hostBridge, HOST_BRIDGE.getBytes());
+            Assert.assertEquals(tapData.hostBridgeSet, 1);
+            Assert.assertArrayEquals(tapData.hostIfName, HOST_IF_NAME.getBytes());
+            Assert.assertEquals(tapData.hostIfNameSet, 1);
+            Assert.assertArrayEquals(tapData.hostNamespace, HOST_NAMESPACE.getBytes());
+            Assert.assertEquals(tapData.hostNamespaceSet, 1);
+            Assert.assertArrayEquals(tapData.hostIp4Addr, ipv4AddressPrefixToArray(
+                    new Ipv4Prefix(String.format("%s/%d", HOST_IPV4_PREFIX, HOST_IPV4_PREFIX_LEN))));
+            Assert.assertEquals(tapData.hostIp4AddrSet, 1);
+            Assert.assertEquals(tapData.hostIp4PrefixLen, HOST_IPV4_PREFIX_LEN);
+            Assert.assertArrayEquals(tapData.hostIp6Addr, ipv6AddressPrefixToArray(
+                    new Ipv6Prefix(String.format("%s/%d", HOST_IPV6_PREFIX, HOST_IPV6_PREFIX_LEN_EXP))));
+            Assert.assertEquals(tapData.hostIp6AddrSet, 1);
+            Assert.assertEquals(tapData.hostIp6PrefixLen, HOST_IPV6_PREFIX_LEN);
+            Assert.assertArrayEquals(tapData.hostIp4Gw, ipv4AddressPrefixToArray(
+                    new Ipv4Prefix(String.format("%s/%d", HOST_IPV4_GW, 24))));
+            Assert.assertEquals(tapData.hostIp4GwSet, 1);
+            Assert.assertArrayEquals(tapData.hostIp6Gw, ipv6AddressPrefixToArray(
+                    new Ipv6Prefix(String.format("%s/%d", HOST_IPV6_GW, 96))));
+            Assert.assertEquals(tapData.hostIp6GwSet, 1);
+            Assert.assertArrayEquals(tapData.hostMacAddr, parseMac(HOST_MAC));
+            Assert.assertEquals(tapData.hostMacAddrSet, 1);
+            Assert.assertEquals(tapData.rxRingSz, RX_TX_RING_SIZE);
+            Assert.assertEquals(tapData.txRingSz, RX_TX_RING_SIZE);
+            final TapCreateV2Reply t = new TapCreateV2Reply();
+            t.swIfIndex = idx.getAndIncrement();
+            return future(t);
+
+        }).when(api).tapCreateV2(any(TapCreateV2.class));
+
+        tapCustomizer.writeCurrentAttributes(getTapId("tap"), getTapData("tap"), writeContext);
+        tapCustomizer.writeCurrentAttributes(getTapId("tap2"), getTapData("tap2"), writeContext);
+
+        verify(api, times(2)).tapCreateV2(any(TapCreateV2.class));
+        verify(mappingContext).put(eq(mappingIid("tap", IFC_TEST_INSTANCE)), eq(
+                mapping("tap", 0).get()));
+        verify(mappingContext).put(eq(mappingIid("tap2", IFC_TEST_INSTANCE)), eq(
+                mapping("tap2", 1).get()));
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        final TapCreateV2Reply t = new TapCreateV2Reply();
+        t.swIfIndex = 0;
+        doReturn(future(t)).when(api).tapCreateV2(any(TapCreateV2.class));
+
+        doReturn(future(new TapDeleteV2Reply())).when(api).tapDeleteV2(any(TapDeleteV2.class));
+        tapCustomizer.writeCurrentAttributes(getTapId("tap-v2"), getTapData("tap-v2"), writeContext);
+        defineMapping(mappingContext, "tap-v2", 1, IFC_TEST_INSTANCE);
+        tapCustomizer.deleteCurrentAttributes(getTapId("tap-v2"), getTapData("tap-v2"), writeContext);
+
+        verify(api).tapCreateV2(any(TapCreateV2.class));
+        verify(api).tapDeleteV2(any(TapDeleteV2.class));
+        verify(mappingContext).delete(eq(mappingIid("tap-v2", IFC_TEST_INSTANCE)));
+    }
+
+    private InstanceIdentifier<TapV2> getTapId(final String tap) {
+        return InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(tap)).augmentation(
+                VppInterfaceAugmentation.class).child(TapV2.class);
+    }
+
+    private TapV2 getTapData(final String tap) {
+        return new TapV2Builder()
+                .setHostInterfaceName(HOST_IF_NAME)
+                .setMac(new PhysAddress(HOST_MAC))
+                .setTag(tap + "_tag")
+                .setHostBridge(HOST_BRIDGE)
+                .setHostIpv4Address(new Ipv4Prefix(String.format("%s/%s", HOST_IPV4_PREFIX, HOST_IPV4_PREFIX_LEN)))
+                .setHostIpv4Gateway(new Ipv4Address(HOST_IPV4_GW))
+                .setHostIpv6Address(new Ipv6Prefix(String.format("%s/%s", HOST_IPV6_PREFIX, HOST_IPV6_PREFIX_LEN_EXP)))
+                .setHostIpv6Gateway(new Ipv6Address(HOST_IPV6_GW))
+                .setRxRingSize(RX_TX_RING_SIZE)
+                .setTxRingSize(RX_TX_RING_SIZE)
+                .setHostMac(new PhysAddress(HOST_MAC))
+                .setHostNamespace(HOST_NAMESPACE)
+                .build();
+    }
+}
index 17665ee..a3864e5 100644 (file)
@@ -34,6 +34,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.
 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.rev181008.AfPacket;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.Tap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.TapV2;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VhostUser;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VxlanGpeTunnel;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VxlanTunnel;
@@ -61,7 +62,8 @@ public class InterfaceDataTranslatorTest implements InterfaceDataTranslator {
 
     @Test
     public void testGetInterfaceType() {
-        assertEquals(Tap.class, getInterfaceType("tap0"));
+        assertEquals(Tap.class, getInterfaceType("tapcli-0"));
+        assertEquals(TapV2.class, getInterfaceType("tap0"));
         assertEquals(VxlanTunnel.class, getInterfaceType("vxlan0"));
         assertEquals(VxlanGpeTunnel.class, getInterfaceType("vxlan_gpe0"));
         assertEquals(VhostUser.class, getInterfaceType("VirtualEthernet0/0/0"));
@@ -72,7 +74,8 @@ public class InterfaceDataTranslatorTest implements InterfaceDataTranslator {
 
     @Test
     public void testIsInterfaceOfType() {
-        assertTrue(isInterfaceOfType(Tap.class, interfaceDetails("tap0")));
+        assertTrue(isInterfaceOfType(Tap.class, interfaceDetails("tapcli-0")));
+        assertTrue(isInterfaceOfType(TapV2.class, interfaceDetails("tap0")));
         assertTrue(isInterfaceOfType(VxlanTunnel.class, interfaceDetails("vxlan0")));
         assertTrue(isInterfaceOfType(VxlanGpeTunnel.class, interfaceDetails("vxlan_gpe0")));
         assertTrue(isInterfaceOfType(VhostUser.class, interfaceDetails("VirtualEthernet0/0/0")));
index aa07eaf..11d8ab8 100644 (file)
@@ -44,7 +44,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 public class TapCustomizerTest extends ReaderCustomizerTest<Tap, TapBuilder> implements InterfaceDumpHelper {
 
     private static final String IFC_CTX_NAME = "ifc-test-instance";
-    private static final String IF_NAME = "tap1";
+    private static final String IF_NAME = "tapcli-1";
     private static final String TAP_NAME = "testTapName";
     private static final int IF_INDEX = 1;
     private static final InstanceIdentifier<Tap> IID =
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2CustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2CustomizerTest.java
new file mode 100644 (file)
index 0000000..ee77afc
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies 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.hc2vpp.v3po.interfacesstate;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import io.fd.hc2vpp.common.test.read.ReaderCustomizerTest;
+import io.fd.hc2vpp.common.test.util.InterfaceDumpHelper;
+import io.fd.hc2vpp.common.translate.util.Ipv4Translator;
+import io.fd.hc2vpp.common.translate.util.Ipv6Translator;
+import io.fd.hc2vpp.common.translate.util.MacTranslator;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.hc2vpp.v3po.interfacesstate.cache.InterfaceCacheDumpManager;
+import io.fd.honeycomb.translate.read.ReadFailedException;
+import io.fd.honeycomb.translate.spi.read.ReaderCustomizer;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceTapV2Details;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceTapV2DetailsReplyDump;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6AddressNoZone;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
+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.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VppInterfaceStateAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.VppInterfaceStateAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.TapV2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev181008.interfaces.state._interface.TapV2Builder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class TapV2CustomizerTest extends ReaderCustomizerTest<TapV2, TapV2Builder> implements InterfaceDumpHelper,
+        Ipv4Translator, Ipv6Translator, MacTranslator {
+
+    private static final String IFC_CTX_NAME = "ifc-test-instance";
+    private static final String IF_NAME = "tapV21";
+    private static final String DEVICE_NAME = "testTapV2Device";
+    private static final int IF_INDEX = 1;
+    private static final String HOST_BRIDGE = "TestBridge";
+    private static final String HOST_IPV4_PREFIX = "192.168.255.100";
+    private static final byte HOST_IPV4_PREFIX_LEN = 24;
+    private static final String HOST_IPV6_PREFIX = "a::100";
+    private static final byte HOST_IPV6_PREFIX_LEN = -128;
+    private static final int HOST_IPV6_PREFIX_LEN_EXP = 128;
+    private static final int RX_TX_RING_SIZE = 512;
+    private static final String HOST_MAC = "00:ee:ee:ee:ee:ee";
+    private static final String HOST_NAMESPACE = "testHostNS";
+
+    private static final InstanceIdentifier<TapV2> IID =
+            InstanceIdentifier.create(InterfacesState.class).child(Interface.class, new InterfaceKey(IF_NAME))
+                    .augmentation(VppInterfaceStateAugmentation.class).child(TapV2.class);
+    private NamingContext interfaceContext;
+
+    @Mock
+    private InterfaceCacheDumpManager dumpCacheManager;
+
+    public TapV2CustomizerTest() {
+        super(TapV2.class, VppInterfaceStateAugmentationBuilder.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        interfaceContext = new NamingContext("generatedIfaceName", IFC_CTX_NAME);
+        defineMapping(mappingContext, IF_NAME, IF_INDEX, IFC_CTX_NAME);
+        when(dumpCacheManager.getInterfaceDetail(IID, ctx, IF_NAME)).thenReturn(ifaceDetails());
+    }
+
+    private SwInterfaceDetails ifaceDetails() {
+        final SwInterfaceDetails details = new SwInterfaceDetails();
+        details.swIfIndex = IF_INDEX;
+        details.interfaceName = IF_NAME.getBytes();
+        details.tag = new byte[64];
+        return details;
+    }
+
+    @Override
+    protected ReaderCustomizer<TapV2, TapV2Builder> initCustomizer() {
+        return new TapV2Customizer(api, interfaceContext, dumpCacheManager);
+    }
+
+    @Test
+    public void testRead() throws ReadFailedException {
+        final TapV2Builder builder = mock(TapV2Builder.class);
+        when(api.swInterfaceTapV2Dump(any())).thenReturn(future(tapDump()));
+        getCustomizer().readCurrentAttributes(IID, builder, ctx);
+        verify(builder).setHostInterfaceName(IF_NAME);
+        verify(builder).setDeviceName(DEVICE_NAME);
+        verify(builder).setHostBridge(HOST_BRIDGE);
+        verify(builder)
+                .setHostIpv4Address(new Ipv4Prefix(String.format("%s/%d", HOST_IPV4_PREFIX, HOST_IPV4_PREFIX_LEN)));
+        verify(builder)
+                .setHostIpv6Address(new Ipv6Prefix(String.format("%s/%d", HOST_IPV6_PREFIX, HOST_IPV6_PREFIX_LEN_EXP)));
+        verify(builder).setTxRingSize(RX_TX_RING_SIZE);
+        verify(builder).setRxRingSize(RX_TX_RING_SIZE);
+        verify(builder).setHostNamespace(HOST_NAMESPACE);
+        verify(builder).setHostMac(new PhysAddress(HOST_MAC));
+    }
+
+    @Test(expected = ReadFailedException.class)
+    public void testReadFailed() throws ReadFailedException {
+        when(api.swInterfaceTapV2Dump(any())).thenReturn(failedFuture());
+        getCustomizer().readCurrentAttributes(IID, mock(TapV2Builder.class), ctx);
+    }
+
+    private SwInterfaceTapV2DetailsReplyDump tapDump() {
+        final SwInterfaceTapV2DetailsReplyDump reply = new SwInterfaceTapV2DetailsReplyDump();
+        final SwInterfaceTapV2Details details = new SwInterfaceTapV2Details();
+        details.devName = DEVICE_NAME.getBytes(UTF_8);
+        details.swIfIndex = IF_INDEX;
+        details.hostBridge = HOST_BRIDGE.getBytes(UTF_8);
+        details.hostNamespace = HOST_NAMESPACE.getBytes(UTF_8);
+        details.hostIfName = IF_NAME.getBytes(UTF_8);
+        details.hostIp4PrefixLen = HOST_IPV4_PREFIX_LEN;
+        details.hostIp4Addr = ipv4AddressNoZoneToArray(HOST_IPV4_PREFIX);
+        details.hostIp6Addr = ipv6AddressNoZoneToArray(new Ipv6AddressNoZone(HOST_IPV6_PREFIX));
+        details.hostIp6PrefixLen = HOST_IPV6_PREFIX_LEN;
+        details.hostMacAddr = parseMac(HOST_MAC);
+        details.txRingSz = details.rxRingSz = RX_TX_RING_SIZE;
+        reply.swInterfaceTapV2Details.add(details);
+        return reply;
+    }
+}