From 930ac175fc8f37a5ceda8d61de6f6aa468fc4871 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 10 May 2016 16:35:16 +0200 Subject: HONEYCOMB-36: vhost user interfaces CRUD support Change-Id: I1c16cf71fa450fe5079b4af8b1e24bdc921a6e82 Signed-off-by: Marek Gradzki Signed-off-by: Maros Marsalek --- .../v3po/interfaces/VhostUserCustomizer.java | 180 ++++++++++++++ .../translate/v3po/interfaces/VxlanCustomizer.java | 2 +- .../v3po/interfacesstate/InterfaceUtils.java | 5 + .../v3po/interfacesstate/TapCustomizer.java | 2 + .../v3po/interfacesstate/VhostUserCustomizer.java | 121 +++++++++ .../v3po/translate/v3po/utils/V3poUtils.java | 10 + .../rev160406/InterfacesHoneycombWriterModule.java | 8 +- .../InterfacesStateHoneycombReaderModule.java | 7 + .../v3po/interfaces/VhostUserCustomizerTest.java | 274 +++++++++++++++++++++ .../v3po/translate/v3po/utils/V3poUtilsTest.java | 8 +- 10 files changed, 614 insertions(+), 3 deletions(-) create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java create mode 100644 v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java (limited to 'v3po/v3po2vpp') diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java new file mode 100644 index 000000000..40669761d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.translate.v3po.interfaces; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; +import io.fd.honeycomb.v3po.translate.v3po.util.VppApiInvocationException; +import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; +import io.fd.honeycomb.v3po.translate.write.WriteFailedException; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUserRole; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUser; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.dto.CreateVhostUserIf; +import org.openvpp.jvpp.dto.CreateVhostUserIfReply; +import org.openvpp.jvpp.dto.DeleteVhostUserIf; +import org.openvpp.jvpp.dto.DeleteVhostUserIfReply; +import org.openvpp.jvpp.dto.ModifyVhostUserIf; +import org.openvpp.jvpp.dto.ModifyVhostUserIfReply; +import org.openvpp.jvpp.future.FutureJVpp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer Customizer responsible for passing vhost user interface CRD operations to VPP + */ +public class VhostUserCustomizer extends FutureJVppCustomizer implements ChildWriterCustomizer { + + private static final Logger LOG = LoggerFactory.getLogger(VhostUserCustomizer.class); + private final NamingContext interfaceContext; + + public VhostUserCustomizer(@Nonnull final FutureJVpp vppApi, @Nonnull final NamingContext interfaceContext) { + super(vppApi); + this.interfaceContext = Preconditions.checkNotNull(interfaceContext, "interfaceContext should not be null"); + } + + @Nonnull + @Override + public Optional extract(@Nonnull final InstanceIdentifier currentId, + @Nonnull final DataObject parentData) { + return Optional.fromNullable(((VppInterfaceAugmentation) parentData).getVhostUser()); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final VhostUser dataAfter, @Nonnull final Context writeContext) + throws WriteFailedException.CreateFailedException { + try { + createVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataAfter); + } catch (VppApiInvocationException e) { + throw new WriteFailedException.CreateFailedException(id, dataAfter, e); + } + } + + private void createVhostUserIf(final String swIfName, final VhostUser vhostUser) throws VppApiInvocationException { + LOG.debug("Creating vhost user interface: name={}, vhostUser={}", swIfName, vhostUser); + final CompletionStage createVhostUserIfReplyCompletionStage = + getFutureJVpp().createVhostUserIf(getCreateVhostUserIfRequest(vhostUser)); + + final CreateVhostUserIfReply reply = + V3poUtils.getReply(createVhostUserIfReplyCompletionStage.toCompletableFuture()); + if (reply.retval < 0) { + LOG.debug("Failed to create vhost user interface: {}, vhostUser: {}", swIfName, vhostUser); + throw new VppApiInvocationException("createVhostUserIf", reply.context, reply.retval); + } else { + LOG.debug("Vhost user interface created successfully for: {}, vhostUser: {}", swIfName, vhostUser); + // Add new interface to our interface context + interfaceContext.addName(reply.swIfIndex, swIfName); + } + } + + private CreateVhostUserIf getCreateVhostUserIfRequest(final VhostUser vhostUser) { + CreateVhostUserIf request = new CreateVhostUserIf(); + request.isServer = V3poUtils.booleanToByte(VhostUserRole.Server.equals(vhostUser.getRole())); + request.sockFilename = vhostUser.getSocket().getBytes(); + request.renumber = 0; // TODO + request.customDevInstance = 0; // TODO + request.useCustomMac = 0; + request.macAddress = new byte[]{}; + return request; + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final VhostUser dataBefore, @Nonnull final VhostUser dataAfter, + @Nonnull final Context writeContext) + throws WriteFailedException.UpdateFailedException { + if (dataBefore.equals(dataAfter)) { + LOG.debug("dataBefore equals dataAfter, update will not be performed"); + return; + } + try { + modifyVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataAfter); + } catch (VppApiInvocationException e) { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, e); + } + } + + private void modifyVhostUserIf(final String swIfName, final VhostUser vhostUser) throws VppApiInvocationException { + LOG.debug("Updating vhost user interface: name={}, vhostUser={}", swIfName, vhostUser); + final CompletionStage modifyVhostUserIfReplyCompletionStage = + getFutureJVpp() + .modifyVhostUserIf(getModifyVhostUserIfRequest(vhostUser, interfaceContext.getIndex(swIfName))); + + final ModifyVhostUserIfReply reply = + V3poUtils.getReply(modifyVhostUserIfReplyCompletionStage.toCompletableFuture()); + if (reply.retval < 0) { + LOG.debug("Failed to update vhost user interface: {}, vhostUser: {}", swIfName, vhostUser); + throw new VppApiInvocationException("modifyVhostUserIf", reply.context, reply.retval); + } else { + LOG.debug("Vhost user interface updated successfully for: {}, vhostUser: {}", swIfName, vhostUser); + } + } + + private ModifyVhostUserIf getModifyVhostUserIfRequest(final VhostUser vhostUser, final int swIfIndex) { + ModifyVhostUserIf request = new ModifyVhostUserIf(); + request.isServer = V3poUtils.booleanToByte(VhostUserRole.Server.equals(vhostUser.getRole())); + request.sockFilename = vhostUser.getSocket().getBytes(); + request.renumber = 0; // TODO + request.customDevInstance = 0; // TODO + request.swIfIndex = swIfIndex; + return request; + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final VhostUser dataBefore, @Nonnull final Context writeContext) + throws WriteFailedException.DeleteFailedException { + try { + deleteVhostUserIf(id.firstKeyOf(Interface.class).getName(), dataBefore); + } catch (VppApiInvocationException e) { + throw new WriteFailedException.DeleteFailedException(id, e); + } + } + + private void deleteVhostUserIf(final String swIfName, final VhostUser vhostUser) throws VppApiInvocationException { + LOG.debug("Deleting vhost user interface: name={}, vhostUser={}", swIfName, vhostUser); + final CompletionStage deleteVhostUserIfReplyCompletionStage = + getFutureJVpp().deleteVhostUserIf(getDeleteVhostUserIfRequest(interfaceContext.getIndex(swIfName))); + + final DeleteVhostUserIfReply reply = + V3poUtils.getReply(deleteVhostUserIfReplyCompletionStage.toCompletableFuture()); + if (reply.retval < 0) { + LOG.debug("Failed to delete vhost user interface: {}, vhostUser: {}", swIfName, vhostUser); + throw new VppApiInvocationException("modifyVhostUserIf", reply.context, reply.retval); + } else { + LOG.debug("Vhost user interface deleted successfully for: {}, vhostUser: {}", swIfName, vhostUser); + // Remove interface from our interface context + interfaceContext.removeName(swIfName); + } + } + + private DeleteVhostUserIf getDeleteVhostUserIfRequest(final int swIfIndex) { + DeleteVhostUserIf request = new DeleteVhostUserIf(); + request.swIfIndex = swIfIndex; + return request; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java index a4a840e32..b5ef6e010 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java @@ -134,7 +134,7 @@ public class VxlanCustomizer extends FutureJVppCustomizer implements ChildWriter throw new VppApiInvocationException("vxlanAddDelTunnel", reply.context, reply.retval); } else { LOG.debug("Vxlan tunnel deleted successfully for: {}, vxlan: {}", swIfName, vxlan); - // Remove interface to our interface context + // Remove interface from our interface context interfaceContext.removeName(swIfName); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java index 62cac3550..09ea09e47 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java @@ -29,6 +29,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.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Gauge64; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUser; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel; import org.openvpp.jvpp.dto.SwInterfaceDetails; import org.openvpp.jvpp.dto.SwInterfaceDetailsReplyDump; @@ -196,6 +197,10 @@ public final class InterfaceUtils { return VxlanTunnel.class; } + if(interfaceName.startsWith("VirtualEthernet")) { + return VhostUser.class; + } + return EthernetCsmacd.class; } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java index a505436c3..1245f6a53 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java @@ -76,6 +76,8 @@ public class TapCustomizer extends FutureJVppCustomizer @Nonnull final Context ctx) throws ReadFailedException { final InterfaceKey key = id.firstKeyOf(Interface.class); + // TODO add logging + @SuppressWarnings("unchecked") Map mappedTaps = (Map) ctx.get(DUMPED_TAPS_CONTEXT_KEY); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java new file mode 100644 index 000000000..4b9c7c8bc --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.translate.v3po.interfacesstate; + +import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.read.ReadFailedException; +import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.FutureJVppCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; +import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; +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.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUserRole; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VhostUser; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VhostUserBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.dto.SwInterfaceVhostUserDetails; +import org.openvpp.jvpp.dto.SwInterfaceVhostUserDetailsReplyDump; +import org.openvpp.jvpp.dto.SwInterfaceVhostUserDump; +import org.openvpp.jvpp.future.FutureJVpp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class VhostUserCustomizer extends FutureJVppCustomizer + implements ChildReaderCustomizer { + + private static final Logger LOG = LoggerFactory.getLogger(VhostUserCustomizer.class); + public static final String DUMPED_VHOST_USERS_CONTEXT_KEY = VhostUserCustomizer.class.getName() + "dumpedVhostUsersDuringGetAllIds"; + private NamingContext interfaceContext; + + public VhostUserCustomizer(@Nonnull final FutureJVpp jvpp, + final NamingContext interfaceContext) { + super(jvpp); + this.interfaceContext = interfaceContext; + } + + @Override + public void merge(@Nonnull Builder parentBuilder, + @Nonnull VhostUser readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setVhostUser(readValue); + } + + @Nonnull + @Override + public VhostUserBuilder getBuilder( + @Nonnull InstanceIdentifier id) { + return new VhostUserBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final VhostUserBuilder builder, + @Nonnull final Context ctx) throws ReadFailedException { + final InterfaceKey key = id.firstKeyOf(Interface.class); + + @SuppressWarnings("unchecked") + Map mappedVhostUsers = + (Map) ctx.get(DUMPED_VHOST_USERS_CONTEXT_KEY); + + if(mappedVhostUsers == null) { + // Full VhostUser dump has to be performed here, no filter or anything is here to help so at least we cache it + final SwInterfaceVhostUserDump request = new SwInterfaceVhostUserDump(); + final CompletionStage swInterfaceVhostUserDetailsReplyDumpCompletionStage = + getFutureJVpp().swInterfaceVhostUserDump(request); + final SwInterfaceVhostUserDetailsReplyDump reply = + V3poUtils.getReply(swInterfaceVhostUserDetailsReplyDumpCompletionStage.toCompletableFuture()); + + if(null == reply || null == reply.swInterfaceVhostUserDetails) { + mappedVhostUsers = Collections.emptyMap(); + } else { + final List swInterfaceVhostUserDetails = reply.swInterfaceVhostUserDetails; + // Cache interfaces dump in per-tx context to later be used in readCurrentAttributes + mappedVhostUsers = swInterfaceVhostUserDetails.stream() + .collect(Collectors.toMap(t -> t.swIfIndex, swInterfaceDetails -> swInterfaceDetails)); + } + + ctx.put(DUMPED_VHOST_USERS_CONTEXT_KEY, mappedVhostUsers); + } + + // Relying here that parent InterfaceCustomizer was invoked first to fill in the context with initial ifc mapping + final int index = interfaceContext.getIndex(key.getName()); + final SwInterfaceVhostUserDetails swInterfaceVhostUserDetails = mappedVhostUsers.get(index); + if(swInterfaceVhostUserDetails == null) { + // Not a VhostUser interface type + return; + } + + builder.setRole(swInterfaceVhostUserDetails.isServer == 1 ? VhostUserRole.Server : VhostUserRole.Client); + builder.setFeatures(BigInteger.valueOf(swInterfaceVhostUserDetails.features)); + builder.setNumMemoryRegions((long) swInterfaceVhostUserDetails.numRegions); + builder.setSocket(V3poUtils.toString(swInterfaceVhostUserDetails.sockFilename)); + builder.setVirtioNetHdrSize((long) swInterfaceVhostUserDetails.virtioNetHdrSz); + builder.setConnectError(Integer.toString(swInterfaceVhostUserDetails.sockErrno)); + // TODO add logging + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java index 392dc4666..8742d1d60 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java @@ -24,6 +24,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.function.BiConsumer; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; import org.openvpp.jvpp.dto.JVppReply; @@ -96,4 +97,13 @@ public final class V3poUtils { private static byte parseHexByte(final String aByte) { return (byte)Integer.parseInt(aByte, 16); } + + /** + * Returns 0 if argument is null or false, 1 otherwise. + * @param value Boolean value to be converted + * @return byte value equal to 0 or 1 + */ + public static byte booleanToByte(@Nullable final Boolean value) { + return value != null && value ? (byte) 1 : (byte) 0; + } } diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java index 2095f3000..fb60635bf 100644 --- a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java +++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java @@ -14,6 +14,7 @@ import io.fd.honeycomb.v3po.translate.v3po.interfaces.InterfaceCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.L2Customizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.RoutingCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.TapCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.interfaces.VhostUserCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.VxlanCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.Ipv4Customizer; import io.fd.honeycomb.v3po.translate.v3po.interfaces.ip.Ipv6Customizer; @@ -30,6 +31,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Routing; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUser; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Vxlan; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.ChildOf; @@ -96,7 +98,10 @@ public class InterfacesHoneycombWriterModule extends org.opendaylight.yang.gen.v new RoutingCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); final ChildWriter vxlanWriter = new CompositeChildWriter<>(Vxlan.class, - new VxlanCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); + new VxlanCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); + + final ChildWriter vhostUserWriter = new CompositeChildWriter<>(VhostUser.class, + new VhostUserCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); final ChildWriter tapWriter = new CompositeChildWriter<>(Tap.class, new TapCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); @@ -105,6 +110,7 @@ public class InterfacesHoneycombWriterModule extends org.opendaylight.yang.gen.v new L2Customizer(getVppJvppIfcDependency(), getInterfaceContextDependency(), getBridgeDomainContextDependency())); final List>> vppIfcChildWriters = Lists.newArrayList(); + vppIfcChildWriters.add(vhostUserWriter); vppIfcChildWriters.add(vxlanWriter); vppIfcChildWriters.add(tapWriter); vppIfcChildWriters.add(ethernetWriter); diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java index 13fcf008e..3bd0e0c16 100644 --- a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java +++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java @@ -17,6 +17,7 @@ import io.fd.honeycomb.v3po.translate.util.read.ReflexiveRootReaderCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfacesstate.EthernetCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceCustomizer; import io.fd.honeycomb.v3po.translate.v3po.interfacesstate.TapCustomizer; +import io.fd.honeycomb.v3po.translate.v3po.interfacesstate.VhostUserCustomizer; import java.util.List; 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.InterfacesStateBuilder; @@ -27,6 +28,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentationBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Ethernet; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Tap; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VhostUser; import org.opendaylight.yangtools.yang.binding.ChildOf; public class InterfacesStateHoneycombReaderModule extends @@ -59,9 +61,14 @@ public class InterfacesStateHoneycombReaderModule extends new CompositeChildReader<>(Tap.class, new TapCustomizer(getVppJvppDependency(), getInterfaceContextIfcStateDependency())); + final ChildReader> vhostUserReader = + new CompositeChildReader<>(VhostUser.class, + new VhostUserCustomizer(getVppJvppDependency(), getInterfaceContextIfcStateDependency())); + final List>> childReaders = Lists.newArrayList(); childReaders.add(ethernetReader); childReaders.add(tapReader); + childReaders.add(vhostUserReader); final ChildReader vppInterfaceStateAugmentationChildReader = new CompositeChildReader<>(VppInterfaceStateAugmentation.class, diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java new file mode 100644 index 000000000..076df6783 --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.translate.v3po.interfaces; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.honeycomb.v3po.translate.Context; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; +import io.fd.honeycomb.v3po.translate.v3po.util.VppApiInvocationException; +import io.fd.honeycomb.v3po.translate.v3po.utils.V3poUtils; +import io.fd.honeycomb.v3po.translate.write.WriteFailedException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUserRole; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUser; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUserBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.dto.CreateVhostUserIf; +import org.openvpp.jvpp.dto.CreateVhostUserIfReply; +import org.openvpp.jvpp.dto.DeleteVhostUserIf; +import org.openvpp.jvpp.dto.DeleteVhostUserIfReply; +import org.openvpp.jvpp.dto.ModifyVhostUserIf; +import org.openvpp.jvpp.dto.ModifyVhostUserIfReply; +import org.openvpp.jvpp.future.FutureJVpp; + +public class VhostUserCustomizerTest { + + @Mock + private FutureJVpp api; + @Mock + private Context ctx; + + private NamingContext namingContext; + private VhostUserCustomizer customizer; + private static final int IFACE_ID = 1; + private static final String IFACE_NAME = "eth0"; + private static final InstanceIdentifier ID = + InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(IFACE_NAME)) + .augmentation(VppInterfaceAugmentation.class).child(VhostUser.class); + + @Before + public void setUp() throws Exception { + initMocks(this); + namingContext = new NamingContext("generatedInterfaceName"); + // TODO create base class for tests using vppApi + customizer = new VhostUserCustomizer(api, namingContext); + } + + private void whenCreateVhostUserIfThen(final int retval) throws ExecutionException, InterruptedException { + final CompletionStage replyCS = mock(CompletionStage.class); + final CompletableFuture replyFuture = mock(CompletableFuture.class); + when(replyCS.toCompletableFuture()).thenReturn(replyFuture); + final CreateVhostUserIfReply reply = new CreateVhostUserIfReply(); + reply.retval = retval; + when(replyFuture.get()).thenReturn(reply); + when(api.createVhostUserIf(any(CreateVhostUserIf.class))).thenReturn(replyCS); + } + + private void whenCreateVhostUserIfThenSuccess() throws ExecutionException, InterruptedException { + whenCreateVhostUserIfThen(0); + } + + private void whenVxlanAddDelTunnelThenFailure() throws ExecutionException, InterruptedException { + whenCreateVhostUserIfThen(-1); + } + + private void whenModifyVhostUserIfThen(final int retval) throws ExecutionException, InterruptedException { + final CompletionStage replyCS = mock(CompletionStage.class); + final CompletableFuture replyFuture = mock(CompletableFuture.class); + when(replyCS.toCompletableFuture()).thenReturn(replyFuture); + final ModifyVhostUserIfReply reply = new ModifyVhostUserIfReply(); + reply.retval = retval; + when(replyFuture.get()).thenReturn(reply); + when(api.modifyVhostUserIf(any(ModifyVhostUserIf.class))).thenReturn(replyCS); + } + + private void whenModifyVhostUserIfThenSuccess() throws ExecutionException, InterruptedException { + whenModifyVhostUserIfThen(0); + } + + private void whenModifyVhostUserIfThenFailure() throws ExecutionException, InterruptedException { + whenModifyVhostUserIfThen(-1); + } + + private void whenDeleteVhostUserIfThen(final int retval) throws ExecutionException, InterruptedException { + final CompletionStage replyCS = mock(CompletionStage.class); + final CompletableFuture replyFuture = mock(CompletableFuture.class); + when(replyCS.toCompletableFuture()).thenReturn(replyFuture); + final DeleteVhostUserIfReply reply = new DeleteVhostUserIfReply(); + reply.retval = retval; + when(replyFuture.get()).thenReturn(reply); + when(api.deleteVhostUserIf(any(DeleteVhostUserIf.class))).thenReturn(replyCS); + } + + private void whenDeleteVhostUserIfThenSuccess() throws ExecutionException, InterruptedException { + whenDeleteVhostUserIfThen(0); + } + + private void whenDeleteVhostUserIfThenFailure() throws ExecutionException, InterruptedException { + whenDeleteVhostUserIfThen(-1); + } + + private CreateVhostUserIf verifyCreateVhostUserIfWasInvoked(final VhostUser vhostUser) { + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(CreateVhostUserIf.class); + verify(api).createVhostUserIf(argumentCaptor.capture()); + final CreateVhostUserIf actual = argumentCaptor.getValue(); + assertEquals(0, actual.customDevInstance); + + assertEquals(V3poUtils.booleanToByte(VhostUserRole.Server.equals(vhostUser.getRole())), actual.isServer); + assertEquals(0, actual.renumber); + assertEquals(0, actual.useCustomMac); + assertArrayEquals(vhostUser.getSocket().getBytes(), actual.sockFilename); + assertNotNull(actual.macAddress); + return actual; + } + + private ModifyVhostUserIf verifyModifyVhostUserIfWasInvoked(final VhostUser vhostUser, final int swIfIndex) { + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(ModifyVhostUserIf.class); + verify(api).modifyVhostUserIf(argumentCaptor.capture()); + final ModifyVhostUserIf actual = argumentCaptor.getValue(); + assertEquals(0, actual.customDevInstance); + + assertEquals(V3poUtils.booleanToByte(VhostUserRole.Server.equals(vhostUser.getRole())), actual.isServer); + assertEquals(0, actual.renumber); + assertEquals(swIfIndex, actual.swIfIndex); + assertArrayEquals(vhostUser.getSocket().getBytes(), actual.sockFilename); + return actual; + } + + private DeleteVhostUserIf verifyDeleteVhostUserIfWasInvoked(final int swIfIndex) { + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(DeleteVhostUserIf.class); + verify(api).deleteVhostUserIf(argumentCaptor.capture()); + final DeleteVhostUserIf actual = argumentCaptor.getValue(); + assertEquals(swIfIndex, actual.swIfIndex); + return actual; + } + + private static VhostUser generateVhostUser(final VhostUserRole role, final String socketName) { + VhostUserBuilder builder = new VhostUserBuilder(); + builder.setRole(role); + builder.setSocket(socketName); + return builder.build(); + } + + @Test + public void testWriteCurrentAttributes() throws Exception { + final VhostUser vhostUser = generateVhostUser(VhostUserRole.Server, "socketName"); + + whenCreateVhostUserIfThenSuccess(); + + customizer.writeCurrentAttributes(ID, vhostUser, ctx); + verifyCreateVhostUserIfWasInvoked(vhostUser); + assertTrue(namingContext.containsIndex(IFACE_NAME)); + } + + @Test + public void testWriteCurrentAttributesFailed() throws Exception { + final VhostUser vhostUser = generateVhostUser(VhostUserRole.Client, "socketName"); + + whenVxlanAddDelTunnelThenFailure(); + + try { + customizer.writeCurrentAttributes(ID, vhostUser, ctx); + } catch (WriteFailedException.CreateFailedException e) { + assertEquals(VppApiInvocationException.class, e.getCause().getClass()); + verifyCreateVhostUserIfWasInvoked(vhostUser); + assertFalse(namingContext.containsIndex(IFACE_NAME)); + return; + } + fail("WriteFailedException.CreateFailedException was expected"); + } + + @Test + public void testUpdateCurrentAttributes() throws Exception { + final VhostUser vhostUserBefore = generateVhostUser(VhostUserRole.Client, "socketName0"); + final VhostUser vhostUserAfter = generateVhostUser(VhostUserRole.Server, "socketName1"); + namingContext.addName(IFACE_ID, IFACE_NAME); + + whenModifyVhostUserIfThenSuccess(); + + customizer.updateCurrentAttributes(ID, vhostUserBefore, vhostUserAfter, ctx); + verifyModifyVhostUserIfWasInvoked(vhostUserAfter, IFACE_ID); + } + + @Test + public void testUpdateCurrentAttributesNoUpdate() throws Exception { + final VhostUser vhostUserBefore = generateVhostUser(VhostUserRole.Server, "socketName"); + final VhostUser vhostUserAfter = generateVhostUser(VhostUserRole.Server, "socketName"); + customizer.updateCurrentAttributes(ID, vhostUserBefore, vhostUserAfter, ctx); + verify(api, never()).modifyVhostUserIf(any(ModifyVhostUserIf.class)); + } + + @Test + public void testUpdateCurrentAttributesFailed() throws Exception { + final VhostUser vhostUserBefore = generateVhostUser(VhostUserRole.Client, "socketName0"); + final VhostUser vhostUserAfter = generateVhostUser(VhostUserRole.Server, "socketName1"); + namingContext.addName(IFACE_ID, IFACE_NAME); + + whenModifyVhostUserIfThenFailure(); + + try { + customizer.updateCurrentAttributes(ID, vhostUserBefore, vhostUserAfter, ctx); + } catch (WriteFailedException.UpdateFailedException e) { + assertEquals(VppApiInvocationException.class, e.getCause().getClass()); + verifyModifyVhostUserIfWasInvoked(vhostUserAfter, IFACE_ID); + return; + } + fail("WriteFailedException.UpdateFailedException was expected"); + } + + @Test + public void testDeleteCurrentAttributes() throws Exception { + final VhostUser vhostUser = generateVhostUser(VhostUserRole.Client, "socketName"); + namingContext.addName(IFACE_ID, IFACE_NAME); + + whenDeleteVhostUserIfThenSuccess(); + + customizer.deleteCurrentAttributes(ID, vhostUser, ctx); + verifyDeleteVhostUserIfWasInvoked(IFACE_ID); + assertFalse(namingContext.containsIndex(IFACE_NAME)); + } + + @Test + public void testDeleteCurrentAttributesFailed() throws Exception { + final VhostUser vhostUser = generateVhostUser(VhostUserRole.Client, "socketName"); + namingContext.addName(IFACE_ID, IFACE_NAME); + + whenDeleteVhostUserIfThenFailure(); + + try { + customizer.deleteCurrentAttributes(ID, vhostUser, ctx); + } catch (WriteFailedException.DeleteFailedException e) { + assertEquals(VppApiInvocationException.class, e.getCause().getClass()); + verifyDeleteVhostUserIfWasInvoked(IFACE_ID); + assertTrue(namingContext.containsIndex(IFACE_NAME)); + return; + } + fail("WriteFailedException.DeleteFailedException was expected"); + } +} \ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java index b28234db4..fcdca49b0 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java @@ -52,4 +52,10 @@ public class V3poUtilsTest { public void testParseMacNumberFormatEx() throws Exception { V3poUtils.parseMac("00:XX:7f:15:5e:77\""); } -} + + public void testBooleanToByte() { + assertEquals(0, V3poUtils.booleanToByte(null)); + assertEquals(0, V3poUtils.booleanToByte(false)); + assertEquals(1, V3poUtils.booleanToByte(true)); + } +} \ No newline at end of file -- cgit 1.2.3-korg