diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-05-10 16:35:16 +0200 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-05-11 09:21:50 +0200 |
commit | e8b97ff12ea9aa475f38fa6c5eb20c997c2c54ed (patch) | |
tree | 09ac49946b93b1cbb66eb59c989f80a6ad49449b /v3po | |
parent | 44348596ff0c675bf3b799f88288111d92d771e6 (diff) |
HONEYCOMB-36: vhost user interfaces CRUD support
Change-Id: I1c16cf71fa450fe5079b4af8b1e24bdc921a6e82
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po')
11 files changed, 661 insertions, 35 deletions
diff --git a/v3po/api/src/main/yang/v3po.yang b/v3po/api/src/main/yang/v3po.yang index 065ff6535..e346ae144 100644 --- a/v3po/api/src/main/yang/v3po.yang +++ b/v3po/api/src/main/yang/v3po.yang @@ -49,7 +49,7 @@ module v3po { identity vhost-user { base if:interface-type; } - + identity tap { base if:interface-type; } @@ -118,6 +118,37 @@ module v3po { } } + grouping vhost-user-interface-base-attributes { + leaf socket { + type string { + length 1..255; + } + } + leaf role { + type vhost-user-role; + default "server"; + } + } + + grouping vhost-user-interface-state-attributes { + leaf features { + type uint64; + config false; + } + leaf virtio-net-hdr-size { + type uint32; + config false; + } + leaf num-memory-regions { + type uint32; + config false; + } + leaf connect-error { + type string; + config false; + } + } + augment /if:interfaces/if:interface { ext:augment-identifier "vpp-interface-augmentation"; @@ -125,10 +156,8 @@ module v3po { // 1. The link between interface type and this augmentation is unclear // 2. Only this augmentation with combination of ifc type is trigger to do something for vpp, what if user only configures base interface stuff ? + We need to get leaves defined by ietf-interfaces when we are processing this augment - // TODO grouping container tap { when "../if:type = 'v3po:tap'"; - uses tap-interface-base-attributes; uses tap-interface-config-attributes; } @@ -154,17 +183,8 @@ module v3po { } container vhost-user { when "../if:type = 'v3po:vhost-user'"; - leaf socket { - type string { - length 1..255; - } - } - leaf role { - type vhost-user-role; - default "server"; - } - description - "vhost-user settings"; + uses vhost-user-interface-base-attributes; + description "vhost-user settings"; } container vxlan { // FIXME: this should be in an vxlan-specific extension @@ -280,7 +300,11 @@ module v3po { container tap { when "../if:type = 'v3po:tap'"; - uses tap-interface-base-attributes; + uses tap-interface-base-attributes { + refine tap-name { + config false; + } + } } container ethernet { @@ -300,24 +324,15 @@ module v3po { } container vhost-user { when "../if:type = 'v3po:vhost-user'"; - leaf socket { - type string; - } - leaf role { - type vhost-user-role; - } - leaf features { - type uint64; - } - leaf virtio-net-hdr-size { - type uint32; - } - leaf num-memory-regions { - type uint32; - } - leaf connect-error { - type string; + uses vhost-user-interface-base-attributes { + refine socket { + config false; + } + refine role { + config false; + } } + uses vhost-user-interface-state-attributes; } container vxlan { when "../if:type = 'v3po:vxlan-tunnel'"; 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<VhostUser> { + + 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<VhostUser> extract(@Nonnull final InstanceIdentifier<VhostUser> currentId, + @Nonnull final DataObject parentData) { + return Optional.fromNullable(((VppInterfaceAugmentation) parentData).getVhostUser()); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<VhostUser> 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<CreateVhostUserIfReply> 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<VhostUser> 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<ModifyVhostUserIfReply> 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<VhostUser> 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<DeleteVhostUserIfReply> 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<Integer, SwInterfaceTapDetails> mappedTaps = (Map<Integer, SwInterfaceTapDetails>) 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<VhostUser, VhostUserBuilder> { + + 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<? extends DataObject> parentBuilder, + @Nonnull VhostUser readValue) { + ((VppInterfaceStateAugmentationBuilder) parentBuilder).setVhostUser(readValue); + } + + @Nonnull + @Override + public VhostUserBuilder getBuilder( + @Nonnull InstanceIdentifier<VhostUser> id) { + return new VhostUserBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VhostUser> id, + @Nonnull final VhostUserBuilder builder, + @Nonnull final Context ctx) throws ReadFailedException { + final InterfaceKey key = id.firstKeyOf(Interface.class); + + @SuppressWarnings("unchecked") + Map<Integer, SwInterfaceVhostUserDetails> mappedVhostUsers = + (Map<Integer, SwInterfaceVhostUserDetails>) 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<SwInterfaceVhostUserDetailsReplyDump> swInterfaceVhostUserDetailsReplyDumpCompletionStage = + getFutureJVpp().swInterfaceVhostUserDump(request); + final SwInterfaceVhostUserDetailsReplyDump reply = + V3poUtils.getReply(swInterfaceVhostUserDetailsReplyDumpCompletionStage.toCompletableFuture()); + + if(null == reply || null == reply.swInterfaceVhostUserDetails) { + mappedVhostUsers = Collections.emptyMap(); + } else { + final List<SwInterfaceVhostUserDetails> 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<Vxlan> vxlanWriter = new CompositeChildWriter<>(Vxlan.class, - new VxlanCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); + new VxlanCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); + + final ChildWriter<VhostUser> vhostUserWriter = new CompositeChildWriter<>(VhostUser.class, + new VhostUserCustomizer(getVppJvppIfcDependency(), getInterfaceContextDependency())); final ChildWriter<Tap> 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<ChildWriter<? extends ChildOf<VppInterfaceAugmentation>>> 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<? extends ChildOf<VppInterfaceStateAugmentation>> vhostUserReader = + new CompositeChildReader<>(VhostUser.class, + new VhostUserCustomizer(getVppJvppDependency(), getInterfaceContextIfcStateDependency())); + final List<ChildReader<? extends ChildOf<VppInterfaceStateAugmentation>>> childReaders = Lists.newArrayList(); childReaders.add(ethernetReader); childReaders.add(tapReader); + childReaders.add(vhostUserReader); final ChildReader<VppInterfaceStateAugmentation> 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<VhostUser> 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<CreateVhostUserIfReply> replyCS = mock(CompletionStage.class); + final CompletableFuture<CreateVhostUserIfReply> 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<ModifyVhostUserIfReply> replyCS = mock(CompletionStage.class); + final CompletableFuture<ModifyVhostUserIfReply> 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<DeleteVhostUserIfReply> replyCS = mock(CompletionStage.class); + final CompletableFuture<DeleteVhostUserIfReply> 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<CreateVhostUserIf> 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<ModifyVhostUserIf> 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<DeleteVhostUserIf> 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 |