This is an automated email from the ASF dual-hosted git repository.

nvazquez pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/master by this push:
     new 4de4eab  Enable DPDK support on KVM (#2839)
4de4eab is described below

commit 4de4eabd18386fdb5d4242f371ec780f6d9097a5
Author: Nicolas Vazquez <nicovazque...@gmail.com>
AuthorDate: Wed Nov 7 09:29:01 2018 -0300

    Enable DPDK support on KVM (#2839)
    
    * Enable DPDK support on KVM
    
    * Allow DPDK deployments on user VMs only
    
    * Fix port name ordering
---
 agent/conf/agent.properties                        |  4 +
 .../main/java/com/cloud/agent/api/to/NicTO.java    |  9 +++
 .../com/cloud/agent/api/to/VirtualMachineTO.java   |  8 ++
 .../org/apache/cloudstack/api/ApiConstants.java    |  1 +
 .../api/command/user/vm/DeployVMCmd.java           |  7 ++
 .../api/command/user/vm/UpdateVMCmd.java           |  6 ++
 .../com/cloud/vm/VirtualMachineManagerImpl.java    | 12 +++
 .../hypervisor/kvm/resource/BridgeVifDriver.java   |  2 +-
 .../hypervisor/kvm/resource/DirectVifDriver.java   |  5 +-
 .../hypervisor/kvm/resource/IvsVifDriver.java      |  2 +-
 .../kvm/resource/LibvirtComputingResource.java     | 81 ++++++++++++++++-----
 .../kvm/resource/LibvirtDomainXMLParser.java       |  5 ++
 .../hypervisor/kvm/resource/LibvirtVMDef.java      | 35 ++++++++-
 .../hypervisor/kvm/resource/OvsVifDriver.java      | 84 +++++++++++++++++++--
 .../cloud/hypervisor/kvm/resource/VifDriver.java   |  2 +-
 .../hypervisor/kvm/resource/VifDriverBase.java     |  2 +-
 .../wrapper/LibvirtPlugNicCommandWrapper.java      |  2 +-
 .../LibvirtPrepareForMigrationCommandWrapper.java  |  2 +-
 .../wrapper/LibvirtReplugNicCommandWrapper.java    |  2 +-
 .../wrapper/LibvirtStartCommandWrapper.java        |  1 +
 .../kvm/resource/LibvirtComputingResourceTest.java | 32 +++++++-
 .../hypervisor/kvm/resource/OvsVifDriverTest.java  | 75 +++++++++++++++++++
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  | 85 +++++++++++++++++++++-
 23 files changed, 422 insertions(+), 42 deletions(-)

diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index ad35b96..6b68568 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -108,6 +108,10 @@ domr.scripts.dir=scripts/network/domr/kvm
 # openvswitch = com.cloud.hypervisor.kvm.resource.OvsVifDriver
 #libvirt.vif.driver=com.cloud.hypervisor.kvm.resource.BridgeVifDriver
 
+# Set DPDK Support on OpenVswitch
+#openvswitch.dpdk.enabled=true
+#openvswitch.dpdk.ovs.path=/var/run/openvswitch
+
 # set the hypervisor type, values are: kvm, lxc
 hypervisor.type=kvm
 
diff --git a/api/src/main/java/com/cloud/agent/api/to/NicTO.java 
b/api/src/main/java/com/cloud/agent/api/to/NicTO.java
index 3863e1b..4861225 100644
--- a/api/src/main/java/com/cloud/agent/api/to/NicTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/NicTO.java
@@ -30,6 +30,7 @@ public class NicTO extends NetworkTO {
     String nicUuid;
     List<String> nicSecIps;
     Map<NetworkOffering.Detail, String> details;
+    boolean dpdkDisabled;
 
     public NicTO() {
         super();
@@ -109,4 +110,12 @@ public class NicTO extends NetworkTO {
     public void setDetails(final Map<NetworkOffering.Detail, String> details) {
         this.details = details;
     }
+
+    public boolean isDpdkDisabled() {
+        return dpdkDisabled;
+    }
+
+    public void setDpdkDisabled(boolean dpdkDisabled) {
+        this.dpdkDisabled = dpdkDisabled;
+    }
 }
diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java 
b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
index 84a6bf5..f977a1c 100644
--- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
@@ -73,6 +73,7 @@ public class VirtualMachineTO {
     Double cpuQuotaPercentage = null;
 
     Map<String, String> guestOsDetails = new HashMap<String, String>();
+    Map<String, String> extraConfig = new HashMap<>();
 
     public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type 
type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType 
bootloader,
             String os, boolean enableHA, boolean limitCpuUse, String 
vncPassword) {
@@ -350,4 +351,11 @@ public class VirtualMachineTO {
     public void setCpuQuotaPercentage(Double cpuQuotaPercentage) {
         this.cpuQuotaPercentage = cpuQuotaPercentage;
     }
+
+    public void addExtraConfig(String key, String value) {
+        extraConfig.put(key, value);
+    }
+    public Map<String, String> getExtraConfig() {
+        return extraConfig;
+    }
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java 
b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index b7779cb..5fa03a7 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -117,6 +117,7 @@ public class ApiConstants {
     public static final String END_PORT = "endport";
     public static final String ENTRY_TIME = "entrytime";
     public static final String EXPIRES = "expires";
+    public static final String EXTRA_CONFIG = "extraconfig";
     public static final String EXTRA_DHCP_OPTION = "extradhcpoption";
     public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname";
     public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode";
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 29d4c97..0874b4e 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -200,6 +200,9 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd 
implements SecurityG
             " an optional parameter used to create additional data disks from 
datadisk templates; can't be specified with diskOfferingId parameter")
     private Map dataDiskTemplateToDiskOfferingList;
 
+    @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, 
since = "4.12", description = "an optional URL encoded string that can be 
passed to the virtual machine upon successful deployment", length = 5120)
+    private String extraConfig;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -482,6 +485,10 @@ public class DeployVMCmd extends 
BaseAsyncCreateCustomIdCmd implements SecurityG
         return dataDiskTemplateToDiskOfferingMap;
     }
 
+    public String getExtraConfig() {
+        return extraConfig;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
index 9e4e6b1..b040f79 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
@@ -128,6 +128,8 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements 
SecurityGroupAction
             + " Example: 
dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com")
     private Map dhcpOptionsNetworkList;
 
+    @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, 
since = "4.12", description = "an optional URL encoded string that can be 
passed to the virtual machine upon successful deployment", authorized = { 
RoleType.Admin }, length = 5120)
+    private String extraConfig;
 
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
@@ -221,6 +223,10 @@ public class UpdateVMCmd extends BaseCustomIdCmd 
implements SecurityGroupAction
         return dhcpOptionsMap;
     }
 
+    public String getExtraConfig() {
+        return extraConfig;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git 
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
 
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index 9e8e227..7c4a2ef 100755
--- 
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ 
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -39,6 +39,7 @@ import java.util.concurrent.TimeUnit;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
 import org.apache.cloudstack.ca.CAManager;
 import org.apache.cloudstack.context.CallContext;
@@ -1112,6 +1113,8 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
 
                     vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx);
 
+                    addExtraConfig(vmTO);
+
                     work = _workDao.findById(work.getId());
                     if (work == null || work.getStep() != Step.Prepare) {
                         throw new ConcurrentOperationException("Work steps 
have been changed: " + work);
@@ -1276,6 +1279,15 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
         }
     }
 
+    private void addExtraConfig(VirtualMachineTO vmTO) {
+        Map<String, String> details = vmTO.getDetails();
+        for (String key : details.keySet()) {
+            if (key.startsWith(ApiConstants.EXTRA_CONFIG)) {
+                vmTO.addExtraConfig(key, details.get(key));
+            }
+        }
+    }
+
     // for managed storage on KVM, need to make sure the path field of the 
volume in question is populated with the IQN
     private void handlePath(final DiskTO[] disks, final HypervisorType 
hypervisorType) {
         if (hypervisorType != HypervisorType.KVM) {
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
index 88e473d..f6ca0a8 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
@@ -207,7 +207,7 @@ public class BridgeVifDriver extends VifDriverBase {
     }
 
     @Override
-    public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, 
String nicAdapter) throws InternalErrorException, LibvirtException {
+    public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, 
String nicAdapter, Map<String, String> extraConfig) throws 
InternalErrorException, LibvirtException {
 
         if (s_logger.isDebugEnabled()) {
             s_logger.debug("nic=" + nic);
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/DirectVifDriver.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/DirectVifDriver.java
index b8763fa..de65a37 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/DirectVifDriver.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/DirectVifDriver.java
@@ -26,6 +26,8 @@ import com.cloud.agent.api.to.NicTO;
 import com.cloud.exception.InternalErrorException;
 import com.cloud.network.Networks;
 
+import java.util.Map;
+
 public class DirectVifDriver extends VifDriverBase {
 
     private static final Logger s_logger = 
Logger.getLogger(DirectVifDriver.class);
@@ -36,12 +38,13 @@ public class DirectVifDriver extends VifDriverBase {
      *
      * @param nic
      * @param guestOsType
+     * @param extraConfig
      * @return
      * @throws InternalErrorException
      * @throws LibvirtException
      */
     @Override
-    public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, 
String nicAdapter) throws InternalErrorException, LibvirtException {
+    public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, 
String nicAdapter, Map<String, String> extraConfig) throws 
InternalErrorException, LibvirtException {
         LibvirtVMDef.InterfaceDef intf = new LibvirtVMDef.InterfaceDef();
 
         if (nic.getType() == Networks.TrafficType.Guest) {
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java
index 5f21b96..4ba0114 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java
@@ -77,7 +77,7 @@ public class IvsVifDriver extends VifDriverBase {
     }
 
     @Override
-    public InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter) 
throws InternalErrorException, LibvirtException {
+    public InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter, 
Map<String, String> extraConfig) throws InternalErrorException, 
LibvirtException {
         LibvirtVMDef.InterfaceDef intf = new LibvirtVMDef.InterfaceDef();
 
         String vNetId = null;
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 9d1924f..26fcd01 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -48,6 +48,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
 import com.cloud.resource.RequestWrapper;
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
 import org.apache.cloudstack.storage.to.TemplateObjectTO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
@@ -56,6 +57,7 @@ import org.apache.cloudstack.utils.linux.CPUStat;
 import org.apache.cloudstack.utils.linux.MemStat;
 import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
 import org.apache.cloudstack.utils.security.KeyStoreUtils;
+import org.apache.commons.collections.MapUtils;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.ArrayUtils;
@@ -521,6 +523,12 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
 
     protected StorageSubsystemCommandHandler storageHandler;
 
+    protected boolean dpdkSupport = false;
+    protected String dpdkOvsPath;
+    protected static final String DPDK_NUMA = ApiConstants.EXTRA_CONFIG + 
"-dpdk-numa";
+    protected static final String DPDK_HUGE_PAGES = ApiConstants.EXTRA_CONFIG 
+ "-dpdk-hugepages";
+    protected static final String DPDK_INTERFACE_PREFIX = 
ApiConstants.EXTRA_CONFIG + "-dpdk-interface-";
+
     private String getEndIpFromStartIp(final String startIp, final int numIps) 
{
         final String[] tokens = startIp.split("[.]");
         assert tokens.length == 4;
@@ -637,6 +645,15 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
             _bridgeType = BridgeType.valueOf(bridgeType.toUpperCase());
         }
 
+        String dpdk = (String) params.get("openvswitch.dpdk.enabled");
+        if (_bridgeType == BridgeType.OPENVSWITCH && 
Boolean.parseBoolean(dpdk)) {
+            dpdkSupport = true;
+            dpdkOvsPath = (String) params.get("openvswitch.dpdk.ovs.path");
+            if (dpdkOvsPath != null && !dpdkOvsPath.endsWith("/")) {
+                dpdkOvsPath += "/";
+            }
+        }
+
         params.put("domr.scripts.dir", domrScriptsDir);
 
         _virtRouterResource = new VirtualRoutingResource(this);
@@ -1634,7 +1651,7 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         }
 
         final Domain vm = getDomain(conn, vmName);
-        vm.attachDevice(getVifDriver(nicTO.getType()).plug(nicTO, "Other PV", 
"").toString());
+        vm.attachDevice(getVifDriver(nicTO.getType()).plug(nicTO, "Other PV", 
"", null).toString());
     }
 
 
@@ -2039,6 +2056,11 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         vm.setDomDescription(vmTO.getOs());
         vm.setPlatformEmulator(vmTO.getPlatformEmulator());
 
+        Map<String, String> extraConfig = vmTO.getExtraConfig();
+        if (dpdkSupport && (!extraConfig.containsKey(DPDK_NUMA) || 
!extraConfig.containsKey(DPDK_HUGE_PAGES))) {
+            s_logger.info("DPDK is enabled but it needs extra configurations 
for CPU NUMA and Huge Pages for VM deployment");
+        }
+
         final GuestDef guest = new GuestDef();
 
         if (HypervisorType.LXC == _hypervisorType && VirtualMachine.Type.User 
== vmTO.getType()) {
@@ -2072,21 +2094,23 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         grd.setVcpuNum(vcpus);
         vm.addComp(grd);
 
-        final CpuModeDef cmd = new CpuModeDef();
-        cmd.setMode(_guestCpuMode);
-        cmd.setModel(_guestCpuModel);
-        if (vmTO.getType() == VirtualMachine.Type.User) {
-            cmd.setFeatures(_cpuFeatures);
-        }
-        // multi cores per socket, for larger core configs
-        if (vcpus % 6 == 0) {
-            final int sockets = vcpus / 6;
-            cmd.setTopology(6, sockets);
-        } else if (vcpus % 4 == 0) {
-            final int sockets = vcpus / 4;
-            cmd.setTopology(4, sockets);
+        if (!extraConfig.containsKey(DPDK_NUMA)) {
+            final CpuModeDef cmd = new CpuModeDef();
+            cmd.setMode(_guestCpuMode);
+            cmd.setModel(_guestCpuModel);
+            if (vmTO.getType() == VirtualMachine.Type.User) {
+                cmd.setFeatures(_cpuFeatures);
+            }
+            // multi cores per socket, for larger core configs
+            if (vcpus % 6 == 0) {
+                final int sockets = vcpus / 6;
+                cmd.setTopology(6, sockets);
+            } else if (vcpus % 4 == 0) {
+                final int sockets = vcpus / 4;
+                cmd.setTopology(4, sockets);
+            }
+            vm.addComp(cmd);
         }
-        vm.addComp(cmd);
 
         if (_hypervisorLibvirtVersion >= 9000) {
             final CpuTuneDef ctd = new CpuTuneDef();
@@ -2192,9 +2216,29 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
 
         vm.addComp(devices);
 
+        addExtraConfigComponent(extraConfig, vm);
+
         return vm;
     }
 
+    /**
+     * Add extra configurations (if any) as a String component to the domain 
XML
+     */
+    protected void addExtraConfigComponent(Map<String, String> extraConfig, 
LibvirtVMDef vm) {
+        if (MapUtils.isNotEmpty(extraConfig)) {
+            StringBuilder extraConfigBuilder = new StringBuilder();
+            for (String key : extraConfig.keySet()) {
+                if (!key.startsWith(DPDK_INTERFACE_PREFIX)) {
+                    extraConfigBuilder.append(extraConfig.get(key));
+                }
+            }
+            String comp = extraConfigBuilder.toString();
+            if (org.apache.commons.lang.StringUtils.isNotBlank(comp)) {
+                vm.addComp(comp);
+            }
+        }
+    }
+
     public void createVifs(final VirtualMachineTO vmSpec, final LibvirtVMDef 
vm) throws InternalErrorException, LibvirtException {
         final NicTO[] nics = vmSpec.getNics();
         final Map <String, String> params = vmSpec.getDetails();
@@ -2202,10 +2246,11 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         if (params != null && params.get("nicAdapter") != null && 
!params.get("nicAdapter").isEmpty()) {
             nicAdapter = params.get("nicAdapter");
         }
+        Map<String, String> extraConfig = vmSpec.getExtraConfig();
         for (int i = 0; i < nics.length; i++) {
             for (final NicTO nic : vmSpec.getNics()) {
                 if (nic.getDeviceId() == i) {
-                    createVif(vm, nic, nicAdapter);
+                    createVif(vm, nic, nicAdapter, extraConfig);
                 }
             }
         }
@@ -2400,7 +2445,7 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
 
     }
 
-    private void createVif(final LibvirtVMDef vm, final NicTO nic, final 
String nicAdapter) throws InternalErrorException, LibvirtException {
+    private void createVif(final LibvirtVMDef vm, final NicTO nic, final 
String nicAdapter, Map<String, String> extraConfig) throws 
InternalErrorException, LibvirtException {
 
         if (nic.getType().equals(TrafficType.Guest) && 
nic.getBroadcastType().equals(BroadcastDomainType.Vsp)) {
             String vrIp = nic.getBroadcastUri().getPath().substring(1);
@@ -2415,7 +2460,7 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
             s_logger.error("LibvirtVMDef object get devices with null result");
             throw new InternalErrorException("LibvirtVMDef object get devices 
with null result");
         }
-        vm.getDevices().addDevice(getVifDriver(nic.getType(), 
nic.getName()).plug(nic, vm.getPlatformEmulator(), nicAdapter));
+        vm.getDevices().addDevice(getVifDriver(nic.getType(), 
nic.getName()).plug(nic, vm.getPlatformEmulator(), nicAdapter, extraConfig));
     }
 
     public boolean cleanupDisk(Map<String, String> volumeToDisconnect) {
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
index 2eeff88..d4ca44b 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
@@ -182,6 +182,11 @@ public class LibvirtDomainXMLParser {
                 } else if (type.equalsIgnoreCase("ethernet")) {
                     String scriptPath = getAttrValue("script", "path", nic);
                     def.defEthernet(dev, mac, 
NicModel.valueOf(model.toUpperCase()), scriptPath, networkRateKBps);
+                } else if (type.equals("vhostuser")) {
+                    String sourcePort = getAttrValue("source", "path", nic);
+                    String[] sourcePathParts = sourcePort.split("/");
+                    String port = sourcePathParts[sourcePathParts.length - 1];
+                    def.setDpdkSourcePort(port);
                 }
 
                 if (StringUtils.isNotBlank(slot)) {
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index 1e49cb5..e0089dc 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.google.common.collect.Maps;
@@ -900,7 +901,7 @@ public class LibvirtVMDef {
 
     public static class InterfaceDef {
         enum GuestNetType {
-            BRIDGE("bridge"), DIRECT("direct"), NETWORK("network"), 
USER("user"), ETHERNET("ethernet"), INTERNAL("internal");
+            BRIDGE("bridge"), DIRECT("direct"), NETWORK("network"), 
USER("user"), ETHERNET("ethernet"), INTERNAL("internal"), 
VHOSTUSER("vhostuser");
             String _type;
 
             GuestNetType(String type) {
@@ -933,7 +934,7 @@ public class LibvirtVMDef {
 
         private GuestNetType _netType; /*
          * bridge, ethernet, network, user,
-         * internal
+         * internal, vhostuser
          */
         private HostNicType _hostNetType; /* Only used by agent java code */
         private String _netSourceMode;
@@ -950,6 +951,9 @@ public class LibvirtVMDef {
         private boolean _pxeDisable = false;
         private boolean _linkStateUp = true;
         private Integer _slot;
+        private String _dpdkSourcePath;
+        private String _dpdkSourcePort;
+        private String _dpdkExtraLines;
 
         public void defBridgeNet(String brName, String targetBrName, String 
macAddr, NicModel model) {
             defBridgeNet(brName, targetBrName, macAddr, model, 0);
@@ -964,6 +968,16 @@ public class LibvirtVMDef {
             _networkRateKBps = networkRateKBps;
         }
 
+        public void defDpdkNet(String dpdkSourcePath, String dpdkPort, String 
macAddress, NicModel model, Integer networkRateKBps, String extra) {
+            _netType = GuestNetType.VHOSTUSER;
+            _dpdkSourcePath = dpdkSourcePath;
+            _dpdkSourcePort = dpdkPort;
+            _macAddr = macAddress;
+            _model = model;
+            _networkRateKBps = networkRateKBps;
+            _dpdkExtraLines = extra;
+        }
+
         public void defDirectNet(String sourceName, String targetName, String 
macAddr, NicModel model, String sourceMode) {
             defDirectNet(sourceName, targetName, macAddr, model, sourceMode, 
0);
         }
@@ -1089,6 +1103,13 @@ public class LibvirtVMDef {
             return _linkStateUp;
         }
 
+        public String getDpdkSourcePort() {
+            return _dpdkSourcePort;
+        }
+        public void setDpdkSourcePort(String port) {
+            _dpdkSourcePort = port;
+        }
+
         @Override
         public String toString() {
             StringBuilder netBuilder = new StringBuilder();
@@ -1099,6 +1120,8 @@ public class LibvirtVMDef {
                 netBuilder.append("<source network='" + _sourceName + "'/>\n");
             } else if (_netType == GuestNetType.DIRECT) {
                 netBuilder.append("<source dev='" + _sourceName + "' mode='" + 
_netSourceMode + "'/>\n");
+            } else if (_netType == GuestNetType.VHOSTUSER) {
+                netBuilder.append("<source type='unix' path='"+ 
_dpdkSourcePath + _dpdkSourcePort + "' mode='client'/>\n");
             }
             if (_networkName != null) {
                 netBuilder.append("<target dev='" + _networkName + "'/>\n");
@@ -1132,7 +1155,13 @@ public class LibvirtVMDef {
                 netBuilder.append("<vlan trunk='no'>\n<tag id='" + _vlanTag + 
"'/>\n</vlan>");
             }
 
-            netBuilder.append("<link state='" + (_linkStateUp ? "up" : "down") 
+"'/>\n");
+            if (StringUtils.isNotBlank(_dpdkExtraLines)) {
+                netBuilder.append(_dpdkExtraLines);
+            }
+
+            if (_netType != GuestNetType.VHOSTUSER) {
+                netBuilder.append("<link state='" + (_linkStateUp ? "up" : 
"down") +"'/>\n");
+            }
 
             if (_slot  != null) {
                 netBuilder.append(String.format("<address type='pci' 
domain='0x0000' bus='0x00' slot='0x%02x' function='0x0'/>\n", _slot));
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
index 06cd161..ea4fd4a 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
@@ -24,6 +24,8 @@ import java.util.Map;
 
 import javax.naming.ConfigurationException;
 
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 import org.libvirt.LibvirtException;
 
@@ -40,6 +42,8 @@ public class OvsVifDriver extends VifDriverBase {
     private static final Logger s_logger = 
Logger.getLogger(OvsVifDriver.class);
     private int _timeout;
 
+    protected static final String DPDK_PORT_PREFIX = "csdpdk-";
+
     @Override
     public void configure(Map<String, Object> params) throws 
ConfigurationException {
         super.configure(params);
@@ -76,12 +80,65 @@ public class OvsVifDriver extends VifDriverBase {
         s_logger.debug("done looking for pifs, no more bridges");
     }
 
+    /**
+     * Get the latest DPDK port number created on a DPDK enabled host
+     */
+    protected int getDpdkLatestPortNumberUsed() {
+        s_logger.debug("Checking the last DPDK port created");
+        String cmd = "ovs-vsctl show | grep Port | grep " + DPDK_PORT_PREFIX + 
" | " +
+                "awk '{ print $2 }' | sort -rV | head -1";
+        String port = Script.runSimpleBashScript(cmd);
+        int portNumber = 0;
+        if (StringUtils.isNotBlank(port)) {
+            String unquotedPort = port.replace("\"", "");
+            String dpdkPortNumber = unquotedPort.split(DPDK_PORT_PREFIX)[1];
+            portNumber = Integer.valueOf(dpdkPortNumber);
+        }
+        return portNumber;
+    }
+
+    /**
+     * Get the next DPDK port name to be created
+     */
+    protected String getNextDpdkPort() {
+        int portNumber = getDpdkLatestPortNumberUsed();
+        return DPDK_PORT_PREFIX + String.valueOf(portNumber + 1);
+    }
+
+    /**
+     * Add OVS port (if it does not exist) to bridge with DPDK support
+     */
+    protected void addDpdkPort(String bridgeName, String port, String vlan) {
+        String cmd = String.format("ovs-vsctl add-port %s %s " +
+                "vlan_mode=access tag=%s " +
+                "-- set Interface %s type=dpdkvhostuser", bridgeName, port, 
vlan, port);
+        s_logger.debug("DPDK property enabled, executing: " + cmd);
+        Script.runSimpleBashScript(cmd);
+    }
+
+    /**
+     * Check for additional extra 'dpdk-interface' configurations, return them 
appended
+     */
+    private String getExtraDpdkProperties(Map<String, String> extraConfig) {
+        StringBuilder stringBuilder = new StringBuilder();
+        for (String key : extraConfig.keySet()) {
+            if 
(key.startsWith(LibvirtComputingResource.DPDK_INTERFACE_PREFIX)) {
+                stringBuilder.append(extraConfig.get(key));
+            }
+        }
+        return stringBuilder.toString();
+    }
+
     @Override
-    public InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter) 
throws InternalErrorException, LibvirtException {
+    public InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter, 
Map<String, String> extraConfig) throws InternalErrorException, 
LibvirtException {
         s_logger.debug("plugging nic=" + nic);
 
         LibvirtVMDef.InterfaceDef intf = new LibvirtVMDef.InterfaceDef();
-        intf.setVirtualPortType("openvswitch");
+        if (!_libvirtComputingResource.dpdkSupport || nic.isDpdkDisabled()) {
+            // Let libvirt handle OVS ports creation when DPDK property is 
disabled or when it is enabled but disabled for the nic
+            // For DPDK support, libvirt does not handle ports creation, 
invoke 'addDpdkPort' method
+            intf.setVirtualPortType("openvswitch");
+        }
 
         String vlanId = null;
         String logicalSwitchUuid = null;
@@ -99,9 +156,19 @@ public class OvsVifDriver extends VifDriverBase {
             if ((nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan 
|| nic.getBroadcastType() == Networks.BroadcastDomainType.Pvlan) &&
                     !vlanId.equalsIgnoreCase("untagged")) {
                 if (trafficLabel != null && !trafficLabel.isEmpty()) {
-                    s_logger.debug("creating a vlan dev and bridge for guest 
traffic per traffic label " + trafficLabel);
-                    intf.defBridgeNet(_pifs.get(trafficLabel), null, 
nic.getMac(), getGuestNicModel(guestOsType, nicAdapter), networkRateKBps);
-                    intf.setVlanTag(Integer.parseInt(vlanId));
+                    if (_libvirtComputingResource.dpdkSupport && 
!nic.isDpdkDisabled()) {
+                        s_logger.debug("DPDK support enabled: configuring per 
traffic label " + trafficLabel);
+                        if 
(StringUtils.isBlank(_libvirtComputingResource.dpdkOvsPath)) {
+                            throw new CloudRuntimeException("DPDK is enabled 
on the host but no OVS path has been provided");
+                        }
+                        String port = getNextDpdkPort();
+                        addDpdkPort(_pifs.get(trafficLabel), port, vlanId);
+                        intf.defDpdkNet(_libvirtComputingResource.dpdkOvsPath, 
port, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter), 0, 
getExtraDpdkProperties(extraConfig));
+                    } else {
+                        s_logger.debug("creating a vlan dev and bridge for 
guest traffic per traffic label " + trafficLabel);
+                        intf.defBridgeNet(_pifs.get(trafficLabel), null, 
nic.getMac(), getGuestNicModel(guestOsType, nicAdapter), networkRateKBps);
+                        intf.setVlanTag(Integer.parseInt(vlanId));
+                    }
                 } else {
                     intf.defBridgeNet(_pifs.get("private"), null, 
nic.getMac(), getGuestNicModel(guestOsType, nicAdapter), networkRateKBps);
                     intf.setVlanTag(Integer.parseInt(vlanId));
@@ -153,6 +220,13 @@ public class OvsVifDriver extends VifDriverBase {
     @Override
     public void unplug(InterfaceDef iface) {
         // Libvirt apparently takes care of this, see BridgeVifDriver unplug
+        if (_libvirtComputingResource.dpdkSupport) {
+            // If DPDK is enabled, we'll need to cleanup the port as libvirt 
won't
+            String dpdkPort = iface.getDpdkSourcePort();
+            String cmd = String.format("ovs-vsctl del-port %s", dpdkPort);
+            s_logger.debug("Removing DPDK port: " + dpdkPort);
+            Script.runSimpleBashScript(cmd);
+        }
     }
 
 
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/VifDriver.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/VifDriver.java
index 387a552..1016fcc 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/VifDriver.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/VifDriver.java
@@ -32,7 +32,7 @@ public interface VifDriver {
 
     public void configure(Map<String, Object> params) throws 
ConfigurationException;
 
-    public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, 
String nicAdapter) throws InternalErrorException, LibvirtException;
+    public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, 
String nicAdapter, Map<String, String> extraConfig) throws 
InternalErrorException, LibvirtException;
 
     public void unplug(LibvirtVMDef.InterfaceDef iface);
 
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/VifDriverBase.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/VifDriverBase.java
index dad73f2..95e38f0 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/VifDriverBase.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/VifDriverBase.java
@@ -42,7 +42,7 @@ public abstract class VifDriverBase implements VifDriver {
     }
 
     @Override
-    public abstract LibvirtVMDef.InterfaceDef plug(NicTO nic, String 
guestOsType, String nicAdapter) throws InternalErrorException, LibvirtException;
+    public abstract LibvirtVMDef.InterfaceDef plug(NicTO nic, String 
guestOsType, String nicAdapter, Map<String, String> extraConfig) throws 
InternalErrorException, LibvirtException;
 
     @Override
     public abstract void unplug(LibvirtVMDef.InterfaceDef iface);
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java
index 2ee9b95..1ef32af 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java
@@ -61,7 +61,7 @@ public final class LibvirtPlugNicCommandWrapper extends 
CommandWrapper<PlugNicCo
                 nicnum++;
             }
             final VifDriver vifDriver = 
libvirtComputingResource.getVifDriver(nic.getType(), nic.getName());
-            final InterfaceDef interfaceDef = vifDriver.plug(nic, "Other PV", 
"");
+            final InterfaceDef interfaceDef = vifDriver.plug(nic, "Other PV", 
"", null);
             vm.attachDevice(interfaceDef.toString());
 
             return new PlugNicAnswer(command, true, "success");
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
index ac9f884..5c9980b 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
@@ -64,7 +64,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper 
extends CommandWrapp
 
             final Connect conn = 
libvirtUtilitiesHelper.getConnectionByVmName(vm.getName());
             for (final NicTO nic : nics) {
-                libvirtComputingResource.getVifDriver(nic.getType(), 
nic.getName()).plug(nic, null, "");
+                libvirtComputingResource.getVifDriver(nic.getType(), 
nic.getName()).plug(nic, null, "", null);
             }
 
             /* setup disks, e.g for iso */
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java
index c91e719..7ee9171 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java
@@ -65,7 +65,7 @@ public final class LibvirtReplugNicCommandWrapper extends 
CommandWrapper<ReplugN
             InterfaceDef oldPluggedNic = 
findPluggedNic(libvirtComputingResource, nic, vmName, conn);
 
             final VifDriver newVifDriver = 
libvirtComputingResource.getVifDriver(nic.getType(), nic.getName());
-            final InterfaceDef interfaceDef = newVifDriver.plug(nic, "Other 
PV", oldPluggedNic.getModel().toString());
+            final InterfaceDef interfaceDef = newVifDriver.plug(nic, "Other 
PV", oldPluggedNic.getModel().toString(), null);
 
             interfaceDef.setSlot(oldPluggedNic.getSlot());
             interfaceDef.setDevName(oldPluggedNic.getDevName());
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java
index fd5f2fa..fd705e4 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java
@@ -69,6 +69,7 @@ public final class LibvirtStartCommandWrapper extends 
CommandWrapper<StartComman
             for (final NicTO nic : nics) {
                 if (vmSpec.getType() != VirtualMachine.Type.User) {
                     nic.setPxeDisable(true);
+                    nic.setDpdkDisabled(true);
                 }
             }
 
diff --git 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index adadc1f..97d14cf 100644
--- 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -26,7 +26,9 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 import java.util.UUID;
 import java.util.Vector;
@@ -176,8 +178,10 @@ 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.doNothing;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -189,6 +193,8 @@ public class LibvirtComputingResourceTest {
     private LibvirtComputingResource libvirtComputingResource;
     @Mock
     VirtualMachineTO vmTO;
+    @Mock
+    LibvirtVMDef vmDef;
 
     String hyperVisorType = "kvm";
     Random random = new Random();
@@ -3165,7 +3171,7 @@ public class LibvirtComputingResourceTest {
 
             when(libvirtComputingResource.getVifDriver(nic.getType(), 
nic.getName())).thenReturn(vifDriver);
 
-            when(vifDriver.plug(nic, "Other PV", "")).thenReturn(interfaceDef);
+            when(vifDriver.plug(nic, "Other PV", "", 
null)).thenReturn(interfaceDef);
             when(interfaceDef.toString()).thenReturn("Interface");
 
             final String interfaceDefStr = interfaceDef.toString();
@@ -3188,7 +3194,7 @@ public class LibvirtComputingResourceTest {
             verify(libvirtUtilitiesHelper, 
times(1)).getConnectionByVmName(command.getVmName());
             verify(libvirtComputingResource, times(1)).getDomain(conn, 
instanceName);
             verify(libvirtComputingResource, 
times(1)).getVifDriver(nic.getType(), nic.getName());
-            verify(vifDriver, times(1)).plug(nic, "Other PV", "");
+            verify(vifDriver, times(1)).plug(nic, "Other PV", "", null);
         } catch (final LibvirtException e) {
             fail(e.getMessage());
         } catch (final InternalErrorException e) {
@@ -3262,7 +3268,7 @@ public class LibvirtComputingResourceTest {
 
             when(libvirtComputingResource.getVifDriver(nic.getType(), 
nic.getName())).thenReturn(vifDriver);
 
-            when(vifDriver.plug(nic, "Other PV", 
"")).thenThrow(InternalErrorException.class);
+            when(vifDriver.plug(nic, "Other PV", "", 
null)).thenThrow(InternalErrorException.class);
 
         } catch (final LibvirtException e) {
             fail(e.getMessage());
@@ -3281,7 +3287,7 @@ public class LibvirtComputingResourceTest {
             verify(libvirtUtilitiesHelper, 
times(1)).getConnectionByVmName(command.getVmName());
             verify(libvirtComputingResource, times(1)).getDomain(conn, 
instanceName);
             verify(libvirtComputingResource, 
times(1)).getVifDriver(nic.getType(), nic.getName());
-            verify(vifDriver, times(1)).plug(nic, "Other PV", "");
+            verify(vifDriver, times(1)).plug(nic, "Other PV", "", null);
         } catch (final LibvirtException e) {
             fail(e.getMessage());
         } catch (final InternalErrorException e) {
@@ -5217,4 +5223,22 @@ public class LibvirtComputingResourceTest {
         assertFalse(ans instanceof UnsupportedAnswer);
         assertTrue(ans instanceof Answer);
     }
+
+    @Test
+    public void testAddExtraConfigComponentEmptyExtraConfig() {
+        libvirtComputingResource = new LibvirtComputingResource();
+        libvirtComputingResource.addExtraConfigComponent(new HashMap<>(), 
vmDef);
+        Mockito.verify(vmDef, never()).addComp(any());
+    }
+
+    @Test
+    public void testAddExtraConfigComponentNotEmptyExtraConfig() {
+        libvirtComputingResource = new LibvirtComputingResource();
+        Map<String, String> extraConfig = new HashMap<>();
+        extraConfig.put("extraconfig-1", "value1");
+        extraConfig.put("extraconfig-2", "value2");
+        extraConfig.put("extraconfig-3", "value3");
+        libvirtComputingResource.addExtraConfigComponent(extraConfig, vmDef);
+        Mockito.verify(vmDef, times(1)).addComp(any());
+    }
 }
diff --git 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/OvsVifDriverTest.java
 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/OvsVifDriverTest.java
new file mode 100644
index 0000000..71a6353
--- /dev/null
+++ 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/OvsVifDriverTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 com.cloud.hypervisor.kvm.resource;
+
+import com.cloud.utils.script.Script;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@PrepareForTest({ Script.class })
+@RunWith(PowerMockRunner.class)
+public class OvsVifDriverTest {
+
+    private static final int dpdkPortNumber = 7;
+
+    private OvsVifDriver driver = new OvsVifDriver();
+
+    @Before
+    public void initMocks() {
+        MockitoAnnotations.initMocks(this);
+        PowerMockito.mockStatic(Script.class);
+        
Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).thenReturn(null);
+    }
+
+    @Test
+    public void testGetDpdkLatestPortNumberUsedNoDpdkPorts() {
+        Assert.assertEquals(0, driver.getDpdkLatestPortNumberUsed());
+    }
+
+    @Test
+    public void testGetDpdkLatestPortNumberUsedExistingDpdkPorts() {
+        Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).
+                thenReturn(OvsVifDriver.DPDK_PORT_PREFIX + 
String.valueOf(dpdkPortNumber));
+        Assert.assertEquals(dpdkPortNumber, 
driver.getDpdkLatestPortNumberUsed());
+    }
+
+    @Test
+    public void testGetNextDpdkPortNoDpdkPorts() {
+        Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).
+                thenReturn(null);
+        String expectedPortName = OvsVifDriver.DPDK_PORT_PREFIX + 
String.valueOf(1);
+        Assert.assertEquals(expectedPortName, driver.getNextDpdkPort());
+    }
+
+    @Test
+    public void testGetNextDpdkPortExistingDpdkPorts() {
+        Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).
+                thenReturn(OvsVifDriver.DPDK_PORT_PREFIX + 
String.valueOf(dpdkPortNumber));
+        String expectedPortName = OvsVifDriver.DPDK_PORT_PREFIX + 
String.valueOf(dpdkPortNumber + 1);
+        Assert.assertEquals(expectedPortName, driver.getNextDpdkPort());
+    }
+}
\ No newline at end of file
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java 
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index a06b595..96a40e1 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -44,6 +44,7 @@ import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
+import com.cloud.user.AccountVO;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import org.apache.cloudstack.affinity.AffinityGroupService;
@@ -514,6 +515,8 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
     private static final ConfigKey<Boolean> AllowDeployVmIfGivenHostFails = 
new ConfigKey<Boolean>("Advanced", Boolean.class, 
"allow.deploy.vm.if.deploy.on.given.host.fails", "false",
             "allow vm to deploy on different host if vm fails to deploy on the 
given host ", true);
 
+    private static final ConfigKey<Boolean> EnableAdditionalVmConfig = new 
ConfigKey<>("Advanced", Boolean.class, "enable.additional.vm.configuration",
+            "false", "allow additional arbitrary configuration to vm", true, 
ConfigKey.Scope.Account);
 
     @Override
     public UserVmVO getVirtualMachine(long vmId) {
@@ -2388,8 +2391,10 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         Map<String,String> details = cmd.getDetails();
         List<Long> securityGroupIdList = getSecurityGroupIdList(cmd);
         boolean cleanupDetails = cmd.isCleanupDetails();
+        String extraConfig = cmd.getExtraConfig();
 
         UserVmVO vmInstance = _vmDao.findById(cmd.getId());
+        long accountId = vmInstance.getAccountId();
 
         if (isDisplayVm != null && isDisplayVm != vmInstance.isDisplay()) {
             updateDisplayVmFlag(isDisplayVm, id, vmInstance);
@@ -2397,9 +2402,15 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         if (cleanupDetails){
             userVmDetailsDao.removeDetails(id);
         }
-        else if (MapUtils.isNotEmpty(details)) {
-            vmInstance.setDetails(details);
-            _vmDao.saveDetails(vmInstance);
+        else {
+            if (MapUtils.isNotEmpty(details)) {
+                vmInstance.setDetails(details);
+                _vmDao.saveDetails(vmInstance);
+            }
+            if (StringUtils.isNotBlank(extraConfig) && 
EnableAdditionalVmConfig.valueIn(accountId)) {
+                AccountVO account = _accountDao.findById(accountId);
+                addExtraConfig(vmInstance, account, extraConfig);
+            }
         }
         return updateVirtualMachine(id, displayName, group, ha, isDisplayVm, 
osTypeId, userData, isDynamicallyScalable,
                 cmd.getHttpMethod(), cmd.getCustomId(), hostName, 
cmd.getInstanceName(), securityGroupIdList, cmd.getDhcpOptionsMap());
@@ -4847,9 +4858,75 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 _tmplService.attachIso(tmpl.getId(), vm.getId());
             }
         }
+
+        // Add extraConfig to user_vm_details table
+        Account caller = CallContext.current().getCallingAccount();
+        Long callerId = caller.getId();
+        String extraConfig = cmd.getExtraConfig();
+        if (StringUtils.isNotBlank(extraConfig) && 
EnableAdditionalVmConfig.valueIn(callerId) ) {
+            addExtraConfig(vm, caller, extraConfig);
+        }
+
         return vm;
     }
 
+    /**
+     * Persist extra configurations as details for VMware VMs
+     */
+    protected void persistExtraConfigVmware(String decodedUrl, UserVm vm) {
+        String[] configDataArr = decodedUrl.split("\\r?\\n");
+        for (String config: configDataArr) {
+            String[] keyValue = config.split("=");
+            try {
+                userVmDetailsDao.addDetail(vm.getId(), keyValue[0], 
keyValue[1], true);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                throw new CloudRuntimeException("Issue occurred during parsing 
of:" + config);
+            }
+        }
+    }
+
+    /**
+     * Persist extra configurations as details for hypervisors except Vmware
+     */
+    protected void persistExtraConfigNonVmware(String decodedUrl, UserVm vm) {
+        String[] extraConfigs = decodedUrl.split("\n\n");
+        for (String cfg : extraConfigs) {
+            int i = 1;
+            String[] cfgParts = cfg.split("\n");
+            String extraConfigKey = ApiConstants.EXTRA_CONFIG;
+            String extraConfigValue;
+            if (cfgParts[0].matches("\\S+:$")) {
+                extraConfigKey += "-" + 
cfgParts[0].substring(0,cfgParts[0].length() - 1);
+                extraConfigValue = cfg.replace(cfgParts[0] + "\n", "");
+            } else {
+                extraConfigKey += "-" + String.valueOf(i);
+                extraConfigValue = cfg;
+            }
+            userVmDetailsDao.addDetail(vm.getId(), extraConfigKey, 
extraConfigValue, true);
+            i++;
+        }
+    }
+
+    protected void addExtraConfig(UserVm vm, Account caller, String 
extraConfig) {
+        String decodedUrl = decodeExtraConfig(extraConfig);
+        HypervisorType hypervisorType = vm.getHypervisorType();
+        if (hypervisorType == HypervisorType.VMware) {
+            persistExtraConfigVmware(decodedUrl, vm);
+        } else {
+            persistExtraConfigNonVmware(decodedUrl, vm);
+        }
+    }
+
+    protected String decodeExtraConfig(String encodeString) {
+        String decodedUrl;
+        try {
+            decodedUrl = URLDecoder.decode(encodeString, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new CloudRuntimeException("Failed to provided decode URL 
string: " + e.getMessage());
+        }
+        return decodedUrl;
+    }
+
     protected List<Long> getSecurityGroupIdList(SecurityGroupAction cmd) {
         if (cmd.getSecurityGroupNameList() != null && 
cmd.getSecurityGroupIdList() != null) {
             throw new InvalidParameterValueException("securitygroupids 
parameter is mutually exclusive with securitygroupnames parameter");
@@ -6394,7 +6471,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
     @Override
     public ConfigKey<?>[] getConfigKeys() {
         return new ConfigKey<?>[] {EnableDynamicallyScaleVm, 
AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, 
VmIpFetchThreadPoolMax,
-            VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails};
+            VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, 
EnableAdditionalVmConfig};
     }
 
     @Override

Reply via email to