From e4c6d641269efdff2eb1212a54f06683cb99d3d3 Mon Sep 17 00:00:00 2001 From: Tibor Král Date: Wed, 3 Oct 2018 17:23:08 +0200 Subject: HC2VPP-390: Tap v2 interface implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I92fe8adabbb770a45ad47c56f5cd21262c4bd6e1 Signed-off-by: Tibor Král Signed-off-by: Michal Cmarada --- v3po/postman_rest_collection.json | 97 +++++++-- .../v3po/factory/InterfacesStateReaderFactory.java | 5 + .../v3po/factory/InterfacesWriterFactory.java | 8 +- .../fd/hc2vpp/v3po/interfaces/TapV2Customizer.java | 227 +++++++++++++++++++++ .../interfacesstate/InterfaceDataTranslator.java | 7 +- .../v3po/interfacesstate/TapV2Customizer.java | 212 +++++++++++++++++++ .../v3po/interfaces/TapV2CustomizerTest.java | 162 +++++++++++++++ .../InterfaceDataTranslatorTest.java | 7 +- .../v3po/interfacesstate/TapCustomizerTest.java | 2 +- .../v3po/interfacesstate/TapV2CustomizerTest.java | 142 +++++++++++++ 10 files changed, 848 insertions(+), 21 deletions(-) create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/TapV2Customizer.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2Customizer.java create mode 100644 v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfaces/TapV2CustomizerTest.java create mode 100644 v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2CustomizerTest.java diff --git a/v3po/postman_rest_collection.json b/v3po/postman_rest_collection.json index cdfa047a1..a65327788 100644 --- a/v3po/postman_rest_collection.json +++ b/v3po/postman_rest_collection.json @@ -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", @@ -599,7 +597,6 @@ }, { "name": "Context", - "description": null, "item": [ { "name": "List naming contexts - context", @@ -710,7 +707,6 @@ }, { "name": "ETH", - "description": "Example requests for management of Ethernet type interfaces", "item": [ { "name": "Set interface mtu", @@ -822,11 +818,11 @@ }, "response": [] } - ] + ], + "description": "Example requests for management of Ethernet type interfaces" }, { "name": "GRE-TUNNEL", - "description": null, "item": [ { "name": "Add gre ifc - cfg", @@ -904,7 +900,6 @@ }, { "name": "PBB", - "description": null, "item": [ { "name": "Enable local0 + configure Pbb", @@ -1055,7 +1050,6 @@ }, { "name": "IP", - "description": null, "item": [ { "name": "Ip Neighbour Get", @@ -1489,7 +1483,6 @@ }, { "name": "SPAN", - "description": null, "item": [ { "name": "Set port mirroring local0 -> GigabiteEthernet0/8/0", @@ -1608,7 +1601,6 @@ }, { "name": "TAP", - "description": null, "item": [ { "name": "Add simple tap ifc -cfg", @@ -1756,9 +1748,86 @@ } ] }, + { + "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", @@ -1875,7 +1944,6 @@ }, { "name": "VLAN", - "description": null, "item": [ { "name": "Add sub interface - cfg", @@ -2207,7 +2275,6 @@ }, { "name": "VXLAN-GPE-TUNNEL", - "description": null, "item": [ { "name": "Add vxlan-gpe - cfg", @@ -2285,7 +2352,6 @@ }, { "name": "VXLAN-TUNNEL", - "description": null, "item": [ { "name": "Add virtual ifc - cfg", @@ -2363,7 +2429,6 @@ }, { "name": "AF_PACKET", - "description": null, "item": [ { "name": "Add af_packet interface", diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesStateReaderFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesStateReaderFactory.java index 862bd447f..b8e5a7bc3 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesStateReaderFactory.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesStateReaderFactory.java @@ -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))); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesWriterFactory.java index ea8fd4c81..cf982aabf 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesWriterFactory.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/factory/InterfacesWriterFactory.java @@ -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 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 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 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> 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 index 000000000..5f315c876 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/TapV2Customizer.java @@ -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 + 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 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 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 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 id, final String swIfName, final TapV2 tapv2, + final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Setting TapV2 interface: {}. TapV2: {}", swIfName, tapv2); + final CompletionStage 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 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 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; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslator.java index aa52d0bb3..188f83d0f 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslator.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslator.java @@ -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 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 index 000000000..934a5c595 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2Customizer.java @@ -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, InterfaceDataTranslator, JvppReplyConsumer, + MacTranslator, Ipv4Translator, Ipv6Translator{ + + private static final Logger LOG = LoggerFactory.getLogger(TapV2Customizer.class); + private NamingContext interfaceContext; + private final InterfaceCacheDumpManager dumpManager; + private final DumpCacheManager 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() + .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 parentBuilder, @Nonnull TapV2 readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setTapV2(readValue); + } + + @Nonnull + @Override + public TapV2Builder getBuilder(@Nonnull InstanceIdentifier id) { + return new TapV2Builder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier 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 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 init( + @Nonnull final InstanceIdentifier 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 getCfgId( + final InstanceIdentifier 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 index 000000000..12dd43452 --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfaces/TapV2CustomizerTest.java @@ -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 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(); + } +} diff --git a/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslatorTest.java b/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslatorTest.java index 17665ee30..a3864e583 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslatorTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/InterfaceDataTranslatorTest.java @@ -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"))); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapCustomizerTest.java index aa07eaff5..11d8ab8da 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapCustomizerTest.java @@ -44,7 +44,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; public class TapCustomizerTest extends ReaderCustomizerTest 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 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 index 000000000..ee77afc82 --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/hc2vpp/v3po/interfacesstate/TapV2CustomizerTest.java @@ -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 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 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 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; + } +} -- cgit 1.2.3-korg