You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by nv...@apache.org on 2019/05/29 11:36:43 UTC
[cloudstack] branch master updated: DPDK vHost User mode selection
(#3153)
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 501aa7c DPDK vHost User mode selection (#3153)
501aa7c is described below
commit 501aa7cd9199c90d4904b113d159779ffa5b8eeb
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Wed May 29 08:36:33 2019 -0300
DPDK vHost User mode selection (#3153)
* DPDK vHost User mode selection
* SQL text field and DPDK classes refactor
* Fix NullPointerException after refactor
* Fix unit test
* Refactor details type
---
.../resources/META-INF/db/schema-41200to41300.sql | 2 +
.../com/cloud/hypervisor/kvm/dpdk/DPDKDriver.java | 56 +++++++++
.../cloud/hypervisor/kvm/dpdk/DPDKDriverImpl.java | 115 ++++++++++++++++++
.../kvm/resource/LibvirtComputingResource.java | 11 +-
.../hypervisor/kvm/resource/LibvirtVMDef.java | 8 +-
.../hypervisor/kvm/resource/OvsVifDriver.java | 74 +++--------
.../DPDKDriverTest.java} | 52 ++++++--
.../configuration/ConfigurationManagerImpl.java | 17 ++-
.../com/cloud/hypervisor/HypervisorGuruBase.java | 6 +-
.../main/java/com/cloud/hypervisor/KVMGuru.java | 31 +++++
.../com/cloud/hypervisor/kvm/dpdk/DPDKHelper.java | 66 ++++++++++
.../cloud/hypervisor/kvm/dpdk/DPDKHelperImpl.java | 68 +++++++++++
.../core/spring-server-core-misc-context.xml | 2 +
.../java/com/cloud/hypervisor/KVMGuruTest.java | 52 +++++++-
.../hypervisor/kvm/dpdk/DPDKHelperImplTest.java | 135 +++++++++++++++++++++
15 files changed, 615 insertions(+), 80 deletions(-)
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41200to41300.sql b/engine/schema/src/main/resources/META-INF/db/schema-41200to41300.sql
index d4de8c4..e2221f2 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41200to41300.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41200to41300.sql
@@ -19,3 +19,5 @@
-- Schema upgrade from 4.12.0.0 to 4.13.0.0
--;
+-- DPDK client and server mode support
+ALTER TABLE `cloud`.`service_offering_details` CHANGE COLUMN `value` `value` TEXT NOT NULL;
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriver.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriver.java
new file mode 100644
index 0000000..f5306e2
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriver.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dpdk;
+
+import com.cloud.utils.component.Adapter;
+
+import java.util.Map;
+
+public interface DPDKDriver extends Adapter {
+
+ /**
+ * Get the next DPDK port name to be created
+ */
+ String getNextDpdkPort();
+
+ /**
+ * Get the latest DPDK port number created on a DPDK enabled host
+ */
+ int getDpdkLatestPortNumberUsed();
+
+ /**
+ * Add OVS port (if it does not exist) to bridge with DPDK support
+ */
+ void addDpdkPort(String bridgeName, String port, String vlan, DPDKHelper.VHostUserMode vHostUserMode, String dpdkOvsPath);
+
+ /**
+ * Since DPDK user client/server mode, retrieve the guest interfaces mode from the DPDK vHost User mode
+ */
+ String getGuestInterfacesModeFromDPDKVhostUserMode(DPDKHelper.VHostUserMode dpdKvHostUserMode);
+
+ /**
+ * Get DPDK vHost User mode from extra config. If it is not present, server is returned as default
+ */
+ DPDKHelper.VHostUserMode getDPDKvHostUserMode(Map<String, String> extraConfig);
+
+ /**
+ * Check for additional extra 'dpdk-interface' configurations, return them appended
+ */
+ String getExtraDpdkProperties(Map<String, String> extraConfig);
+}
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriverImpl.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriverImpl.java
new file mode 100644
index 0000000..71da0ae
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriverImpl.java
@@ -0,0 +1,115 @@
+/*
+ * 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.dpdk;
+
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.script.Script;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+import java.util.Map;
+
+public class DPDKDriverImpl extends AdapterBase implements DPDKDriver {
+ static final String DPDK_PORT_PREFIX = "csdpdk-";
+
+ private final String dpdkPortVhostUserType = "dpdkvhostuser";
+ private final String dpdkPortVhostUserClientType = "dpdkvhostuserclient";
+
+ private static final Logger s_logger = Logger.getLogger(DPDKDriver.class);
+
+ public DPDKDriverImpl() {
+ }
+
+ /**
+ * Get the next DPDK port name to be created
+ */
+ public String getNextDpdkPort() {
+ int portNumber = getDpdkLatestPortNumberUsed();
+ return DPDK_PORT_PREFIX + String.valueOf(portNumber + 1);
+ }
+
+ /**
+ * Get the latest DPDK port number created on a DPDK enabled host
+ */
+ public 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;
+ }
+
+ /**
+ * Add OVS port (if it does not exist) to bridge with DPDK support
+ */
+ public void addDpdkPort(String bridgeName, String port, String vlan, DPDKHelper.VHostUserMode vHostUserMode, String dpdkOvsPath) {
+ String type = vHostUserMode == DPDKHelper.VHostUserMode.SERVER ?
+ dpdkPortVhostUserType :
+ dpdkPortVhostUserClientType;
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(String.format("ovs-vsctl add-port %s %s " +
+ "vlan_mode=access tag=%s " +
+ "-- set Interface %s type=%s", bridgeName, port, vlan, port, type));
+
+ if (vHostUserMode == DPDKHelper.VHostUserMode.CLIENT) {
+ stringBuilder.append(String.format(" options:vhost-server-path=%s/%s",
+ dpdkOvsPath, port));
+ }
+
+ String cmd = stringBuilder.toString();
+ s_logger.debug("DPDK property enabled, executing: " + cmd);
+ Script.runSimpleBashScript(cmd);
+ }
+
+ /**
+ * Since DPDK user client/server mode, retrieve the guest interfaces mode from the DPDK vHost User mode
+ */
+ public String getGuestInterfacesModeFromDPDKVhostUserMode(DPDKHelper.VHostUserMode dpdKvHostUserMode) {
+ return dpdKvHostUserMode == DPDKHelper.VHostUserMode.CLIENT ? "server" : "client";
+ }
+
+ /**
+ * Get DPDK vHost User mode from extra config. If it is not present, server is returned as default
+ */
+ public DPDKHelper.VHostUserMode getDPDKvHostUserMode(Map<String, String> extraConfig) {
+ return extraConfig.containsKey(DPDKHelper.DPDK_VHOST_USER_MODE) ?
+ DPDKHelper.VHostUserMode.fromValue(extraConfig.get(DPDKHelper.DPDK_VHOST_USER_MODE)) :
+ DPDKHelper.VHostUserMode.SERVER;
+ }
+
+ /**
+ * Check for additional extra 'dpdk-interface' configurations, return them appended
+ */
+ public String getExtraDpdkProperties(Map<String, String> extraConfig) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (String key : extraConfig.keySet()) {
+ if (key.startsWith(DPDKHelper.DPDK_INTERFACE_PREFIX)) {
+ stringBuilder.append(extraConfig.get(key));
+ }
+ }
+ return stringBuilder.toString();
+ }
+}
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 ee47a47..5f2a911 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
@@ -46,8 +46,8 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import com.cloud.hypervisor.kvm.dpdk.DPDKHelper;
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;
@@ -524,9 +524,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
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("[.]");
@@ -2073,7 +2070,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
vm.setPlatformEmulator(vmTO.getPlatformEmulator());
Map<String, String> extraConfig = vmTO.getExtraConfig();
- if (dpdkSupport && (!extraConfig.containsKey(DPDK_NUMA) || !extraConfig.containsKey(DPDK_HUGE_PAGES))) {
+ if (dpdkSupport && (!extraConfig.containsKey(DPDKHelper.DPDK_NUMA) || !extraConfig.containsKey(DPDKHelper.DPDK_HUGE_PAGES))) {
s_logger.info("DPDK is enabled but it needs extra configurations for CPU NUMA and Huge Pages for VM deployment");
}
@@ -2110,7 +2107,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
grd.setVcpuNum(vcpus);
vm.addComp(grd);
- if (!extraConfig.containsKey(DPDK_NUMA)) {
+ if (!extraConfig.containsKey(DPDKHelper.DPDK_NUMA)) {
final CpuModeDef cmd = new CpuModeDef();
cmd.setMode(_guestCpuMode);
cmd.setModel(_guestCpuModel);
@@ -2238,7 +2235,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
if (MapUtils.isNotEmpty(extraConfig)) {
StringBuilder extraConfigBuilder = new StringBuilder();
for (String key : extraConfig.keySet()) {
- if (!key.startsWith(DPDK_INTERFACE_PREFIX)) {
+ if (!key.startsWith(DPDKHelper.DPDK_INTERFACE_PREFIX) && !key.equals(DPDKHelper.DPDK_VHOST_USER_MODE)) {
extraConfigBuilder.append(extraConfig.get(key));
}
}
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 617c278..400e16d 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
@@ -1017,6 +1017,7 @@ public class LibvirtVMDef {
private String _dpdkSourcePath;
private String _dpdkSourcePort;
private String _dpdkExtraLines;
+ private String _interfaceMode;
public void defBridgeNet(String brName, String targetBrName, String macAddr, NicModel model) {
defBridgeNet(brName, targetBrName, macAddr, model, 0);
@@ -1031,7 +1032,8 @@ public class LibvirtVMDef {
_networkRateKBps = networkRateKBps;
}
- public void defDpdkNet(String dpdkSourcePath, String dpdkPort, String macAddress, NicModel model, Integer networkRateKBps, String extra) {
+ public void defDpdkNet(String dpdkSourcePath, String dpdkPort, String macAddress, NicModel model,
+ Integer networkRateKBps, String extra, String interfaceMode) {
_netType = GuestNetType.VHOSTUSER;
_dpdkSourcePath = dpdkSourcePath;
_dpdkSourcePort = dpdkPort;
@@ -1039,6 +1041,7 @@ public class LibvirtVMDef {
_model = model;
_networkRateKBps = networkRateKBps;
_dpdkExtraLines = extra;
+ _interfaceMode = interfaceMode;
}
public void defDirectNet(String sourceName, String targetName, String macAddr, NicModel model, String sourceMode) {
@@ -1184,7 +1187,8 @@ public class LibvirtVMDef {
} 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");
+ netBuilder.append("<source type='unix' path='"+ _dpdkSourcePath + _dpdkSourcePort +
+ "' mode='" + _interfaceMode + "'/>\n");
}
if (_networkName != null) {
netBuilder.append("<target dev='" + _networkName + "'/>\n");
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 db3ee4d..8208530 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,9 @@ import java.util.Map;
import javax.naming.ConfigurationException;
+import com.cloud.hypervisor.kvm.dpdk.DPDKDriver;
+import com.cloud.hypervisor.kvm.dpdk.DPDKDriverImpl;
+import com.cloud.hypervisor.kvm.dpdk.DPDKHelper;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
@@ -41,8 +44,7 @@ import com.cloud.utils.script.Script;
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-";
+ private DPDKDriver dpdkDriver;
@Override
public void configure(Map<String, Object> params) throws ConfigurationException {
@@ -55,6 +57,11 @@ public class OvsVifDriver extends VifDriverBase {
networkScriptsDir = "scripts/vm/network/vnet";
}
+ String dpdk = (String) params.get("openvswitch.dpdk.enabled");
+ if (StringUtils.isNotBlank(dpdk) && Boolean.parseBoolean(dpdk)) {
+ dpdkDriver = new DPDKDriverImpl();
+ }
+
String value = (String)params.get("scripts.timeout");
_timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000;
}
@@ -80,55 +87,6 @@ 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, Map<String, String> extraConfig) throws InternalErrorException, LibvirtException {
s_logger.debug("plugging nic=" + nic);
@@ -158,12 +116,18 @@ public class OvsVifDriver extends VifDriverBase {
if (trafficLabel != null && !trafficLabel.isEmpty()) {
if (_libvirtComputingResource.dpdkSupport && !nic.isDpdkDisabled()) {
s_logger.debug("DPDK support enabled: configuring per traffic label " + trafficLabel);
- if (StringUtils.isBlank(_libvirtComputingResource.dpdkOvsPath)) {
+ String dpdkOvsPath = _libvirtComputingResource.dpdkOvsPath;
+ if (StringUtils.isBlank(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));
+ String port = dpdkDriver.getNextDpdkPort();
+ DPDKHelper.VHostUserMode dpdKvHostUserMode = dpdkDriver.getDPDKvHostUserMode(extraConfig);
+ dpdkDriver.addDpdkPort(_pifs.get(trafficLabel), port, vlanId, dpdKvHostUserMode, dpdkOvsPath);
+ String interfaceMode = dpdkDriver.getGuestInterfacesModeFromDPDKVhostUserMode(dpdKvHostUserMode);
+ intf.defDpdkNet(dpdkOvsPath, port, nic.getMac(),
+ getGuestNicModel(guestOsType, nicAdapter), 0,
+ dpdkDriver.getExtraDpdkProperties(extraConfig),
+ interfaceMode);
} 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);
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/dpdk/DPDKDriverTest.java
similarity index 53%
rename from plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/OvsVifDriverTest.java
rename to plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriverTest.java
index 71a6353..a2a62fe 100644
--- 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/dpdk/DPDKDriverTest.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package com.cloud.hypervisor.kvm.resource;
+package com.cloud.hypervisor.kvm.dpdk;
import com.cloud.utils.script.Script;
import org.junit.Assert;
@@ -30,19 +30,25 @@ import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
+import java.util.HashMap;
+import java.util.Map;
+
@PrepareForTest({ Script.class })
@RunWith(PowerMockRunner.class)
-public class OvsVifDriverTest {
+public class DPDKDriverTest {
private static final int dpdkPortNumber = 7;
- private OvsVifDriver driver = new OvsVifDriver();
+ private DPDKDriver driver = new DPDKDriverImpl();
+
+ private Map<String, String> extraConfig;
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(Script.class);
Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).thenReturn(null);
+ extraConfig = new HashMap<>();
}
@Test
@@ -53,7 +59,7 @@ public class OvsVifDriverTest {
@Test
public void testGetDpdkLatestPortNumberUsedExistingDpdkPorts() {
Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).
- thenReturn(OvsVifDriver.DPDK_PORT_PREFIX + String.valueOf(dpdkPortNumber));
+ thenReturn(DPDKDriverImpl.DPDK_PORT_PREFIX + String.valueOf(dpdkPortNumber));
Assert.assertEquals(dpdkPortNumber, driver.getDpdkLatestPortNumberUsed());
}
@@ -61,15 +67,47 @@ public class OvsVifDriverTest {
public void testGetNextDpdkPortNoDpdkPorts() {
Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).
thenReturn(null);
- String expectedPortName = OvsVifDriver.DPDK_PORT_PREFIX + String.valueOf(1);
+ String expectedPortName = DPDKDriverImpl.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);
+ thenReturn(DPDKDriverImpl.DPDK_PORT_PREFIX + String.valueOf(dpdkPortNumber));
+ String expectedPortName = DPDKDriverImpl.DPDK_PORT_PREFIX + String.valueOf(dpdkPortNumber + 1);
Assert.assertEquals(expectedPortName, driver.getNextDpdkPort());
}
+
+ @Test
+ public void testGetGuestInterfacesModeFromDPDKVhostUserModeClientDPDK() {
+ String guestMode = driver.getGuestInterfacesModeFromDPDKVhostUserMode(DPDKHelper.VHostUserMode.CLIENT);
+ Assert.assertEquals("server", guestMode);
+ }
+
+ @Test
+ public void testGetGuestInterfacesModeFromDPDKVhostUserModeServerDPDK() {
+ String guestMode = driver.getGuestInterfacesModeFromDPDKVhostUserMode(DPDKHelper.VHostUserMode.SERVER);
+ Assert.assertEquals("client", guestMode);
+ }
+
+ @Test
+ public void testGetDPDKvHostUserModeServerExtraConfig() {
+ extraConfig.put(DPDKHelper.DPDK_VHOST_USER_MODE, DPDKHelper.VHostUserMode.SERVER.toString());
+ DPDKHelper.VHostUserMode dpdKvHostUserMode = driver.getDPDKvHostUserMode(extraConfig);
+ Assert.assertEquals(DPDKHelper.VHostUserMode.SERVER, dpdKvHostUserMode);
+ }
+
+ @Test
+ public void testGetDPDKvHostUserModeServerClientExtraConfig() {
+ extraConfig.put(DPDKHelper.DPDK_VHOST_USER_MODE, DPDKHelper.VHostUserMode.CLIENT.toString());
+ DPDKHelper.VHostUserMode dpdKvHostUserMode = driver.getDPDKvHostUserMode(extraConfig);
+ Assert.assertEquals(DPDKHelper.VHostUserMode.CLIENT, dpdKvHostUserMode);
+ }
+
+ @Test
+ public void testGetDPDKvHostUserModeServerEmptyExtraConfig() {
+ DPDKHelper.VHostUserMode dpdKvHostUserMode = driver.getDPDKvHostUserMode(extraConfig);
+ Assert.assertEquals(DPDKHelper.VHostUserMode.SERVER, dpdKvHostUserMode);
+ }
}
\ No newline at end of file
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index cebc9e1..8df1750 100755
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -16,8 +16,10 @@
// under the License.
package com.cloud.configuration;
+import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.net.URLDecoder;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.util.ArrayList;
@@ -2492,17 +2494,26 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
detailsVO = new ArrayList<ServiceOfferingDetailsVO>();
for (final Entry<String, String> detailEntry : details.entrySet()) {
+ String detailEntryValue = detailEntry.getValue();
if (detailEntry.getKey().equals(GPU.Keys.pciDevice.toString())) {
- if (detailEntry.getValue() == null) {
+ if (detailEntryValue == null) {
throw new InvalidParameterValueException("Please specify a GPU Card.");
}
}
if (detailEntry.getKey().equals(GPU.Keys.vgpuType.toString())) {
- if (detailEntry.getValue() == null) {
+ if (detailEntryValue == null) {
throw new InvalidParameterValueException("vGPUType value cannot be null");
}
}
- detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), detailEntry.getKey(), detailEntry.getValue(), true));
+ if (detailEntry.getKey().startsWith(ApiConstants.EXTRA_CONFIG)) {
+ try {
+ detailEntryValue = URLDecoder.decode(detailEntry.getValue(), "UTF-8");
+ } catch (UnsupportedEncodingException | IllegalArgumentException e) {
+ s_logger.error("Cannot decode extra configuration value for key: " + detailEntry.getKey() + ", skipping it");
+ continue;
+ }
+ }
+ detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), detailEntry.getKey(), detailEntryValue, true));
}
}
diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
index 445997a..6410b43 100644
--- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
+++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
@@ -72,7 +72,7 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
@Inject
private ResourceManager _resourceMgr;
@Inject
- private ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
+ protected ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
@Inject
private ServiceOfferingDao _serviceOfferingDao;
@@ -172,8 +172,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
}
// Set GPU details
- ServiceOfferingDetailsVO offeringDetail = null;
- if ((offeringDetail = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString())) != null) {
+ ServiceOfferingDetailsVO offeringDetail = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString());
+ if (offeringDetail != null) {
ServiceOfferingDetailsVO groupName = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.pciDevice.toString());
to.setGpuDevice(_resourceMgr.getGPUDevice(vm.getHostId(), groupName.getValue(), offeringDetail.getValue()));
}
diff --git a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
index c512f06..5d20638 100644
--- a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
+++ b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
@@ -22,6 +22,9 @@ import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.hypervisor.kvm.dpdk.DPDKHelper;
+import com.cloud.offering.ServiceOffering;
+import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.GuestOSHypervisorVO;
import com.cloud.storage.GuestOSVO;
@@ -31,13 +34,16 @@ import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
+import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import javax.inject.Inject;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.util.List;
import java.util.Map;
public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
@@ -47,6 +53,8 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
GuestOSHypervisorDao _guestOsHypervisorDao;
@Inject
HostDao _hostDao;
+ @Inject
+ DPDKHelper dpdkHelper;
public static final Logger s_logger = Logger.getLogger(KVMGuru.class);
@@ -106,6 +114,11 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
public VirtualMachineTO implement(VirtualMachineProfile vm) {
VirtualMachineTO to = toVirtualMachineTO(vm);
setVmQuotaPercentage(to, vm);
+ addServiceOfferingExtraConfiguration(to, vm);
+
+ if (dpdkHelper.isDPDKvHostUserModeSettingOnServiceOffering(vm)) {
+ dpdkHelper.setDpdkVhostUserMode(to, vm);
+ }
// Determine the VM's OS description
GuestOSVO guestOS = _guestOsDao.findByIdIncludingRemoved(vm.getVirtualMachine().getGuestOSId());
@@ -124,6 +137,24 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
return to;
}
+ /**
+ * Add extra configurations from service offering to the VM TO.
+ * Extra configuration keys are expected in formats:
+ * - "extraconfig-N"
+ * - "extraconfig-CONFIG_NAME"
+ */
+ protected void addServiceOfferingExtraConfiguration(VirtualMachineTO to, VirtualMachineProfile vmProfile) {
+ ServiceOffering offering = vmProfile.getServiceOffering();
+ List<ServiceOfferingDetailsVO> details = _serviceOfferingDetailsDao.listDetails(offering.getId());
+ if (CollectionUtils.isNotEmpty(details)) {
+ for (ServiceOfferingDetailsVO detail : details) {
+ if (detail.getName().startsWith(ApiConstants.EXTRA_CONFIG)) {
+ to.addExtraConfig(detail.getName(), detail.getValue());
+ }
+ }
+ }
+ }
+
@Override
public Pair<Boolean, Long> getCommandHostDelegation(long hostId, Command cmd) {
if (cmd instanceof StorageSubSystemCommand) {
diff --git a/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelper.java b/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelper.java
new file mode 100644
index 0000000..f1dadc0
--- /dev/null
+++ b/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelper.java
@@ -0,0 +1,66 @@
+// 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.dpdk;
+
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.vm.VirtualMachineProfile;
+import org.apache.cloudstack.api.ApiConstants;
+
+public interface DPDKHelper {
+
+ String DPDK_VHOST_USER_MODE = "DPDK-VHOSTUSER";
+ String DPDK_NUMA = ApiConstants.EXTRA_CONFIG + "-dpdk-numa";
+ String DPDK_HUGE_PAGES = ApiConstants.EXTRA_CONFIG + "-dpdk-hugepages";
+ String DPDK_INTERFACE_PREFIX = ApiConstants.EXTRA_CONFIG + "-dpdk-interface-";
+
+ enum VHostUserMode {
+ CLIENT("client"), SERVER("server");
+
+ private String str;
+
+ VHostUserMode(String str) {
+ this.str = str;
+ }
+
+ public static VHostUserMode fromValue(String val) {
+ if (val.equalsIgnoreCase("client")) {
+ return CLIENT;
+ } else if (val.equalsIgnoreCase("server")) {
+ return SERVER;
+ } else {
+ throw new IllegalArgumentException("Invalid DPDK vHost User mode:" + val);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return str;
+ }
+ }
+
+ /**
+ * True if the DPDK vHost user mode setting is part of the VM service offering details, false if not.
+ * @param vm
+ */
+ boolean isDPDKvHostUserModeSettingOnServiceOffering(VirtualMachineProfile vm);
+
+ /**
+ * Add DPDK vHost User Mode as extra configuration to the VM TO (if present on the VM service offering details)
+ */
+ void setDpdkVhostUserMode(VirtualMachineTO to, VirtualMachineProfile vm);
+
+}
diff --git a/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImpl.java b/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImpl.java
new file mode 100644
index 0000000..70d448f
--- /dev/null
+++ b/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImpl.java
@@ -0,0 +1,68 @@
+// 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.dpdk;
+
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.offering.ServiceOffering;
+import com.cloud.service.ServiceOfferingDetailsVO;
+import com.cloud.service.dao.ServiceOfferingDetailsDao;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VirtualMachineProfile;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+
+public class DPDKHelperImpl implements DPDKHelper {
+
+ @Inject
+ ServiceOfferingDetailsDao serviceOfferingDetailsDao;
+
+ public static final Logger s_logger = Logger.getLogger(DPDKHelperImpl.class);
+
+ private ServiceOffering getServiceOfferingFromVMProfile(VirtualMachineProfile virtualMachineProfile) {
+ ServiceOffering offering = virtualMachineProfile.getServiceOffering();
+ if (offering == null) {
+ throw new CloudRuntimeException("VM does not have an associated service offering");
+ }
+ return offering;
+ }
+
+ @Override
+ public boolean isDPDKvHostUserModeSettingOnServiceOffering(VirtualMachineProfile vm) {
+ ServiceOffering offering = getServiceOfferingFromVMProfile(vm);
+ ServiceOfferingDetailsVO detail = serviceOfferingDetailsDao.findDetail(offering.getId(), DPDK_VHOST_USER_MODE);
+ return detail != null;
+ }
+
+ @Override
+ public void setDpdkVhostUserMode(VirtualMachineTO to, VirtualMachineProfile vm) {
+ ServiceOffering offering = getServiceOfferingFromVMProfile(vm);
+ ServiceOfferingDetailsVO detail = serviceOfferingDetailsDao.findDetail(offering.getId(), DPDK_VHOST_USER_MODE);
+ if (detail != null) {
+ String mode = detail.getValue();
+ try {
+ VHostUserMode dpdKvHostUserMode = VHostUserMode.fromValue(mode);
+ to.addExtraConfig(DPDK_VHOST_USER_MODE, dpdKvHostUserMode.toString());
+ } catch (IllegalArgumentException e) {
+ s_logger.error(String.format("DPDK vHost User mode found as a detail for service offering: %s " +
+ "but value: %s is not supported. Supported values: %s, %s",
+ offering.getId(), mode,
+ VHostUserMode.CLIENT.toString(), VHostUserMode.SERVER.toString()));
+ }
+ }
+ }
+}
diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml
index 481db24..a201250 100644
--- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml
+++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml
@@ -78,5 +78,7 @@
<bean id="ExternalIpAddressAllocator" class="com.cloud.network.ExternalIpAddressAllocator">
<property name="name" value="Basic" />
</bean>
+
+ <bean id="DPDKHelper" class="com.cloud.hypervisor.kvm.dpdk.DPDKHelperImpl" />
</beans>
\ No newline at end of file
diff --git a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
index 597e20f..e4deffd 100644
--- a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
+++ b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
@@ -19,9 +19,13 @@ package com.cloud.hypervisor;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
+import com.cloud.offering.ServiceOffering;
+import com.cloud.service.ServiceOfferingDetailsVO;
+import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
+import org.apache.cloudstack.api.ApiConstants;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,11 +35,16 @@ import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
@RunWith(MockitoJUnitRunner.class)
public class KVMGuruTest {
@Mock
HostDao hostDao;
+ @Mock
+ ServiceOfferingDetailsDao serviceOfferingDetailsDao;
@Spy
@InjectMocks
@@ -49,18 +58,42 @@ public class KVMGuruTest {
VirtualMachine vm;
@Mock
HostVO host;
+ @Mock
+ ServiceOffering serviceOffering;
+ @Mock
+ ServiceOfferingDetailsVO detail1;
+ @Mock
+ ServiceOfferingDetailsVO detail2;
+
+ private static final long hostId = 1L;
+ private static final Long offeringId = 1L;
- private static final long hostId = 1l;
+ private static final String detail1Key = ApiConstants.EXTRA_CONFIG + "-config-1";
+ private static final String detail1Value = "value1";
+ private static final String detail2Key = "detail2";
+ private static final String detail2Value = "value2";
@Before
- public void setup() {
+ public void setup() throws UnsupportedEncodingException {
Mockito.when(vmTO.getLimitCpuUse()).thenReturn(true);
Mockito.when(vmProfile.getVirtualMachine()).thenReturn(vm);
Mockito.when(vm.getHostId()).thenReturn(hostId);
Mockito.when(hostDao.findById(hostId)).thenReturn(host);
Mockito.when(host.getCpus()).thenReturn(3);
- Mockito.when(host.getSpeed()).thenReturn(1995l);
+ Mockito.when(host.getSpeed()).thenReturn(1995L);
Mockito.when(vmTO.getMaxSpeed()).thenReturn(500);
+ Mockito.when(serviceOffering.getId()).thenReturn(offeringId);
+ Mockito.when(vmProfile.getServiceOffering()).thenReturn(serviceOffering);
+
+ Mockito.when(detail1.getName()).thenReturn(detail1Key);
+ Mockito.when(detail1.getValue()).thenReturn(detail1Value);
+ Mockito.when(detail1.getResourceId()).thenReturn(offeringId);
+ Mockito.when(detail2.getName()).thenReturn(detail2Key);
+ Mockito.when(detail2.getResourceId()).thenReturn(offeringId);
+ Mockito.when(detail2.getValue()).thenReturn(detail2Value);
+
+ Mockito.when(serviceOfferingDetailsDao.listDetails(offeringId)).thenReturn(
+ Arrays.asList(detail1, detail2));
}
@Test
@@ -96,4 +129,17 @@ public class KVMGuruTest {
guru.setVmQuotaPercentage(vmTO, vmProfile);
Mockito.verify(vmTO).setCpuQuotaPercentage(1d);
}
+
+ @Test
+ public void testAddServiceOfferingExtraConfigurationDpdkDetails() {
+ guru.addServiceOfferingExtraConfiguration(vmTO, vmProfile);
+ Mockito.verify(vmTO).addExtraConfig(detail1Key, detail1Value);
+ }
+
+ @Test
+ public void testAddServiceOfferingExtraConfigurationEmptyDetails() {
+ Mockito.when(serviceOfferingDetailsDao.listDetails(offeringId)).thenReturn(null);
+ guru.addServiceOfferingExtraConfiguration(vmTO, vmProfile);
+ Mockito.verify(vmTO, Mockito.never()).addExtraConfig(Mockito.anyString(), Mockito.anyString());
+ }
}
\ No newline at end of file
diff --git a/server/src/test/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImplTest.java b/server/src/test/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImplTest.java
new file mode 100644
index 0000000..b666ad4
--- /dev/null
+++ b/server/src/test/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImplTest.java
@@ -0,0 +1,135 @@
+// 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.dpdk;
+
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.offering.ServiceOffering;
+import com.cloud.service.ServiceOfferingDetailsVO;
+import com.cloud.service.dao.ServiceOfferingDetailsDao;
+import com.cloud.vm.VirtualMachineProfile;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Arrays;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DPDKHelperImplTest {
+
+ @Mock
+ ServiceOfferingDetailsDao serviceOfferingDetailsDao;
+
+ @Spy
+ @InjectMocks
+ private DPDKHelper dpdkHelper = new DPDKHelperImpl();
+
+ @Mock
+ VirtualMachineTO vmTO;
+ @Mock
+ VirtualMachineProfile vmProfile;
+ @Mock
+ ServiceOfferingDetailsVO dpdkVhostUserModeDetailVO;
+ @Mock
+ ServiceOfferingDetailsVO dpdkNumaDetailVO;
+ @Mock
+ ServiceOfferingDetailsVO dpdkHugePagesDetailVO;
+ @Mock
+ ServiceOffering serviceOffering;
+
+ private String dpdkVhostMode = DPDKHelper.VHostUserMode.SERVER.toString();
+
+ private static final String dpdkNumaConf =
+ "<cpu mode=\"host-passthrough\">\n" +
+ " <numa>\n" +
+ " <cell id=\"0\" cpus=\"0\" memory=\"9437184\" unit=\"KiB\" memAccess=\"shared\"/>\n" +
+ " </numa>\n" +
+ "</cpu>";
+ private static final String dpdkHugePagesConf =
+ "<memoryBacking>\n" +
+ " <hugePages/>\n" +
+ "</memoryBacking>";
+ private static String dpdkNumaValue;
+ private static String dpdkHugePagesValue;
+ private static final Long offeringId = 1L;
+
+ @Before
+ public void setup() throws UnsupportedEncodingException {
+ dpdkHugePagesValue = URLEncoder.encode(dpdkHugePagesConf, "UTF-8");
+ dpdkNumaValue = URLEncoder.encode(dpdkNumaConf, "UTF-8");
+
+ Mockito.when(dpdkVhostUserModeDetailVO.getName()).thenReturn(DPDKHelper.DPDK_VHOST_USER_MODE);
+ Mockito.when(dpdkVhostUserModeDetailVO.getValue()).thenReturn(dpdkVhostMode);
+ Mockito.when(dpdkVhostUserModeDetailVO.getResourceId()).thenReturn(offeringId);
+ Mockito.when(dpdkNumaDetailVO.getName()).thenReturn(DPDKHelper.DPDK_NUMA);
+ Mockito.when(dpdkNumaDetailVO.getResourceId()).thenReturn(offeringId);
+ Mockito.when(dpdkNumaDetailVO.getValue()).thenReturn(dpdkNumaValue);
+ Mockito.when(dpdkHugePagesDetailVO.getName()).thenReturn(DPDKHelper.DPDK_HUGE_PAGES);
+ Mockito.when(dpdkHugePagesDetailVO.getResourceId()).thenReturn(offeringId);
+ Mockito.when(dpdkHugePagesDetailVO.getValue()).thenReturn(dpdkHugePagesValue);
+
+ Mockito.when(serviceOfferingDetailsDao.listDetails(offeringId)).thenReturn(
+ Arrays.asList(dpdkNumaDetailVO, dpdkHugePagesDetailVO, dpdkVhostUserModeDetailVO));
+ Mockito.when(vmProfile.getServiceOffering()).thenReturn(serviceOffering);
+ Mockito.when(serviceOffering.getId()).thenReturn(offeringId);
+ }
+
+ @Test
+ public void testSetDpdkVhostUserModeValidDetail() {
+ Mockito.when(serviceOfferingDetailsDao.findDetail(offeringId, DPDKHelper.DPDK_VHOST_USER_MODE)).
+ thenReturn(dpdkVhostUserModeDetailVO);
+ dpdkHelper.setDpdkVhostUserMode(vmTO, vmProfile);
+ Mockito.verify(vmTO).addExtraConfig(DPDKHelper.DPDK_VHOST_USER_MODE, dpdkVhostMode);
+ }
+
+ @Test
+ public void testSetDpdkVhostUserModeInvalidDetail() {
+ Mockito.when(dpdkVhostUserModeDetailVO.getValue()).thenReturn("serverrrr");
+ Mockito.verify(vmTO, Mockito.never()).addExtraConfig(Mockito.anyString(), Mockito.anyString());
+ }
+
+ @Test
+ public void testSetDpdkVhostUserModeNotExistingDetail() {
+ Mockito.when(serviceOfferingDetailsDao.listDetails(offeringId)).thenReturn(
+ Arrays.asList(dpdkNumaDetailVO, dpdkHugePagesDetailVO));
+ Mockito.verify(vmTO, Mockito.never()).addExtraConfig(Mockito.anyString(), Mockito.anyString());
+ }
+
+ @Test
+ public void testDPDKvHostUserFromValueClient() {
+ DPDKHelper.VHostUserMode mode = DPDKHelper.VHostUserMode.fromValue("client");
+ Assert.assertEquals(DPDKHelper.VHostUserMode.CLIENT, mode);
+ }
+
+ @Test
+ public void testDPDKvHostUserFromValueServer() {
+ DPDKHelper.VHostUserMode mode = DPDKHelper.VHostUserMode.fromValue("server");
+ Assert.assertEquals(DPDKHelper.VHostUserMode.SERVER, mode);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDPDKvHostUserFromValueServerInvalid() {
+ DPDKHelper.VHostUserMode.fromValue("serverrrr");
+ }
+}