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 <nicovazque...@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"); + } +}