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 a75444a KVM: DPDK live migrations (#3365)
a75444a is described below
commit a75444a585b952aeb3e984430238cae9e6be4dc5
Author: Nicolas Vazquez <[email protected]>
AuthorDate: Tue Jun 25 12:23:09 2019 -0300
KVM: DPDK live migrations (#3365)
* DPDK live migrations
* Remove DPDK created ports if VM migration fails or prepare migration fails
* Rename DPDK classes lowercase
---
.../main/java/com/cloud/agent/api/to/DpdkTO.java | 32 ++-
.../main/java/com/cloud/agent/api/to/NicTO.java | 10 +-
.../java/com/cloud/agent/api/MigrateCommand.java | 10 +
.../cloud/agent/api/PrepareForMigrationAnswer.java | 16 ++
.../main/java/com/cloud/agent/api/StopCommand.java | 10 +
.../com/cloud/vm/VirtualMachineManagerImpl.java | 29 ++-
.../kvm/dpdk/{DPDKDriver.java => DpdkDriver.java} | 8 +-
.../{DPDKDriverImpl.java => DpdkDriverImpl.java} | 26 +--
.../kvm/resource/LibvirtComputingResource.java | 13 +-
.../kvm/resource/LibvirtDomainXMLParser.java | 8 +-
.../hypervisor/kvm/resource/LibvirtVMDef.java | 30 ++-
.../hypervisor/kvm/resource/OvsVifDriver.java | 52 +++--
.../wrapper/LibvirtMigrateCommandWrapper.java | 76 +++++++
.../LibvirtPrepareForMigrationCommandWrapper.java | 36 ++-
.../wrapper/LibvirtStartCommandWrapper.java | 1 -
.../wrapper/LibvirtStopCommandWrapper.java | 26 ++-
.../{DPDKDriverTest.java => DpdkDriverTest.java} | 42 ++--
.../wrapper/LibvirtMigrateCommandWrapperTest.java | 153 +++++++++++++
.../com/cloud/hypervisor/HypervisorGuruBase.java | 33 +++
.../main/java/com/cloud/hypervisor/KVMGuru.java | 39 +---
.../cloud/hypervisor/kvm/dpdk/DPDKHelperImpl.java | 68 ------
.../kvm/dpdk/{DPDKHelper.java => DpdkHelper.java} | 19 +-
.../cloud/hypervisor/kvm/dpdk/DpdkHelperImpl.java | 154 +++++++++++++
.../com/cloud/server/ManagementServerImpl.java | 24 +-
.../main/java/com/cloud/vm/UserVmManagerImpl.java | 7 +
.../core/spring-server-core-misc-context.xml | 2 +-
.../java/com/cloud/hypervisor/KVMGuruTest.java | 13 --
.../hypervisor/kvm/dpdk/DPDKHelperImplTest.java | 135 ------------
.../hypervisor/kvm/dpdk/DpdkHelperImplTest.java | 245 +++++++++++++++++++++
29 files changed, 955 insertions(+), 362 deletions(-)
diff --git
a/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
b/api/src/main/java/com/cloud/agent/api/to/DpdkTO.java
similarity index 63%
copy from core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
copy to api/src/main/java/com/cloud/agent/api/to/DpdkTO.java
index c9f23d6..86ae277 100644
--- a/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
+++ b/api/src/main/java/com/cloud/agent/api/to/DpdkTO.java
@@ -1,4 +1,3 @@
-//
// 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
@@ -15,23 +14,32 @@
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
-//
+package com.cloud.agent.api.to;
+
+public class DpdkTO {
-package com.cloud.agent.api;
+ private String path;
+ private String port;
+ private String mode;
+
+ public DpdkTO() {
+ }
-public class PrepareForMigrationAnswer extends Answer {
- protected PrepareForMigrationAnswer() {
+ public DpdkTO(String path, String port, String mode) {
+ this.path = path;
+ this.port = port;
+ this.mode = mode;
}
- public PrepareForMigrationAnswer(PrepareForMigrationCommand cmd, String
detail) {
- super(cmd, false, detail);
+ public String getPath() {
+ return path;
}
- public PrepareForMigrationAnswer(PrepareForMigrationCommand cmd, Exception
ex) {
- super(cmd, ex);
+ public String getPort() {
+ return port;
}
- public PrepareForMigrationAnswer(PrepareForMigrationCommand cmd) {
- super(cmd, true, null);
+ public String getMode() {
+ return mode;
}
-}
+}
\ No newline at end of file
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 4861225..6a77f62 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,7 +30,7 @@ public class NicTO extends NetworkTO {
String nicUuid;
List<String> nicSecIps;
Map<NetworkOffering.Detail, String> details;
- boolean dpdkDisabled;
+ boolean dpdkEnabled;
public NicTO() {
super();
@@ -111,11 +111,11 @@ public class NicTO extends NetworkTO {
this.details = details;
}
- public boolean isDpdkDisabled() {
- return dpdkDisabled;
+ public boolean isDpdkEnabled() {
+ return dpdkEnabled;
}
- public void setDpdkDisabled(boolean dpdkDisabled) {
- this.dpdkDisabled = dpdkDisabled;
+ public void setDpdkEnabled(boolean dpdkEnabled) {
+ this.dpdkEnabled = dpdkEnabled;
}
}
diff --git a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
index f189c72..93324d2 100644
--- a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import com.cloud.agent.api.to.DpdkTO;
import com.cloud.agent.api.to.VirtualMachineTO;
public class MigrateCommand extends Command {
@@ -37,6 +38,15 @@ public class MigrateCommand extends Command {
private VirtualMachineTO vmTO;
private boolean executeInSequence = false;
private List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<>();
+ private Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
+
+ public Map<String, DpdkTO> getDpdkInterfaceMapping() {
+ return dpdkInterfaceMapping;
+ }
+
+ public void setDpdkInterfaceMapping(Map<String, DpdkTO>
dpdkInterfaceMapping) {
+ this.dpdkInterfaceMapping = dpdkInterfaceMapping;
+ }
protected MigrateCommand() {
}
diff --git
a/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
b/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
index c9f23d6..d0a544b 100644
--- a/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
+++ b/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
@@ -19,7 +19,15 @@
package com.cloud.agent.api;
+import com.cloud.agent.api.to.DpdkTO;
+
+import java.util.HashMap;
+import java.util.Map;
+
public class PrepareForMigrationAnswer extends Answer {
+
+ private Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
+
protected PrepareForMigrationAnswer() {
}
@@ -34,4 +42,12 @@ public class PrepareForMigrationAnswer extends Answer {
public PrepareForMigrationAnswer(PrepareForMigrationCommand cmd) {
super(cmd, true, null);
}
+
+ public void setDpdkInterfaceMapping(Map<String, DpdkTO> mapping) {
+ this.dpdkInterfaceMapping = mapping;
+ }
+
+ public Map<String, DpdkTO> getDpdkInterfaceMapping() {
+ return this.dpdkInterfaceMapping;
+ }
}
diff --git a/core/src/main/java/com/cloud/agent/api/StopCommand.java
b/core/src/main/java/com/cloud/agent/api/StopCommand.java
index eedf736..ad6833f 100644
--- a/core/src/main/java/com/cloud/agent/api/StopCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/StopCommand.java
@@ -19,6 +19,7 @@
package com.cloud.agent.api;
+import com.cloud.agent.api.to.DpdkTO;
import com.cloud.agent.api.to.GPUDeviceTO;
import com.cloud.vm.VirtualMachine;
@@ -34,6 +35,15 @@ public class StopCommand extends RebootCommand {
boolean checkBeforeCleanup = false;
String controlIp = null;
boolean forceStop = false;
+ private Map<String, DpdkTO> dpdkInterfaceMapping;
+
+ public Map<String, DpdkTO> getDpdkInterfaceMapping() {
+ return dpdkInterfaceMapping;
+ }
+
+ public void setDpdkInterfaceMapping(Map<String, DpdkTO>
dpdkInterfaceMapping) {
+ this.dpdkInterfaceMapping = dpdkInterfaceMapping;
+ }
/**
* On KVM when using iSCSI-based managed storage, if the user shuts a VM
down from the guest OS (as opposed to doing so from CloudStack),
* we need to pass to the KVM agent a list of applicable iSCSI volumes
that need to be disconnected.
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 354323e..7957053 100755
---
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -39,7 +39,8 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
-import org.apache.cloudstack.api.ApiConstants;
+import com.cloud.agent.api.PrepareForMigrationAnswer;
+import com.cloud.agent.api.to.DpdkTO;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
import org.apache.cloudstack.api.command.admin.volume.MigrateVolumeCmdByAdmin;
@@ -1118,8 +1119,6 @@ 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);
@@ -1284,15 +1283,6 @@ 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) {
@@ -2362,6 +2352,7 @@ public class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
}
boolean migrated = false;
+ Map<String, DpdkTO> dpdkInterfaceMapping = null;
try {
final boolean isWindows =
_guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
final MigrateCommand mc = new MigrateCommand(vm.getInstanceName(),
dest.getHost().getPrivateIpAddress(), isWindows, to,
getExecuteInSequence(vm.getHypervisorType()));
@@ -2370,6 +2361,11 @@ public class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
mc.setAutoConvergence(kvmAutoConvergence);
mc.setHostGuid(dest.getHost().getGuid());
+ dpdkInterfaceMapping = ((PrepareForMigrationAnswer)
pfma).getDpdkInterfaceMapping();
+ if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
+ mc.setDpdkInterfaceMapping(dpdkInterfaceMapping);
+ }
+
try {
final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
if (ma == null || !ma.getResult()) {
@@ -2396,7 +2392,7 @@ public class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
if (!checkVmOnHost(vm, dstHostId)) {
s_logger.error("Unable to complete migration for " + vm);
try {
- _agentMgr.send(srcHostId, new Commands(cleanup(vm)),
null);
+ _agentMgr.send(srcHostId, new Commands(cleanup(vm,
dpdkInterfaceMapping)), null);
} catch (final AgentUnavailableException e) {
s_logger.error("AgentUnavailableException while
cleanup on source host: " + srcHostId);
}
@@ -2417,7 +2413,7 @@ public class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
"Unable to migrate vm " + vm.getInstanceName() + "
from host " + fromHost.getName() + " in zone " + dest.getDataCenter().getName()
+ " and pod " +
dest.getPod().getName(), "Migrate Command
failed. Please check logs.");
try {
- _agentMgr.send(dstHostId, new Commands(cleanup(vm)), null);
+ _agentMgr.send(dstHostId, new Commands(cleanup(vm,
dpdkInterfaceMapping)), null);
} catch (final AgentUnavailableException ae) {
s_logger.info("Looks like the destination Host is
unavailable for cleanup");
}
@@ -3106,9 +3102,12 @@ public class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
}
}
- public Command cleanup(final VirtualMachine vm) {
+ public Command cleanup(final VirtualMachine vm, Map<String, DpdkTO>
dpdkInterfaceMapping) {
StopCommand cmd = new StopCommand(vm,
getExecuteInSequence(vm.getHypervisorType()), false);
cmd.setControlIp(getControlNicIpForVM(vm));
+ if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
+ cmd.setDpdkInterfaceMapping(dpdkInterfaceMapping);
+ }
return cmd;
}
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
similarity index 88%
rename from
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriver.java
rename to
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DpdkDriver.java
index f5306e2..0a010de 100644
---
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
@@ -22,7 +22,7 @@ import com.cloud.utils.component.Adapter;
import java.util.Map;
-public interface DPDKDriver extends Adapter {
+public interface DpdkDriver extends Adapter {
/**
* Get the next DPDK port name to be created
@@ -37,17 +37,17 @@ public interface DPDKDriver extends Adapter {
/**
* 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);
+ 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);
+ 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);
+ DpdkHelper.VHostUserMode getDpdkvHostUserMode(Map<String, String>
extraConfig);
/**
* Check for additional extra 'dpdk-interface' configurations, return them
appended
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
similarity index 81%
rename from
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriverImpl.java
rename to
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/dpdk/DpdkDriverImpl.java
index 71da0ae..cd962b6 100644
---
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
@@ -25,15 +25,15 @@ import org.apache.log4j.Logger;
import java.util.Map;
-public class DPDKDriverImpl extends AdapterBase implements DPDKDriver {
+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);
+ private static final Logger s_logger = Logger.getLogger(DpdkDriver.class);
- public DPDKDriverImpl() {
+ public DpdkDriverImpl() {
}
/**
@@ -64,8 +64,8 @@ public class DPDKDriverImpl extends AdapterBase implements
DPDKDriver {
/**
* 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 ?
+ public void addDpdkPort(String bridgeName, String port, String vlan,
DpdkHelper.VHostUserMode vHostUserMode, String dpdkOvsPath) {
+ String type = vHostUserMode == DpdkHelper.VHostUserMode.SERVER ?
dpdkPortVhostUserType :
dpdkPortVhostUserClientType;
@@ -74,7 +74,7 @@ public class DPDKDriverImpl extends AdapterBase implements
DPDKDriver {
"vlan_mode=access tag=%s " +
"-- set Interface %s type=%s", bridgeName, port, vlan, port,
type));
- if (vHostUserMode == DPDKHelper.VHostUserMode.CLIENT) {
+ if (vHostUserMode == DpdkHelper.VHostUserMode.CLIENT) {
stringBuilder.append(String.format("
options:vhost-server-path=%s/%s",
dpdkOvsPath, port));
}
@@ -87,17 +87,17 @@ public class DPDKDriverImpl extends AdapterBase implements
DPDKDriver {
/**
* 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";
+ 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;
+ 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;
}
/**
@@ -106,7 +106,7 @@ public class DPDKDriverImpl extends AdapterBase implements
DPDKDriver {
public String getExtraDpdkProperties(Map<String, String> extraConfig) {
StringBuilder stringBuilder = new StringBuilder();
for (String key : extraConfig.keySet()) {
- if (key.startsWith(DPDKHelper.DPDK_INTERFACE_PREFIX)) {
+ if (key.startsWith(DpdkHelper.DPDK_INTERFACE_PREFIX)) {
stringBuilder.append(extraConfig.get(key));
}
}
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 5f2a911..9319ee5 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,7 +46,7 @@ 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.hypervisor.kvm.dpdk.DpdkHelper;
import com.cloud.resource.RequestWrapper;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
@@ -2070,7 +2070,7 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
vm.setPlatformEmulator(vmTO.getPlatformEmulator());
Map<String, String> extraConfig = vmTO.getExtraConfig();
- if (dpdkSupport && (!extraConfig.containsKey(DPDKHelper.DPDK_NUMA) ||
!extraConfig.containsKey(DPDKHelper.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");
}
@@ -2107,7 +2107,7 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
grd.setVcpuNum(vcpus);
vm.addComp(grd);
- if (!extraConfig.containsKey(DPDKHelper.DPDK_NUMA)) {
+ if (!extraConfig.containsKey(DpdkHelper.DPDK_NUMA)) {
final CpuModeDef cmd = new CpuModeDef();
cmd.setMode(_guestCpuMode);
cmd.setModel(_guestCpuModel);
@@ -2235,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(DPDKHelper.DPDK_INTERFACE_PREFIX) &&
!key.equals(DPDKHelper.DPDK_VHOST_USER_MODE)) {
+ if (!key.startsWith(DpdkHelper.DPDK_INTERFACE_PREFIX) &&
!key.equals(DpdkHelper.DPDK_VHOST_USER_MODE)) {
extraConfigBuilder.append(extraConfig.get(key));
}
}
@@ -2706,7 +2706,10 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
final KVMHostInfo info = new KVMHostInfo(_dom0MinMem,
_dom0OvercommitMem);
- final String capabilities = String.join(",", info.getCapabilities());
+ String capabilities = String.join(",", info.getCapabilities());
+ if (dpdkSupport) {
+ capabilities += ",dpdk";
+ }
final StartupRoutingCommand cmd =
new StartupRoutingCommand(info.getCpus(), info.getCpuSpeed(),
info.getTotalMemory(), info.getReservedMemory(), capabilities, _hypervisorType,
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 97963fd..51ebdb3 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
@@ -224,9 +224,13 @@ public class LibvirtDomainXMLParser {
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];
+ String mode = getAttrValue("source", "mode", nic);
+ int lastSlashIndex = sourcePort.lastIndexOf("/");
+ String ovsPath = sourcePort.substring(0,lastSlashIndex);
+ String port = sourcePort.substring(lastSlashIndex + 1);
def.setDpdkSourcePort(port);
+ def.setDpdkOvsPath(ovsPath);
+ def.setInterfaceMode(mode);
}
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 400e16d..dab1af5 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
@@ -963,7 +963,7 @@ public class LibvirtVMDef {
}
public static class InterfaceDef {
- enum GuestNetType {
+ public enum GuestNetType {
BRIDGE("bridge"), DIRECT("direct"), NETWORK("network"),
USER("user"), ETHERNET("ethernet"), INTERNAL("internal"),
VHOSTUSER("vhostuser");
String _type;
@@ -1176,10 +1176,24 @@ public class LibvirtVMDef {
_dpdkSourcePort = port;
}
- @Override
- public String toString() {
+ public String getDpdkOvsPath() {
+ return _dpdkSourcePath;
+ }
+
+ public void setDpdkOvsPath(String path) {
+ _dpdkSourcePath = path;
+ }
+
+ public String getInterfaceMode() {
+ return _interfaceMode;
+ }
+
+ public void setInterfaceMode(String mode) {
+ _interfaceMode = mode;
+ }
+
+ public String getContent() {
StringBuilder netBuilder = new StringBuilder();
- netBuilder.append("<interface type='" + _netType + "'>\n");
if (_netType == GuestNetType.BRIDGE) {
netBuilder.append("<source bridge='" + _sourceName + "'/>\n");
} else if (_netType == GuestNetType.NETWORK) {
@@ -1233,6 +1247,14 @@ public class LibvirtVMDef {
if (_slot != null) {
netBuilder.append(String.format("<address type='pci'
domain='0x0000' bus='0x00' slot='0x%02x' function='0x0'/>\n", _slot));
}
+ return netBuilder.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder netBuilder = new StringBuilder();
+ netBuilder.append("<interface type='" + _netType + "'>\n");
+ netBuilder.append(getContent());
netBuilder.append("</interface>\n");
return netBuilder.toString();
}
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 8208530..1e7f4d5 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,9 +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.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;
@@ -44,7 +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;
- private DPDKDriver dpdkDriver;
+ private DpdkDriver dpdkDriver;
@Override
public void configure(Map<String, Object> params) throws
ConfigurationException {
@@ -59,7 +59,7 @@ public class OvsVifDriver extends VifDriverBase {
String dpdk = (String) params.get("openvswitch.dpdk.enabled");
if (StringUtils.isNotBlank(dpdk) && Boolean.parseBoolean(dpdk)) {
- dpdkDriver = new DPDKDriverImpl();
+ dpdkDriver = new DpdkDriverImpl();
}
String value = (String)params.get("scripts.timeout");
@@ -87,12 +87,34 @@ public class OvsVifDriver extends VifDriverBase {
s_logger.debug("done looking for pifs, no more bridges");
}
+ /**
+ * Plug interface with DPDK support:
+ * - Create a new port with DPDK support for the interface
+ * - Set the 'intf' path to the new port
+ */
+ protected void plugDPDKInterface(InterfaceDef intf, String trafficLabel,
Map<String, String> extraConfig,
+ String vlanId, String guestOsType, NicTO
nic, String nicAdapter) {
+ s_logger.debug("DPDK support enabled: configuring per traffic label "
+ trafficLabel);
+ 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 = 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);
+ }
+
@Override
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();
- if (!_libvirtComputingResource.dpdkSupport || nic.isDpdkDisabled()) {
+ if (!_libvirtComputingResource.dpdkSupport || !nic.isDpdkEnabled()) {
// 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");
@@ -114,20 +136,8 @@ public class OvsVifDriver extends VifDriverBase {
if ((nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan
|| nic.getBroadcastType() == Networks.BroadcastDomainType.Pvlan) &&
!vlanId.equalsIgnoreCase("untagged")) {
if (trafficLabel != null && !trafficLabel.isEmpty()) {
- if (_libvirtComputingResource.dpdkSupport &&
!nic.isDpdkDisabled()) {
- s_logger.debug("DPDK support enabled: configuring per
traffic label " + trafficLabel);
- 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 = 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);
+ if (_libvirtComputingResource.dpdkSupport &&
nic.isDpdkEnabled()) {
+ plugDPDKInterface(intf, trafficLabel, extraConfig,
vlanId, guestOsType, nic, nicAdapter);
} 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);
@@ -180,7 +190,7 @@ public class OvsVifDriver extends VifDriverBase {
@Override
public void unplug(InterfaceDef iface) {
// Libvirt apparently takes care of this, see BridgeVifDriver unplug
- if (_libvirtComputingResource.dpdkSupport) {
+ if (_libvirtComputingResource.dpdkSupport &&
StringUtils.isNotBlank(iface.getDpdkSourcePort())) {
// 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);
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
index 5bf8d25..e6d366e 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
@@ -43,6 +43,7 @@ import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import com.cloud.agent.api.to.DpdkTO;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
@@ -153,6 +154,11 @@ public final class LibvirtMigrateCommandWrapper extends
CommandWrapper<MigrateCo
xmlDesc = replaceStorage(xmlDesc, mapMigrateStorage,
migrateStorageManaged);
}
+ Map<String, DpdkTO> dpdkPortsMapping =
command.getDpdkInterfaceMapping();
+ if (MapUtils.isNotEmpty(dpdkPortsMapping)) {
+ xmlDesc = replaceDpdkInterfaces(xmlDesc, dpdkPortsMapping);
+ }
+
dconn =
libvirtUtilitiesHelper.retrieveQemuConnection(destinationUri);
//run migration in thread so we can monitor it
@@ -284,6 +290,76 @@ public final class LibvirtMigrateCommandWrapper extends
CommandWrapper<MigrateCo
}
/**
+ * Replace DPDK source path and target before migrations
+ */
+ protected String replaceDpdkInterfaces(String xmlDesc, Map<String, DpdkTO>
dpdkPortsMapping) throws TransformerException, ParserConfigurationException,
IOException, SAXException {
+ InputStream in = IOUtils.toInputStream(xmlDesc);
+
+ DocumentBuilderFactory docFactory =
DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+ Document doc = docBuilder.parse(in);
+
+ // Get the root element
+ Node domainNode = doc.getFirstChild();
+
+ NodeList domainChildNodes = domainNode.getChildNodes();
+
+ for (int i = 0; i < domainChildNodes.getLength(); i++) {
+ Node domainChildNode = domainChildNodes.item(i);
+
+ if ("devices".equals(domainChildNode.getNodeName())) {
+ NodeList devicesChildNodes = domainChildNode.getChildNodes();
+
+ for (int x = 0; x < devicesChildNodes.getLength(); x++) {
+ Node deviceChildNode = devicesChildNodes.item(x);
+
+ if ("interface".equals(deviceChildNode.getNodeName())) {
+ Node interfaceNode = deviceChildNode;
+ NamedNodeMap attributes =
interfaceNode.getAttributes();
+ Node interfaceTypeAttr =
attributes.getNamedItem("type");
+
+ if
("vhostuser".equals(interfaceTypeAttr.getNodeValue())) {
+ NodeList diskChildNodes =
interfaceNode.getChildNodes();
+
+ String mac = null;
+ for (int y = 0; y < diskChildNodes.getLength();
y++) {
+ Node diskChildNode = diskChildNodes.item(y);
+ if
(!"mac".equals(diskChildNode.getNodeName())) {
+ continue;
+ }
+ mac =
diskChildNode.getAttributes().getNamedItem("address").getNodeValue();
+ }
+
+ if (StringUtils.isNotBlank(mac)) {
+ DpdkTO to = dpdkPortsMapping.get(mac);
+
+ for (int z = 0; z <
diskChildNodes.getLength(); z++) {
+ Node diskChildNode =
diskChildNodes.item(z);
+
+ if
("target".equals(diskChildNode.getNodeName())) {
+ Node targetNode = diskChildNode;
+ Node targetNodeAttr =
targetNode.getAttributes().getNamedItem("dev");
+
targetNodeAttr.setNodeValue(to.getPort());
+ } else if
("source".equals(diskChildNode.getNodeName())) {
+ Node sourceNode = diskChildNode;
+ NamedNodeMap attrs =
sourceNode.getAttributes();
+ Node path = attrs.getNamedItem("path");
+ path.setNodeValue(to.getPath() + "/" +
to.getPort());
+ Node mode = attrs.getNamedItem("mode");
+ mode.setNodeValue(to.getMode());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return getXml(doc);
+ }
+
+ /**
* In case of a local file, it deletes the file on the source host/storage
pool. Otherwise (for instance iScsi) it disconnects the disk on the source
storage pool. </br>
* This method must be executed after a successful migration to a target
storage pool, cleaning up the source storage.
*/
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 5c9980b..f3f50aa 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
@@ -22,20 +22,28 @@ package com.cloud.hypervisor.kvm.resource.wrapper;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.PrepareForMigrationAnswer;
import com.cloud.agent.api.PrepareForMigrationCommand;
+import com.cloud.agent.api.to.DpdkTO;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
+import
com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.GuestNetType;
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.cloud.storage.Volume;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.Script;
+import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;
import org.libvirt.Connect;
import org.libvirt.LibvirtException;
import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
@ResourceWrapper(handles = PrepareForMigrationCommand.class)
public final class LibvirtPrepareForMigrationCommandWrapper extends
CommandWrapper<PrepareForMigrationCommand, Answer, LibvirtComputingResource> {
@@ -56,6 +64,8 @@ public final class LibvirtPrepareForMigrationCommandWrapper
extends CommandWrapp
final NicTO[] nics = vm.getNics();
+ Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
+
boolean skipDisconnect = false;
final KVMStoragePoolManager storagePoolMgr =
libvirtComputingResource.getStoragePoolMgr();
@@ -63,8 +73,13 @@ public final class LibvirtPrepareForMigrationCommandWrapper
extends CommandWrapp
final LibvirtUtilitiesHelper libvirtUtilitiesHelper =
libvirtComputingResource.getLibvirtUtilitiesHelper();
final Connect conn =
libvirtUtilitiesHelper.getConnectionByVmName(vm.getName());
+
for (final NicTO nic : nics) {
- libvirtComputingResource.getVifDriver(nic.getType(),
nic.getName()).plug(nic, null, "", null);
+ LibvirtVMDef.InterfaceDef interfaceDef =
libvirtComputingResource.getVifDriver(nic.getType(), nic.getName()).plug(nic,
null, "", vm.getExtraConfig());
+ if (interfaceDef != null && interfaceDef.getNetType() ==
GuestNetType.VHOSTUSER) {
+ DpdkTO to = new DpdkTO(interfaceDef.getDpdkOvsPath(),
interfaceDef.getDpdkSourcePort(), interfaceDef.getInterfaceMode());
+ dpdkInterfaceMapping.put(nic.getMac(), to);
+ }
}
/* setup disks, e.g for iso */
@@ -81,12 +96,19 @@ public final class LibvirtPrepareForMigrationCommandWrapper
extends CommandWrapp
return new PrepareForMigrationAnswer(command, "failed to
connect physical disks to host");
}
- return new PrepareForMigrationAnswer(command);
- } catch (final LibvirtException e) {
- return new PrepareForMigrationAnswer(command, e.toString());
- } catch (final InternalErrorException e) {
- return new PrepareForMigrationAnswer(command, e.toString());
- } catch (final URISyntaxException e) {
+ PrepareForMigrationAnswer answer = new
PrepareForMigrationAnswer(command);
+ if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
+ answer.setDpdkInterfaceMapping(dpdkInterfaceMapping);
+ }
+ return answer;
+ } catch (final LibvirtException | CloudRuntimeException |
InternalErrorException | URISyntaxException e) {
+ if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
+ for (DpdkTO to : dpdkInterfaceMapping.values()) {
+ String cmd = String.format("ovs-vsctl del-port %s",
to.getPort());
+ s_logger.debug("Removing DPDK port: " + to.getPort());
+ Script.runSimpleBashScript(cmd);
+ }
+ }
return new PrepareForMigrationAnswer(command, e.toString());
} finally {
if (!skipDisconnect) {
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 f3839f0..9c97bd4 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,7 +69,6 @@ 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/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStopCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStopCommandWrapper.java
index 7a0375f..ad12971 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStopCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStopCommandWrapper.java
@@ -23,8 +23,12 @@ import java.io.File;
import java.util.List;
import java.util.Map;
+import com.cloud.agent.api.to.DpdkTO;
import com.cloud.utils.Pair;
+import com.cloud.utils.script.Script;
import com.cloud.utils.ssh.SshHelper;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;
import org.libvirt.Connect;
import org.libvirt.Domain;
@@ -106,11 +110,23 @@ public final class LibvirtStopCommandWrapper extends
CommandWrapper<StopCommand,
}
}
- for (final InterfaceDef iface : ifaces) {
- // We don't know which "traffic type" is associated with
- // each interface at this point, so inform all vif drivers
- for (final VifDriver vifDriver :
libvirtComputingResource.getAllVifDrivers()) {
- vifDriver.unplug(iface);
+ if (CollectionUtils.isEmpty(ifaces)) {
+ Map<String, DpdkTO> dpdkInterfaceMapping =
command.getDpdkInterfaceMapping();
+ if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
+ for (DpdkTO to : dpdkInterfaceMapping.values()) {
+ String portToRemove = to.getPort();
+ String cmd = String.format("ovs-vsctl del-port
%s", portToRemove);
+ s_logger.debug("Removing DPDK port: " +
portToRemove);
+ Script.runSimpleBashScript(cmd);
+ }
+ }
+ } else {
+ for (final InterfaceDef iface : ifaces) {
+ // We don't know which "traffic type" is associated
with
+ // each interface at this point, so inform all vif
drivers
+ for (final VifDriver vifDriver :
libvirtComputingResource.getAllVifDrivers()) {
+ vifDriver.unplug(iface);
+ }
}
}
}
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriverTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/dpdk/DpdkDriverTest.java
similarity index 66%
rename from
plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriverTest.java
rename to
plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/dpdk/DpdkDriverTest.java
index a2a62fe..3bf1a6a 100644
---
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/dpdk/DPDKDriverTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/dpdk/DpdkDriverTest.java
@@ -35,11 +35,11 @@ import java.util.Map;
@PrepareForTest({ Script.class })
@RunWith(PowerMockRunner.class)
-public class DPDKDriverTest {
+public class DpdkDriverTest {
private static final int dpdkPortNumber = 7;
- private DPDKDriver driver = new DPDKDriverImpl();
+ private DpdkDriver driver = new DpdkDriverImpl();
private Map<String, String> extraConfig;
@@ -59,7 +59,7 @@ public class DPDKDriverTest {
@Test
public void testGetDpdkLatestPortNumberUsedExistingDpdkPorts() {
Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).
- thenReturn(DPDKDriverImpl.DPDK_PORT_PREFIX +
String.valueOf(dpdkPortNumber));
+ thenReturn(DpdkDriverImpl.DPDK_PORT_PREFIX +
String.valueOf(dpdkPortNumber));
Assert.assertEquals(dpdkPortNumber,
driver.getDpdkLatestPortNumberUsed());
}
@@ -67,47 +67,47 @@ public class DPDKDriverTest {
public void testGetNextDpdkPortNoDpdkPorts() {
Mockito.when(Script.runSimpleBashScript(Matchers.anyString())).
thenReturn(null);
- String expectedPortName = DPDKDriverImpl.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(DPDKDriverImpl.DPDK_PORT_PREFIX +
String.valueOf(dpdkPortNumber));
- String expectedPortName = DPDKDriverImpl.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);
+ 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);
+ 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);
+ 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);
+ 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);
+ public void testGetDpdkvHostUserModeServerEmptyExtraConfig() {
+ DpdkHelper.VHostUserMode dpdKvHostUserMode =
driver.getDpdkvHostUserMode(extraConfig);
+ Assert.assertEquals(DpdkHelper.VHostUserMode.SERVER,
dpdKvHostUserMode);
}
}
\ No newline at end of file
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
index 5289481..086808d 100644
---
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
@@ -37,6 +37,7 @@ import javax.xml.transform.TransformerException;
import org.junit.Assert;
import org.junit.Before;
+import com.cloud.agent.api.to.DpdkTO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.libvirt.Connect;
@@ -302,6 +303,146 @@ public class LibvirtMigrateCommandWrapperTest {
" </devices>\n" +
"</domain>";
+ private String sourceDPDKVMToMigrate =
+ "<domain type='kvm' id='17'>\n" +
+ " <name>i-2-33-VM</name>\n" +
+ " <uuid>14c5c052-46cb-4301-a00a-28f6cc1dc605</uuid>\n" +
+ " <description>Other PV (64-bit)</description>\n" +
+ " <memory unit='KiB'>9437184</memory>\n" +
+ " <currentMemory unit='KiB'>9437184</currentMemory>\n" +
+ " <memoryBacking>\n" +
+ " <hugepages/>\n" +
+ " </memoryBacking>\n" +
+ " <vcpu placement='static'>2</vcpu>\n" +
+ " <cputune>\n" +
+ " <shares>4000</shares>\n" +
+ " </cputune>\n" +
+ " <resource>\n" +
+ " <partition>/machine</partition>\n" +
+ " </resource>\n" +
+ " <sysinfo type='smbios'>\n" +
+ " <system>\n" +
+ " <entry name='manufacturer'>Apache Software
Foundation</entry>\n" +
+ " <entry name='product'>CloudStack KVM Hypervisor</entry>\n" +
+ " <entry
name='uuid'>14c5c052-46cb-4301-a00a-28f6cc1dc605</entry>\n" +
+ " </system>\n" +
+ " </sysinfo>\n" +
+ " <os>\n" +
+ " <type arch='x86_64' machine='pc-i440fx-rhel7.5.0'>hvm</type>\n" +
+ " <boot dev='cdrom'/>\n" +
+ " <boot dev='hd'/>\n" +
+ " <smbios mode='sysinfo'/>\n" +
+ " </os>\n" +
+ " <features>\n" +
+ " <acpi/>\n" +
+ " <apic/>\n" +
+ " <pae/>\n" +
+ " </features>\n" +
+ " <cpu mode='host-passthrough' check='none'>\n" +
+ " <numa>\n" +
+ " <cell id='0' cpus='0' memory='9437184' unit='KiB'
memAccess='shared'/>\n" +
+ " </numa>\n" +
+ " </cpu>\n" +
+ " <clock offset='utc'>\n" +
+ " <timer name='kvmclock'/>\n" +
+ " </clock>\n" +
+ " <on_poweroff>destroy</on_poweroff>\n" +
+ " <on_reboot>restart</on_reboot>\n" +
+ " <on_crash>destroy</on_crash>\n" +
+ " <devices>\n" +
+ " <emulator>/usr/libexec/qemu-kvm</emulator>\n" +
+ " <disk type='network' device='disk'>\n" +
+ " <driver name='qemu' type='raw' cache='none'/>\n" +
+ " <auth username='cloudstack'>\n" +
+ " <secret type='ceph'
uuid='66afbc07-6fdb-385a-ae25-a9acfbc3684d'/>\n" +
+ " </auth>\n" +
+ " <source protocol='rbd'
name='cloudstack/afb1d2e4-01fe-4694-940b-fcf052afa279'>\n" +
+ " <host name='VLAB01-CEPH-MON.ceph.local' port='6789'/>\n" +
+ " </source>\n" +
+ " <target dev='vda' bus='virtio'/>\n" +
+ " <serial>afb1d2e401fe4694940b</serial>\n" +
+ " <alias name='virtio-disk0'/>\n" +
+ " <address type='pci' domain='0x0000' bus='0x00' slot='0x05'
function='0x0'/>\n" +
+ " </disk>\n" +
+ " <disk type='file' device='cdrom'>\n" +
+ " <target dev='hdc' bus='ide'/>\n" +
+ " <readonly/>\n" +
+ " <alias name='ide0-1-0'/>\n" +
+ " <address type='drive' controller='0' bus='1' target='0'
unit='0'/>\n" +
+ " </disk>\n" +
+ " <controller type='usb' index='0' model='piix3-uhci'>\n" +
+ " <alias name='usb'/>\n" +
+ " <address type='pci' domain='0x0000' bus='0x00' slot='0x01'
function='0x2'/>\n" +
+ " </controller>\n" +
+ " <controller type='pci' index='0' model='pci-root'>\n" +
+ " <alias name='pci.0'/>\n" +
+ " </controller>\n" +
+ " <controller type='ide' index='0'>\n" +
+ " <alias name='ide'/>\n" +
+ " <address type='pci' domain='0x0000' bus='0x00' slot='0x01'
function='0x1'/>\n" +
+ " </controller>\n" +
+ " <controller type='virtio-serial' index='0'>\n" +
+ " <alias name='virtio-serial0'/>\n" +
+ " <address type='pci' domain='0x0000' bus='0x00' slot='0x04'
function='0x0'/>\n" +
+ " </controller>\n" +
+ " <interface type='vhostuser'>\n" +
+ " <mac address='02:00:18:91:00:10'/>\n" +
+ " <source type='unix' path='/var/run/libvirt-vhost-user/csdpdk-1'
mode='server'/>\n" +
+ " <target dev='csdpdk-1'/>\n" +
+ " <model type='virtio'/>\n" +
+ " <alias name='net0'/>\n" +
+ " <address type='pci' domain='0x0000' bus='0x00' slot='0x03'
function='0x0'/>\n" +
+ " </interface>\n" +
+ " <serial type='pty'>\n" +
+ " <source path='/dev/pts/0'/>\n" +
+ " <target type='isa-serial' port='0'>\n" +
+ " <model name='isa-serial'/>\n" +
+ " </target>\n" +
+ " <alias name='serial0'/>\n" +
+ " </serial>\n" +
+ " <console type='pty' tty='/dev/pts/0'>\n" +
+ " <source path='/dev/pts/0'/>\n" +
+ " <target type='serial' port='0'/>\n" +
+ " <alias name='serial0'/>\n" +
+ " </console>\n" +
+ " <channel type='unix'>\n" +
+ " <source mode='bind'
path='/var/lib/libvirt/qemu/i-2-33-VM.org.qemu.guest_agent.0'/>\n" +
+ " <target type='virtio' name='org.qemu.guest_agent.0'
state='disconnected'/>\n" +
+ " <alias name='channel0'/>\n" +
+ " <address type='virtio-serial' controller='0' bus='0'
port='1'/>\n" +
+ " </channel>\n" +
+ " <input type='tablet' bus='usb'>\n" +
+ " <alias name='input0'/>\n" +
+ " <address type='usb' bus='0' port='1'/>\n" +
+ " </input>\n" +
+ " <input type='mouse' bus='ps2'>\n" +
+ " <alias name='input1'/>\n" +
+ " </input>\n" +
+ " <input type='keyboard' bus='ps2'>\n" +
+ " <alias name='input2'/>\n" +
+ " </input>\n" +
+ " <graphics type='vnc' port='5900' autoport='yes'
listen='198.19.254.10'>\n" +
+ " <listen type='address' address='198.19.254.10'/>\n" +
+ " </graphics>\n" +
+ " <video>\n" +
+ " <model type='cirrus' vram='16384' heads='1' primary='yes'/>\n" +
+ " <alias name='video0'/>\n" +
+ " <address type='pci' domain='0x0000' bus='0x00' slot='0x02'
function='0x0'/>\n" +
+ " </video>\n" +
+ " <watchdog model='i6300esb' action='none'>\n" +
+ " <alias name='watchdog0'/>\n" +
+ " <address type='pci' domain='0x0000' bus='0x00' slot='0x06'
function='0x0'/>\n" +
+ " </watchdog>\n" +
+ " <memballoon model='none'>\n" +
+ " <alias name='balloon0'/>\n" +
+ " </memballoon>\n" +
+ " </devices>\n" +
+ " <seclabel type='dynamic' model='dac' relabel='yes'>\n" +
+ " <label>+0:+0</label>\n" +
+ " <imagelabel>+0:+0</imagelabel>\n" +
+ " </seclabel>\n" +
+ "</domain>";
+
LibvirtMigrateCommandWrapper libvirtMigrateCmdWrapper = new
LibvirtMigrateCommandWrapper();
final String memInfo = "MemTotal: 5830236 kB\n" +
@@ -636,4 +777,16 @@ public class LibvirtMigrateCommandWrapperTest {
assertFalse(newXml.contains("/mnt/" + sourcePoolUuid + "/" +
disk1SourceFilename));
assertFalse(newXml.contains("/mnt/" + sourcePoolUuid + "/" +
disk2SourceFilename));
}
+
+ @Test
+ public void testReplaceDPDKPorts() throws ParserConfigurationException,
IOException, SAXException, TransformerException {
+ final LibvirtMigrateCommandWrapper lw = new
LibvirtMigrateCommandWrapper();
+ Map<String, DpdkTO> dpdkPortMapping = new HashMap<>();
+ DpdkTO to = new DpdkTO("/var/run/libvirt-vhost-user", "csdpdk-7",
"client");
+ dpdkPortMapping.put("02:00:18:91:00:10", to);
+ String replaced = lw.replaceDpdkInterfaces(sourceDPDKVMToMigrate,
dpdkPortMapping);
+ Assert.assertTrue(replaced.contains("csdpdk-7"));
+ Assert.assertFalse(replaced.contains("csdpdk-1"));
+ }
+
}
diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
index 6410b43..b790978 100644
--- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
+++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
@@ -22,7 +22,9 @@ import java.util.UUID;
import javax.inject.Inject;
+import org.apache.cloudstack.api.ApiConstants;
import
org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Command;
@@ -126,6 +128,34 @@ public abstract class HypervisorGuruBase extends
AdapterBase implements Hypervis
return to;
}
+ /**
+ * Add extra configuration from VM details. Extra configuration is stored
as details starting with 'extraconfig'
+ */
+ private void addExtraConfig(Map<String, String> details, VirtualMachineTO
to) {
+ for (String key : details.keySet()) {
+ if (key.startsWith(ApiConstants.EXTRA_CONFIG)) {
+ to.addExtraConfig(key, details.get(key));
+ }
+ }
+ }
+
+ /**
+ * 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(ServiceOffering
offering, VirtualMachineTO to) {
+ 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());
+ }
+ }
+ }
+ }
+
protected VirtualMachineTO toVirtualMachineTO(VirtualMachineProfile
vmProfile) {
ServiceOffering offering =
_serviceOfferingDao.findById(vmProfile.getId(),
vmProfile.getServiceOfferingId());
VirtualMachine vm = vmProfile.getVirtualMachine();
@@ -169,8 +199,11 @@ public abstract class HypervisorGuruBase extends
AdapterBase implements Hypervis
Map<String, String> detailsInVm =
_userVmDetailsDao.listDetailsKeyPairs(vm.getId());
if (detailsInVm != null) {
to.setDetails(detailsInVm);
+ addExtraConfig(detailsInVm, to);
}
+ addServiceOfferingExtraConfiguration(offering, to);
+
// Set GPU details
ServiceOfferingDetailsVO offeringDetail =
_serviceOfferingDetailsDao.findDetail(offering.getId(),
GPU.Keys.vgpuType.toString());
if (offeringDetail != null) {
diff --git a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
index 5d20638..2b84ff9b 100644
--- a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
+++ b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
@@ -18,13 +18,12 @@ package com.cloud.hypervisor;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.to.DataObjectType;
+import com.cloud.agent.api.to.NicTO;
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.hypervisor.kvm.dpdk.DpdkHelper;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.GuestOSHypervisorVO;
import com.cloud.storage.GuestOSVO;
@@ -34,16 +33,14 @@ 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.commons.collections.MapUtils;
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 {
@@ -54,7 +51,7 @@ public class KVMGuru extends HypervisorGuruBase implements
HypervisorGuru {
@Inject
HostDao _hostDao;
@Inject
- DPDKHelper dpdkHelper;
+ DpdkHelper dpdkHelper;
public static final Logger s_logger = Logger.getLogger(KVMGuru.class);
@@ -114,12 +111,18 @@ 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)) {
+ if (dpdkHelper.isDpdkvHostUserModeSettingOnServiceOffering(vm)) {
dpdkHelper.setDpdkVhostUserMode(to, vm);
}
+ if (to.getType() == VirtualMachine.Type.User &&
MapUtils.isNotEmpty(to.getExtraConfig()) &&
+ to.getExtraConfig().containsKey(DpdkHelper.DPDK_NUMA) &&
to.getExtraConfig().containsKey(DpdkHelper.DPDK_HUGE_PAGES)) {
+ for (final NicTO nic : to.getNics()) {
+ nic.setDpdkEnabled(true);
+ }
+ }
+
// Determine the VM's OS description
GuestOSVO guestOS =
_guestOsDao.findByIdIncludingRemoved(vm.getVirtualMachine().getGuestOSId());
to.setOs(guestOS.getDisplayName());
@@ -137,24 +140,6 @@ 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/DPDKHelperImpl.java
b/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImpl.java
deleted file mode 100644
index 70d448f..0000000
--- a/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImpl.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelper.java
b/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DpdkHelper.java
similarity index 79%
rename from server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelper.java
rename to server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DpdkHelper.java
index f1dadc0..b31d2f9 100644
--- a/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelper.java
+++ b/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DpdkHelper.java
@@ -20,7 +20,7 @@ import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.vm.VirtualMachineProfile;
import org.apache.cloudstack.api.ApiConstants;
-public interface DPDKHelper {
+public interface DpdkHelper {
String DPDK_VHOST_USER_MODE = "DPDK-VHOSTUSER";
String DPDK_NUMA = ApiConstants.EXTRA_CONFIG + "-dpdk-numa";
@@ -56,11 +56,26 @@ public interface DPDKHelper {
* 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);
+ 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);
+ /**
+ * True if VM is a guest DPDK enabled VM, false if not.
+ * It is determined by:
+ * - VM type is guest
+ * - VM details contains NUMA and Huge pages configurations for DPDK
+ * - VM host contains the DPDK capability
+ */
+ boolean isVMDpdkEnabled(long vmId);
+
+ /**
+ * True if host is DPDK enabled, false if not.
+ * Host is DPDK enabled when:
+ * - 'dpdk' is part of the host capabilities
+ */
+ boolean isHostDpdkEnabled(long hostId);
}
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..e26b801
--- /dev/null
+++ b/server/src/main/java/com/cloud/hypervisor/kvm/dpdk/DpdkHelperImpl.java
@@ -0,0 +1,154 @@
+// 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.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.StringUtils;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.UserVmDetailVO;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.vm.dao.UserVmDetailsDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+import java.util.List;
+
+public class DpdkHelperImpl implements DpdkHelper {
+
+ @Inject
+ private ServiceOfferingDetailsDao serviceOfferingDetailsDao;
+ @Inject
+ private HostDao hostDao;
+ @Inject
+ private VMInstanceDao vmInstanceDao;
+ @Inject
+ private UserVmDetailsDao userVmDetailsDao;
+
+ 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()));
+ }
+ }
+ }
+
+ @Override
+ public boolean isVMDpdkEnabled(long vmId) {
+ VMInstanceVO vm = vmInstanceDao.findById(vmId);
+ if (vm == null) {
+ throw new CloudRuntimeException("Could not find VM with id " +
vmId);
+ }
+
+ if (vm.getType() != VirtualMachine.Type.User) {
+ return false;
+ }
+
+ List<UserVmDetailVO> details =
userVmDetailsDao.listDetails(vm.getId());
+ List<ServiceOfferingDetailsVO> offeringDetails =
serviceOfferingDetailsDao.listDetails(vm.getServiceOfferingId());
+
+ if (!hasRequiredDPDKConfigurations(details, offeringDetails)) {
+ return false;
+ }
+
+ return isHostDpdkEnabled(vm.getHostId());
+ }
+
+ /**
+ * True if VM is DPDK enabled. NUMA and HUGEPAGES configurations must be
present on VM or service offering details
+ */
+ private boolean hasRequiredDPDKConfigurations(List<UserVmDetailVO>
details, List<ServiceOfferingDetailsVO> offeringDetails) {
+ if (CollectionUtils.isEmpty(details)) {
+ return hasValidDPDKConfigurationsOnServiceOffering(false, false,
offeringDetails);
+ } else {
+ boolean isNumaSet = false;
+ boolean isHugePagesSet = false;
+ for (UserVmDetailVO detail : details) {
+ if (detail.getName().equals(DPDK_NUMA)) {
+ isNumaSet = true;
+ } else if (detail.getName().equals(DPDK_HUGE_PAGES)) {
+ isHugePagesSet = true;
+ }
+ }
+ boolean valid = isNumaSet && isHugePagesSet;
+ if (!valid) {
+ return hasValidDPDKConfigurationsOnServiceOffering(isNumaSet,
isHugePagesSet, offeringDetails);
+ }
+ return true;
+ }
+ }
+
+ /**
+ * True if DPDK required configurations are set
+ */
+ private boolean hasValidDPDKConfigurationsOnServiceOffering(boolean
isNumaSet, boolean isHugePagesSet, List<ServiceOfferingDetailsVO>
offeringDetails) {
+ if (!CollectionUtils.isEmpty(offeringDetails)) {
+ for (ServiceOfferingDetailsVO detail : offeringDetails) {
+ if (detail.getName().equals(DPDK_NUMA)) {
+ isNumaSet = true;
+ } else if (detail.getName().equals(DPDK_HUGE_PAGES)) {
+ isHugePagesSet = true;
+ }
+ }
+ }
+ return isNumaSet && isHugePagesSet;
+ }
+
+ @Override
+ public boolean isHostDpdkEnabled(long hostId) {
+ HostVO host = hostDao.findById(hostId);
+ if (host == null) {
+ throw new CloudRuntimeException("Could not find host with id " +
hostId);
+ }
+ return StringUtils.isNotBlank(host.getCapabilities()) &&
host.getCapabilities().contains("dpdk");
+ }
+}
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index 2c5892e..2c3e715 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -37,6 +37,8 @@ import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
+import com.cloud.storage.ScopeType;
+import com.cloud.hypervisor.kvm.dpdk.DpdkHelper;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
@@ -634,7 +636,6 @@ import com.cloud.storage.GuestOSHypervisor;
import com.cloud.storage.GuestOSHypervisorVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.GuestOsCategory;
-import com.cloud.storage.ScopeType;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
@@ -811,6 +812,8 @@ public class ManagementServerImpl extends ManagerBase
implements ManagementServe
private GuestOsDetailsDao _guestOsDetailsDao;
@Inject
private KeystoreManager _ksMgr;
+ @Inject
+ private DpdkHelper dpdkHelper;
private LockMasterListener _lockMasterListener;
private final ScheduledExecutorService _eventExecutor =
Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
@@ -1302,6 +1305,10 @@ public class ManagementServerImpl extends ManagerBase
implements ManagementServe
final ExcludeList excludes = new ExcludeList();
excludes.addHost(srcHostId);
+ if (dpdkHelper.isVMDpdkEnabled(vm.getId())) {
+ excludeNonDPDKEnabledHosts(plan, excludes);
+ }
+
// call affinitygroup chain
final long vmGroupCount =
_affinityGroupVMMapDao.countAffinityGroupsForVm(vm.getId());
@@ -1334,6 +1341,21 @@ public class ManagementServerImpl extends ManagerBase
implements ManagementServe
return new Ternary<Pair<List<? extends Host>, Integer>, List<? extends
Host>, Map<Host, Boolean>>(otherHosts, suitableHosts, requiresStorageMotion);
}
+ /**
+ * Add non DPDK enabled hosts to the avoid list
+ */
+ private void excludeNonDPDKEnabledHosts(DataCenterDeployment plan,
ExcludeList excludes) {
+ long dataCenterId = plan.getDataCenterId();
+ Long clusterId = plan.getClusterId();
+ Long podId = plan.getPodId();
+ List<HostVO> hosts =
_hostDao.listAllUpAndEnabledNonHAHosts(Type.Routing, clusterId, podId,
dataCenterId, null);
+ for (HostVO host : hosts) {
+ if (!dpdkHelper.isHostDpdkEnabled(host.getId())) {
+ excludes.addHost(host.getId());
+ }
+ }
+ }
+
private boolean hasSuitablePoolsForVolume(final VolumeVO volume, final
Host host, final VirtualMachineProfile vmProfile) {
final DiskOfferingVO diskOffering =
_diskOfferingDao.findById(volume.getDiskOfferingId());
final DiskProfile diskProfile = new DiskProfile(volume, diskOffering,
vmProfile.getHypervisorType());
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 8992351..1064415 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -96,6 +96,7 @@ import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
+import com.cloud.hypervisor.kvm.dpdk.DpdkHelper;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
@@ -472,6 +473,8 @@ public class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
private TemplateApiService _tmplService;
@Inject
private ConfigurationDao _configDao;
+ @Inject
+ private DpdkHelper dpdkHelper;
private ScheduledExecutorService _executor = null;
private ScheduledExecutorService _vmIpFetchExecutor = null;
@@ -5225,6 +5228,10 @@ public class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
}
}
+ if (dpdkHelper.isVMDpdkEnabled(vm.getId()) &&
!dpdkHelper.isHostDpdkEnabled(destinationHost.getId())) {
+ throw new CloudRuntimeException("Cannot migrate VM, VM is DPDK
enabled VM but destination host is not DPDK enabled");
+ }
+
checkHostsDedication(vm, srcHostId, destinationHost.getId());
// call to core process
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 a201250..1900717 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
@@ -79,6 +79,6 @@
<property name="name" value="Basic" />
</bean>
- <bean id="DPDKHelper" class="com.cloud.hypervisor.kvm.dpdk.DPDKHelperImpl"
/>
+ <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 e4deffd..2299d21 100644
--- a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
+++ b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
@@ -129,17 +129,4 @@ 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
deleted file mode 100644
index b666ad4..0000000
--- a/server/src/test/java/com/cloud/hypervisor/kvm/dpdk/DPDKHelperImplTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// 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");
- }
-}
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..f74e2e6
--- /dev/null
+++ b/server/src/test/java/com/cloud/hypervisor/kvm/dpdk/DpdkHelperImplTest.java
@@ -0,0 +1,245 @@
+// 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.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.vm.UserVmDetailVO;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.vm.dao.UserVmDetailsDao;
+import com.cloud.vm.dao.VMInstanceDao;
+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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DpdkHelperImplTest {
+
+ @Mock
+ ServiceOfferingDetailsDao serviceOfferingDetailsDao;
+ @Mock
+ HostDao hostDao;
+ @Mock
+ VMInstanceDao vmInstanceDao;
+ @Mock
+ UserVmDetailsDao userVmDetailsDao;
+
+ @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;
+ @Mock
+ UserVmDetailVO dpdkNumaVmDetail;
+ @Mock
+ UserVmDetailVO dpdkHugePagesVmDetail;
+ @Mock
+ HostVO hostVO;
+ @Mock
+ VMInstanceVO vmInstanceVO;
+
+ 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;
+ private static final Long hostId = 1L;
+ private static final String hostCapabilities = "hvm,snapshot";
+ private static final Long vmId = 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);
+
Mockito.when(vmProfile.getServiceOffering()).thenReturn(serviceOffering);
+ Mockito.when(serviceOffering.getId()).thenReturn(offeringId);
+
+ Mockito.when(hostDao.findById(hostId)).thenReturn(hostVO);
+ Mockito.when(hostVO.getCapabilities()).thenReturn(hostCapabilities);
+
+ Mockito.when(vmInstanceDao.findById(vmId)).thenReturn(vmInstanceVO);
+
Mockito.when(vmInstanceVO.getType()).thenReturn(VirtualMachine.Type.User);
+ Mockito.when(vmInstanceVO.getHostId()).thenReturn(hostId);
+
Mockito.when(vmInstanceVO.getServiceOfferingId()).thenReturn(offeringId);
+ Mockito.when(vmInstanceVO.getId()).thenReturn(vmId);
+
+
Mockito.when(dpdkNumaVmDetail.getName()).thenReturn(DpdkHelper.DPDK_NUMA);
+ Mockito.when(dpdkNumaVmDetail.getValue()).thenReturn(dpdkNumaConf);
+
Mockito.when(dpdkHugePagesVmDetail.getName()).thenReturn(DpdkHelper.DPDK_HUGE_PAGES);
+
Mockito.when(dpdkHugePagesVmDetail.getValue()).thenReturn(dpdkHugePagesConf);
+
Mockito.when(userVmDetailsDao.listDetails(vmId)).thenReturn(Arrays.asList(dpdkNumaVmDetail,
dpdkHugePagesVmDetail));
+ }
+
+ @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");
+ }
+
+ @Test
+ public void testIsHostDpdkEnabledNonDPDKHost() {
+ Assert.assertFalse(dpdkHelper.isHostDpdkEnabled(hostId));
+ }
+
+ @Test
+ public void testIsHostDpdkEnabledDpdkHost() {
+ Mockito.when(hostVO.getCapabilities()).thenReturn(hostCapabilities +
",dpdk");
+ Assert.assertTrue(dpdkHelper.isHostDpdkEnabled(hostId));
+ }
+
+ @Test
+ public void testIsVMDPDKEnabledDPDKEnabledVM() {
+ Mockito.when(hostVO.getCapabilities()).thenReturn(hostCapabilities +
",dpdk");
+ Assert.assertTrue(dpdkHelper.isVMDpdkEnabled(vmId));
+ }
+
+ @Test
+ public void testIsVMDpdkEnabledGuestType() {
+
Mockito.when(vmInstanceVO.getType()).thenReturn(VirtualMachine.Type.SecondaryStorageVm);
+ Assert.assertFalse(dpdkHelper.isVMDpdkEnabled(vmId));
+ Mockito.verify(dpdkHelper, Mockito.never()).isHostDpdkEnabled(hostId);
+ }
+
+ @Test
+ public void testIsVMDpdkEnabledGuestTypeMissingConfigurationOnDetails() {
+
Mockito.when(userVmDetailsDao.listDetails(vmId)).thenReturn(Arrays.asList(dpdkNumaVmDetail));
+
Mockito.when(serviceOfferingDetailsDao.listDetails(offeringId)).thenReturn(new
ArrayList<>());
+ Assert.assertFalse(dpdkHelper.isVMDpdkEnabled(vmId));
+ Mockito.verify(dpdkHelper, Mockito.never()).isHostDpdkEnabled(hostId);
+ }
+
+ @Test
+ public void testIsVMDpdkEnabledGuestTypeEmptyDetails() {
+ Mockito.when(userVmDetailsDao.listDetails(vmId)).thenReturn(new
ArrayList<>());
+
Mockito.when(serviceOfferingDetailsDao.listDetails(offeringId)).thenReturn(new
ArrayList<>());
+ Assert.assertFalse(dpdkHelper.isVMDpdkEnabled(vmId));
+ Mockito.verify(dpdkHelper, Mockito.never()).isHostDpdkEnabled(hostId);
+ }
+
+ @Test
+ public void testIsVMDpdkEnabledNonDPDKCapabilityOnHost() {
+ Assert.assertFalse(dpdkHelper.isVMDpdkEnabled(vmId));
+ }
+
+ @Test
+ public void testIsVMDpdkEnabledGuestTypeMissingConfigurationOnVmDetails() {
+
Mockito.when(userVmDetailsDao.listDetails(vmId)).thenReturn(Collections.singletonList(dpdkNumaVmDetail));
+ Mockito.when(hostVO.getCapabilities()).thenReturn(hostCapabilities +
",dpdk");
+ Assert.assertTrue(dpdkHelper.isVMDpdkEnabled(vmId));
+ }
+
+ @Test
+ public void testIsVMDpdkEnabledGuestTypeEmptyVmDetails() {
+ Mockito.when(userVmDetailsDao.listDetails(vmId)).thenReturn(new
ArrayList<>());
+ Mockito.when(hostVO.getCapabilities()).thenReturn(hostCapabilities +
",dpdk");
+ Assert.assertTrue(dpdkHelper.isVMDpdkEnabled(vmId));
+ }
+
+ @Test
+ public void testIsVMDpdkEnabledGuestTypeMixedConfigurationOnDetails() {
+
Mockito.when(userVmDetailsDao.listDetails(vmId)).thenReturn(Collections.singletonList(dpdkNumaVmDetail));
+
Mockito.when(serviceOfferingDetailsDao.listDetails(offeringId)).thenReturn(Collections.singletonList(dpdkHugePagesDetailVO));
+ Mockito.when(hostVO.getCapabilities()).thenReturn(hostCapabilities +
",dpdk");
+ Assert.assertTrue(dpdkHelper.isVMDpdkEnabled(vmId));
+ }
+}