HONEYCOMB-36: vhost user interfaces CRUD support 49/1049/5
authorMaros Marsalek <mmarsale@cisco.com>
Tue, 10 May 2016 14:35:16 +0000 (16:35 +0200)
committerMaros Marsalek <mmarsale@cisco.com>
Wed, 11 May 2016 07:21:50 +0000 (09:21 +0200)
Change-Id: I1c16cf71fa450fe5079b4af8b1e24bdc921a6e82
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
v3po/api/src/main/yang/v3po.yang
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizer.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VxlanCustomizer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/TapCustomizer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/VhostUserCustomizer.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtils.java
v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesHoneycombWriterModule.java
v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/InterfacesStateHoneycombReaderModule.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/interfaces/VhostUserCustomizerTest.java [new file with mode: 0644]
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/utils/V3poUtilsTest.java

index 065ff65..e346ae1 100644 (file)
@@ -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 (file)
index 0000000..4066976
--- /dev/null
@@ -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;
+    }
+}
index a4a840e..b5ef6e0 100644 (file)
@@ -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);
         }
     }
index 62cac35..09ea09e 100644 (file)
@@ -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;
     }
 }
index a505436..1245f6a 100644 (file)
@@ -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 (file)
index 0000000..4b9c7c8
--- /dev/null
@@ -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
+    }
+}
index 392dc46..8742d1d 100644 (file)
@@ -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;
+    }
 }
index 2095f30..fb60635 100644 (file)
@@ -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);
index 13fcf00..3bd0e0c 100644 (file)
@@ -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 (file)
index 0000000..076df67
--- /dev/null
@@ -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
index b28234d..fcdca49 100644 (file)
@@ -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