Improve migration of external VMware VMs into KVM cluster (#8815)

* Create/Export OVA file of the VM on external vCenter host, to temporary conversion location (NFS)

* Fixed ova issue on untar/extract ovf from ova file
"tar -xf" cmd on ova fails with "ovf: Not found in archive" while extracting ovf file

* Updated VMware to KVM instance migration using OVA

* Refactoring and cleanup

* test fixes

* Consider zone wide pools in the destination cluster for instance conversion

* Remove local storage pool support as temporary conversion location
- OVA export not possible as the pool is not accessible outside host, NFS pools are supported.

* cleanup unused code

* some improvements, and refactoring

* import nic unit tests

* vmware guru unit tests

* Separate clone VM and create template file for VMware migration
- Export OVA (of the cloned VM) to the conversion location takes time.
- Do any validations with cloned VM before creating the template (and fail early).
- Updated unit tests.

* Check conversion support on host before clone vm / create template on vmware (and fail early)

* minor code improvements

* Auto select the host with instance conversion capability

* Skip instance conversion supported response param for non-KVM hosts

* Show supported conversion hosts in the UI

* Skip persistence map update if network doesn't exist

* Added support to export OVA from KVM host, through ovftool (when installed in KVM host)

* Updated importvm api param 'usemsforovaexport' to 'forcemstodownloadvmfiles', to be generic

* Updated hardcoded UI messages with message labels

* Updated UI to support importvm api param - forcemstodownloadvmfiles

* Improved instance conversion support checks on ubuntu hosts, and for windows guest vms

* Use OVF template (VM disks and spec files) for instance conversion from VMware, instead of OVA file
 - this would further increase the migration performance (as it reduces the time for OVA preparation / archiving of the VM files into a single file)

* OVF export tool parallel threads code improvements

* Updated 'convert.vmware.instance.to.kvm.timeout' config default value to 3 hrs

* Config values check & code improvements

* Updated import log, with time taken and vm details

* Support for parallel downloads of VMware VM disk files while exporting OVF from MS, and other changes below.
- Skip clone for powered off VMs
- Fixes to support standalone host (with its default datacenter)
- Some code improvements

* rebase fixes

* rebase fixes

* minor improvement

* code improvements - threads configuration, and api parameter changes to import vm files

* typo fix in error msg
This commit is contained in:
Suresh Kumar Anaparti 2024-06-27 21:14:13 +05:30 committed by GitHub
parent 23f8856c7d
commit 46f672563e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 1898 additions and 348 deletions

View File

@ -751,7 +751,7 @@ public class AgentProperties{
public static final Property<Integer> IOTHREADS = new Property<>("iothreads", 1);
/**
* Enable verbose mode for virt-v2v Instance Conversion from Vmware to KVM
* Enable verbose mode for virt-v2v Instance Conversion from VMware to KVM
* Data type: Boolean.<br>
* Default value: <code>false</code>
*/

View File

@ -18,40 +18,39 @@
*/
package com.cloud.agent.api.to;
import java.io.Serializable;
import com.cloud.agent.api.LogLevel;
import com.cloud.hypervisor.Hypervisor;
import java.io.Serializable;
public class RemoteInstanceTO implements Serializable {
private Hypervisor.HypervisorType hypervisorType;
private String hostName;
private String instanceName;
// Vmware Remote Instances parameters
// VMware Remote Instances parameters (required for exporting OVA through ovftool)
// TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes
private String vcenterUsername;
@LogLevel(LogLevel.Log4jLevel.Off)
private String vcenterPassword;
private String vcenterHost;
private String datacenterName;
private String clusterName;
public RemoteInstanceTO() {
}
public RemoteInstanceTO(String hostName, String instanceName, String vcenterHost,
String datacenterName, String clusterName,
String vcenterUsername, String vcenterPassword) {
public RemoteInstanceTO(String instanceName) {
this.hypervisorType = Hypervisor.HypervisorType.VMware;
this.instanceName = instanceName;
}
public RemoteInstanceTO(String instanceName, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) {
this.hypervisorType = Hypervisor.HypervisorType.VMware;
this.hostName = hostName;
this.instanceName = instanceName;
this.vcenterHost = vcenterHost;
this.datacenterName = datacenterName;
this.clusterName = clusterName;
this.vcenterUsername = vcenterUsername;
this.vcenterPassword = vcenterPassword;
this.datacenterName = datacenterName;
}
public Hypervisor.HypervisorType getHypervisorType() {
@ -62,10 +61,6 @@ public class RemoteInstanceTO implements Serializable {
return this.instanceName;
}
public String getHostName() {
return this.hostName;
}
public String getVcenterUsername() {
return vcenterUsername;
}
@ -81,8 +76,4 @@ public class RemoteInstanceTO implements Serializable {
public String getDatacenterName() {
return datacenterName;
}
public String getClusterName() {
return clusterName;
}
}

View File

@ -54,6 +54,7 @@ public interface Host extends StateObject<Status>, Identity, Partition, HAResour
}
public static final String HOST_UEFI_ENABLE = "host.uefi.enable";
public static final String HOST_VOLUME_ENCRYPTION = "host.volume.encryption";
public static final String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
/**
* @return name of the machine.

View File

@ -23,6 +23,7 @@ import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.framework.config.ConfigKey;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
@ -101,21 +102,20 @@ public interface HypervisorGuru extends Adapter {
* Will generate commands to migrate a vm to a pool. For now this will only work for stopped VMs on Vmware.
*
* @param vm the stopped vm to migrate
* @param destination the primary storage pool to migrate to
* @param volumeToPool the primary storage pools to migrate to
* @return a list of commands to perform for a successful migration
*/
List<Command> finalizeMigrate(VirtualMachine vm, Map<Volume, StoragePool> volumeToPool);
/**
* Will perform a clone of a VM on an external host (if the guru can handle)
* Will return the hypervisor VM (clone VM for PowerOn VMs), performs a clone of a VM if required on an external host (if the guru can handle)
* @param hostIp VM's source host IP
* @param vmName name of the source VM to clone from
* @param vmName name of the source VM (clone VM name if cloned)
* @param params hypervisor specific additional parameters
* @return a reference to the cloned VM
* @return a reference to the hypervisor or cloned VM, and cloned flag
*/
UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName,
Map<String, String> params);
Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map<String, String> params);
/**
* Removes a VM created as a clone of a VM on an external host
@ -124,6 +124,23 @@ public interface HypervisorGuru extends Adapter {
* @param params hypervisor specific additional parameters
* @return true if the operation succeeds, false if not
*/
boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName,
Map<String, String> params);
boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params);
/**
* Create an OVA/OVF template of a VM on an external host (if the guru can handle)
* @param hostIp VM's source host IP
* @param vmName name of the source VM to create template from
* @param params hypervisor specific additional parameters
* @param templateLocation datastore to create the template file
* @return the created template dir/name
*/
String createVMTemplateOutOfBand(String hostIp, String vmName, Map<String, String> params, DataStoreTO templateLocation, int threadsCountToExportOvf);
/**
* Removes the template on the location
* @param templateLocation datastore to remove the template file
* @param templateDir the template dir to remove from datastore
* @return true if the operation succeeds, false if not
*/
boolean removeVMTemplateOutOfBand(DataStoreTO templateLocation, String templateDir);
}

View File

@ -189,6 +189,7 @@ public class ApiConstants {
public static final String FORCED = "forced";
public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
public static final String FORCE_DELETE_HOST = "forcedeletehost";
public static final String FORCE_MS_TO_IMPORT_VM_FILES = "forcemstoimportvmfiles";
public static final String FORMAT = "format";
public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
public static final String FOR_SYSTEM_VMS = "forsystemvms";
@ -236,6 +237,7 @@ public class ApiConstants {
public static final String NEXT_ACL_RULE_ID = "nextaclruleid";
public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash";
public static final String IMAGE_PATH = "imagepath";
public static final String INSTANCE_CONVERSION_SUPPORTED = "instanceconversionsupported";
public static final String INTERNAL_DNS1 = "internaldns1";
public static final String INTERNAL_DNS2 = "internaldns2";
public static final String INTERNET_PROTOCOL = "internetprotocol";

View File

@ -37,6 +37,7 @@ import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VmwareDatacenterResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.vm.VmImportService;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
@ -118,40 +119,44 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
description = "Temp Path on external host for disk image copy" )
private String tmpPath;
// Import from Vmware to KVM migration parameters
// Import from VMware to KVM migration parameters
@Parameter(name = ApiConstants.EXISTING_VCENTER_ID,
type = CommandType.UUID,
entityType = VmwareDatacenterResponse.class,
description = "(only for importing migrated VMs from Vmware to KVM) UUID of a linked existing vCenter")
description = "(only for importing VMs from VMware to KVM) UUID of a linked existing vCenter")
private Long existingVcenterId;
@Parameter(name = ApiConstants.HOST_IP,
type = BaseCmd.CommandType.STRING,
description = "(only for importing migrated VMs from Vmware to KVM) VMware ESXi host IP/Name.")
description = "(only for importing VMs from VMware to KVM) VMware ESXi host IP/Name.")
private String hostip;
@Parameter(name = ApiConstants.VCENTER,
type = CommandType.STRING,
description = "(only for importing migrated VMs from Vmware to KVM) The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.")
description = "(only for importing VMs from VMware to KVM) The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.")
private String vcenter;
@Parameter(name = ApiConstants.DATACENTER_NAME, type = CommandType.STRING,
description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware datacenter.")
description = "(only for importing VMs from VMware to KVM) Name of VMware datacenter.")
private String datacenterName;
@Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING,
description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware cluster.")
description = "(only for importing VMs from VMware to KVM) Name of VMware cluster.")
private String clusterName;
@Parameter(name = ApiConstants.CONVERT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class,
description = "(only for importing migrated VMs from Vmware to KVM) optional - the host to perform the virt-v2v migration from VMware to KVM.")
description = "(only for importing VMs from VMware to KVM) optional - the host to perform the virt-v2v migration from VMware to KVM.")
private Long convertInstanceHostId;
@Parameter(name = ApiConstants.CONVERT_INSTANCE_STORAGE_POOL_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class,
description = "(only for importing migrated VMs from Vmware to KVM) optional - the temporary storage pool to perform the virt-v2v migration from VMware to KVM.")
description = "(only for importing VMs from VMware to KVM) optional - the temporary storage pool to perform the virt-v2v migration from VMware to KVM.")
private Long convertStoragePoolId;
@Parameter(name = ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES, type = CommandType.BOOLEAN,
description = "(only for importing VMs from VMware to KVM) optional - if true, forces MS to import VM file(s) to temporary storage, else uses KVM Host if ovftool is available, falls back to MS if not.")
private Boolean forceMsToImportVmFiles;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -200,6 +205,10 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
return convertStoragePoolId;
}
public Boolean getForceMsToImportVmFiles() {
return BooleanUtils.toBooleanDefaultIfNull(forceMsToImportVmFiles, false);
}
public String getHypervisor() {
return hypervisor;
}

View File

@ -29,6 +29,7 @@ import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
import com.cloud.host.Host;
import com.cloud.host.Status;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
@ -277,6 +278,10 @@ public class HostResponse extends BaseResponseWithAnnotations {
@Param(description = "true if the host supports encryption", since = "4.18")
private Boolean encryptionSupported;
@SerializedName(ApiConstants.INSTANCE_CONVERSION_SUPPORTED)
@Param(description = "true if the host supports instance conversion (using virt-v2v)", since = "4.19.1")
private Boolean instanceConversionSupported;
@Override
public String getObjectId() {
return this.getId();
@ -526,7 +531,7 @@ public class HostResponse extends BaseResponseWithAnnotations {
this.username = username;
}
public void setDetails(Map details) {
public void setDetails(Map details, Hypervisor.HypervisorType hypervisorType) {
if (details == null) {
return;
@ -547,6 +552,15 @@ public class HostResponse extends BaseResponseWithAnnotations {
this.setEncryptionSupported(new Boolean(false)); // default
}
if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) {
if (detailsCopy.containsKey(Host.HOST_INSTANCE_CONVERSION)) {
this.setInstanceConversionSupported(Boolean.parseBoolean((String) detailsCopy.get(Host.HOST_INSTANCE_CONVERSION)));
detailsCopy.remove(Host.HOST_INSTANCE_CONVERSION);
} else {
this.setInstanceConversionSupported(new Boolean(false)); // default
}
}
this.details = detailsCopy;
}
@ -737,6 +751,10 @@ public class HostResponse extends BaseResponseWithAnnotations {
this.encryptionSupported = encryptionSupported;
}
public void setInstanceConversionSupported(Boolean instanceConversionSupported) {
this.instanceConversionSupported = instanceConversionSupported;
}
public Boolean getIsTagARule() {
return isTagARule;
}

View File

@ -17,6 +17,8 @@
package org.apache.cloudstack.vm;
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.util.List;
public class UnmanagedInstanceTO {
@ -317,6 +319,16 @@ public class UnmanagedInstanceTO {
public int getDatastorePort() {
return datastorePort;
}
@Override
public String toString() {
return "Disk {" +
"diskId='" + diskId + '\'' +
", capacity=" + toHumanReadableSize(capacity) +
", controller='" + controller + '\'' +
", controllerUnit=" + controllerUnit +
"}";
}
}
public static class Nic {
@ -409,5 +421,14 @@ public class UnmanagedInstanceTO {
public void setPciSlot(String pciSlot) {
this.pciSlot = pciSlot;
}
@Override
public String toString() {
return "Nic{" +
"nicId='" + nicId + '\'' +
", adapterType='" + adapterType + '\'' +
", macAddress='" + macAddress + '\'' +
"}";
}
}
}

View File

@ -39,6 +39,37 @@ public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService,
ConfigKey.Scope.Global,
null);
ConfigKey<Integer> ConvertVmwareInstanceToKvmTimeout = new ConfigKey<>(Integer.class,
"convert.vmware.instance.to.kvm.timeout",
"Advanced",
"3",
"Timeout (in hours) for the instance conversion process from VMware through the virt-v2v binary on a KVM host",
true,
ConfigKey.Scope.Global,
null);
ConfigKey<Integer> ThreadsOnMSToImportVMwareVMFiles = new ConfigKey<>(Integer.class,
"threads.on.ms.to.import.vmware.vm.files",
"Advanced",
"0",
"Threads to use on the management server when importing VM files from VMWare." +
" -1 or 1 disables threads, 0 uses a thread per VM disk (disabled for single disk) and >1 for manual thread configuration." +
" Max number is 10, Default is 0.",
true,
ConfigKey.Scope.Global,
null);
ConfigKey<Integer> ThreadsOnKVMHostToImportVMwareVMFiles = new ConfigKey<>(Integer.class,
"threads.on.kvm.host.to.import.vmware.vm.files",
"Advanced",
"0",
"Threads to use on the KVM host (by the ovftool, if the version is 4.4.0+) when importing VM files from VMWare." +
" -1 or 1 disables threads, 0 uses a thread per VM disk (disabled for single disk) and >1 for manual thread configuration." +
" Max number is 10, Default is 0.",
true,
ConfigKey.Scope.Global,
null);
static boolean isSupported(Hypervisor.HypervisorType hypervisorType) {
return hypervisorType == VMware || hypervisorType == KVM;
}

View File

@ -23,6 +23,8 @@ import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import com.cloud.hypervisor.Hypervisor;
public final class HostResponseTest extends TestCase {
private static final String VALID_KEY = "validkey";
@ -32,7 +34,7 @@ public final class HostResponseTest extends TestCase {
public void testSetDetailsNull() {
final HostResponse hostResponse = new HostResponse();
hostResponse.setDetails(null);
hostResponse.setDetails(null, null);
assertEquals(null, hostResponse.getDetails());
@ -51,7 +53,7 @@ public final class HostResponseTest extends TestCase {
final Map expectedDetails = new HashedMap();
expectedDetails.put(VALID_KEY, VALID_VALUE);
hostResponse.setDetails(details);
hostResponse.setDetails(details, Hypervisor.HypervisorType.KVM);
final Map actualDetails = hostResponse.getDetails();
assertTrue(details != actualDetails);
@ -70,7 +72,7 @@ public final class HostResponseTest extends TestCase {
final Map expectedDetails = new HashedMap();
expectedDetails.put(VALID_KEY, VALID_VALUE);
hostResponse.setDetails(details);
hostResponse.setDetails(details, Hypervisor.HypervisorType.KVM);
final Map actualDetails = hostResponse.getDetails();
assertTrue(details != actualDetails);

View File

@ -0,0 +1,43 @@
// 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.agent.api;
public class CheckConvertInstanceAnswer extends Answer {
private boolean ovfExportSupported = false;
public CheckConvertInstanceAnswer() {
super();
}
public CheckConvertInstanceAnswer(Command command, boolean success) {
super(command, success, "");
}
public CheckConvertInstanceAnswer(Command command, boolean success, String details) {
super(command, success, details);
}
public CheckConvertInstanceAnswer(Command command, boolean success, boolean ovfExportSupported, String details) {
super(command, success, details);
this.ovfExportSupported = ovfExportSupported;
}
public boolean isOvfExportSupported() {
return ovfExportSupported;
}
}

View File

@ -0,0 +1,37 @@
// 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.agent.api;
public class CheckConvertInstanceCommand extends Command {
boolean checkWindowsGuestConversionSupport = false;
public CheckConvertInstanceCommand() {
}
public CheckConvertInstanceCommand(boolean checkWindowsGuestConversionSupport) {
this.checkWindowsGuestConversionSupport = checkWindowsGuestConversionSupport;
}
@Override
public boolean executeInSequence() {
return false;
}
public boolean getCheckWindowsGuestConversionSupport() {
return checkWindowsGuestConversionSupport;
}
}

View File

@ -28,16 +28,24 @@ public class ConvertInstanceCommand extends Command {
private Hypervisor.HypervisorType destinationHypervisorType;
private List<String> destinationStoragePools;
private DataStoreTO conversionTemporaryLocation;
private String templateDirOnConversionLocation;
private boolean checkConversionSupport;
private boolean exportOvfToConversionLocation;
private int threadsCountToExportOvf = 0;
public ConvertInstanceCommand() {
}
public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType,
List<String> destinationStoragePools, DataStoreTO conversionTemporaryLocation) {
List<String> destinationStoragePools, DataStoreTO conversionTemporaryLocation,
String templateDirOnConversionLocation, boolean checkConversionSupport, boolean exportOvfToConversionLocation) {
this.sourceInstance = sourceInstance;
this.destinationHypervisorType = destinationHypervisorType;
this.destinationStoragePools = destinationStoragePools;
this.conversionTemporaryLocation = conversionTemporaryLocation;
this.templateDirOnConversionLocation = templateDirOnConversionLocation;
this.checkConversionSupport = checkConversionSupport;
this.exportOvfToConversionLocation = exportOvfToConversionLocation;
}
public RemoteInstanceTO getSourceInstance() {
@ -56,6 +64,26 @@ public class ConvertInstanceCommand extends Command {
return conversionTemporaryLocation;
}
public String getTemplateDirOnConversionLocation() {
return templateDirOnConversionLocation;
}
public boolean getCheckConversionSupport() {
return checkConversionSupport;
}
public boolean getExportOvfToConversionLocation() {
return exportOvfToConversionLocation;
}
public int getThreadsCountToExportOvf() {
return threadsCountToExportOvf;
}
public void setThreadsCountToExportOvf(int threadsCountToExportOvf) {
this.threadsCountToExportOvf = threadsCountToExportOvf;
}
@Override
public boolean executeInSequence() {
return false;

View File

@ -97,14 +97,6 @@ public interface StorageManager extends StorageService {
true,
ConfigKey.Scope.Global,
null);
ConfigKey<Integer> ConvertVmwareInstanceToKvmTimeout = new ConfigKey<>(Integer.class,
"convert.vmware.instance.to.kvm.timeout",
"Storage",
"8",
"Timeout (in hours) for the instance conversion process from VMware through the virt-v2v binary on a KVM host",
true,
ConfigKey.Scope.Global,
null);
ConfigKey<Boolean> KvmAutoConvergence = new ConfigKey<>(Boolean.class,
"kvm.auto.convergence",
"Storage",

View File

@ -1972,20 +1972,24 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
private void updatePersistenceMap(Map<String, Boolean> vlanToPersistenceMap, NetworkVO networkVO) {
if (networkVO == null) {
return;
}
NetworkOfferingVO offeringVO = networkOfferingDao.findById(networkVO.getNetworkOfferingId());
if (offeringVO != null) {
if (offeringVO == null) {
return;
}
Pair<String, Boolean> data = getVMNetworkDetails(networkVO, offeringVO.isPersistent());
Boolean shouldDeleteNwResource = (MapUtils.isNotEmpty(vlanToPersistenceMap) && data != null) ? vlanToPersistenceMap.get(data.first()) : null;
if (data != null && (shouldDeleteNwResource == null || shouldDeleteNwResource)) {
vlanToPersistenceMap.put(data.first(), data.second());
}
}
}
private Map<String, Boolean> getVlanToPersistenceMapForVM(long vmId) {
List<UserVmJoinVO> userVmJoinVOs = userVmJoinDao.searchByIds(vmId);
Map<String, Boolean> vlanToPersistenceMap = new HashMap<>();
if (userVmJoinVOs != null && !userVmJoinVOs.isEmpty()) {
if (CollectionUtils.isNotEmpty(userVmJoinVOs)) {
for (UserVmJoinVO userVmJoinVO : userVmJoinVOs) {
NetworkVO networkVO = _networkDao.findById(userVmJoinVO.getNetworkId());
updatePersistenceMap(vlanToPersistenceMap, networkVO);

View File

@ -4607,10 +4607,16 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
final NicVO vo = Transaction.execute(new TransactionCallback<NicVO>() {
@Override
public NicVO doInTransaction(TransactionStatus status) {
NicVO existingNic = _nicDao.findByNetworkIdAndMacAddress(network.getId(), macAddress);
String macAddressToPersist = macAddress;
if (StringUtils.isBlank(macAddress)) {
throw new CloudRuntimeException("Mac address not specified");
}
String macAddressToPersist = macAddress.trim();
if (!NetUtils.isValidMac(macAddressToPersist)) {
throw new CloudRuntimeException("Invalid mac address: " + macAddressToPersist);
}
NicVO existingNic = _nicDao.findByNetworkIdAndMacAddress(network.getId(), macAddressToPersist);
if (existingNic != null) {
macAddressToPersist = generateNewMacAddressIfForced(network, macAddress, forced);
macAddressToPersist = generateNewMacAddressIfForced(network, macAddressToPersist, forced);
}
NicVO vo = new NicVO(network.getGuruName(), vm.getId(), network.getId(), vm.getType());
vo.setMacAddress(macAddressToPersist);
@ -4655,7 +4661,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network),
_networkModel.getNetworkTag(vm.getHypervisorType(), network));
return new Pair<NicProfile, Integer>(vmNic, Integer.valueOf(deviceId));
return new Pair<>(vmNic, Integer.valueOf(deviceId));
}
protected String getSelectedIpForNicImport(Network network, DataCenter dataCenter, Network.IpAddresses ipAddresses) {
@ -4699,7 +4705,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
private String generateNewMacAddressIfForced(Network network, String macAddress, boolean forced) {
if (!forced) {
throw new CloudRuntimeException("NIC with MAC address = " + macAddress + " exists on network with ID = " + network.getId() +
throw new CloudRuntimeException("NIC with MAC address " + macAddress + " exists on network with ID " + network.getUuid() +
" and forced flag is disabled");
}
try {

View File

@ -17,6 +17,7 @@
package org.apache.cloudstack.engine.orchestration;
import static org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService.NetworkLockTimeout;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@ -31,8 +32,10 @@ import java.util.List;
import java.util.Map;
import com.cloud.dc.DataCenter;
import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
import com.cloud.network.IpAddressManager;
import com.cloud.utils.Pair;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.Before;
@ -40,6 +43,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Matchers;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import com.cloud.api.query.dao.DomainRouterJoinDao;
@ -72,6 +76,8 @@ import com.cloud.network.vpc.VpcManager;
import com.cloud.network.vpc.VpcVO;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.Ip;
import com.cloud.vm.DomainRouterVO;
@ -892,4 +898,118 @@ public class NetworkOrchestratorTest extends TestCase {
verify(testOrchestrator._networksDao, times(1)).acquireInLockTable(networkId, NetworkLockTimeout.value());
verify(testOrchestrator._networksDao, times(1)).releaseFromLockTable(networkId);
}
@Test(expected = InsufficientVirtualNetworkCapacityException.class)
public void testImportNicAcquireGuestIPFailed() throws Exception {
DataCenter dataCenter = Mockito.mock(DataCenter.class);
VirtualMachine vm = mock(VirtualMachine.class);
Network network = Mockito.mock(Network.class);
Mockito.when(network.getGuestType()).thenReturn(GuestType.Isolated);
Mockito.when(network.getNetworkOfferingId()).thenReturn(networkOfferingId);
long dataCenterId = 1L;
Mockito.when(network.getDataCenterId()).thenReturn(dataCenterId);
Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class);
String ipAddress = "10.1.10.10";
Mockito.when(ipAddresses.getIp4Address()).thenReturn(ipAddress);
Mockito.when(testOrchestrator.getSelectedIpForNicImport(network, dataCenter, ipAddresses)).thenReturn(null);
Mockito.when(testOrchestrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.Dns, Service.Dhcp));
String macAddress = "02:01:01:82:00:01";
int deviceId = 0;
testOrchestrator.importNic(macAddress, deviceId, network, true, vm, ipAddresses, dataCenter, false);
}
@Test(expected = InsufficientVirtualNetworkCapacityException.class)
public void testImportNicAutoAcquireGuestIPFailed() throws Exception {
DataCenter dataCenter = Mockito.mock(DataCenter.class);
VirtualMachine vm = mock(VirtualMachine.class);
Network network = Mockito.mock(Network.class);
Mockito.when(network.getGuestType()).thenReturn(GuestType.Isolated);
Mockito.when(network.getNetworkOfferingId()).thenReturn(networkOfferingId);
long dataCenterId = 1L;
Mockito.when(network.getDataCenterId()).thenReturn(dataCenterId);
Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class);
String ipAddress = "auto";
Mockito.when(ipAddresses.getIp4Address()).thenReturn(ipAddress);
Mockito.when(testOrchestrator.getSelectedIpForNicImport(network, dataCenter, ipAddresses)).thenReturn(null);
Mockito.when(testOrchestrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.Dns, Service.Dhcp));
String macAddress = "02:01:01:82:00:01";
int deviceId = 0;
testOrchestrator.importNic(macAddress, deviceId, network, true, vm, ipAddresses, dataCenter, false);
}
@Test
public void testImportNicNoIP4Address() throws Exception {
DataCenter dataCenter = Mockito.mock(DataCenter.class);
Long vmId = 1L;
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
VirtualMachine vm = mock(VirtualMachine.class);
Mockito.when(vm.getId()).thenReturn(vmId);
Mockito.when(vm.getHypervisorType()).thenReturn(hypervisorType);
Long networkId = 1L;
Network network = Mockito.mock(Network.class);
Mockito.when(network.getId()).thenReturn(networkId);
Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class);
Mockito.when(ipAddresses.getIp4Address()).thenReturn(null);
URI broadcastUri = URI.create("vlan://123");
NicVO nic = mock(NicVO.class);
Mockito.when(nic.getBroadcastUri()).thenReturn(broadcastUri);
String macAddress = "02:01:01:82:00:01";
int deviceId = 1;
Integer networkRate = 200;
Mockito.when(testOrchestrator._networkModel.getNetworkRate(networkId, vmId)).thenReturn(networkRate);
Mockito.when(testOrchestrator._networkModel.isSecurityGroupSupportedInNetwork(network)).thenReturn(false);
Mockito.when(testOrchestrator._networkModel.getNetworkTag(hypervisorType, network)).thenReturn("testtag");
try (MockedStatic<Transaction> transactionMocked = Mockito.mockStatic(Transaction.class)) {
transactionMocked.when(() -> Transaction.execute(any(TransactionCallback.class))).thenReturn(nic);
Pair<NicProfile, Integer> nicProfileIntegerPair = testOrchestrator.importNic(macAddress, deviceId, network, true, vm, ipAddresses, dataCenter, false);
verify(testOrchestrator._networkModel, times(1)).getNetworkRate(networkId, vmId);
verify(testOrchestrator._networkModel, times(1)).isSecurityGroupSupportedInNetwork(network);
verify(testOrchestrator._networkModel, times(1)).getNetworkTag(Hypervisor.HypervisorType.KVM, network);
assertEquals(deviceId, nicProfileIntegerPair.second().intValue());
NicProfile nicProfile = nicProfileIntegerPair.first();
assertEquals(broadcastUri, nicProfile.getBroadCastUri());
assertEquals(networkRate, nicProfile.getNetworkRate());
assertFalse(nicProfile.isSecurityGroupEnabled());
assertEquals("testtag", nicProfile.getName());
}
}
@Test
public void testImportNicWithIP4Address() throws Exception {
DataCenter dataCenter = Mockito.mock(DataCenter.class);
Long vmId = 1L;
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
VirtualMachine vm = mock(VirtualMachine.class);
Mockito.when(vm.getId()).thenReturn(vmId);
Mockito.when(vm.getHypervisorType()).thenReturn(hypervisorType);
Long networkId = 1L;
Network network = Mockito.mock(Network.class);
Mockito.when(network.getId()).thenReturn(networkId);
String ipAddress = "10.1.10.10";
Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class);
Mockito.when(ipAddresses.getIp4Address()).thenReturn(ipAddress);
URI broadcastUri = URI.create("vlan://123");
NicVO nic = mock(NicVO.class);
Mockito.when(nic.getBroadcastUri()).thenReturn(broadcastUri);
String macAddress = "02:01:01:82:00:01";
int deviceId = 1;
Integer networkRate = 200;
Mockito.when(testOrchestrator._networkModel.getNetworkRate(networkId, vmId)).thenReturn(networkRate);
Mockito.when(testOrchestrator._networkModel.isSecurityGroupSupportedInNetwork(network)).thenReturn(false);
Mockito.when(testOrchestrator._networkModel.getNetworkTag(hypervisorType, network)).thenReturn("testtag");
try (MockedStatic<Transaction> transactionMocked = Mockito.mockStatic(Transaction.class)) {
transactionMocked.when(() -> Transaction.execute(any(TransactionCallback.class))).thenReturn(nic);
Pair<NicProfile, Integer> nicProfileIntegerPair = testOrchestrator.importNic(macAddress, deviceId, network, true, vm, ipAddresses, dataCenter, false);
verify(testOrchestrator, times(1)).getSelectedIpForNicImport(network, dataCenter, ipAddresses);
verify(testOrchestrator._networkModel, times(1)).getNetworkRate(networkId, vmId);
verify(testOrchestrator._networkModel, times(1)).isSecurityGroupSupportedInNetwork(network);
verify(testOrchestrator._networkModel, times(1)).getNetworkTag(Hypervisor.HypervisorType.KVM, network);
assertEquals(deviceId, nicProfileIntegerPair.second().intValue());
NicProfile nicProfile = nicProfileIntegerPair.first();
assertEquals(broadcastUri, nicProfile.getBroadCastUri());
assertEquals(networkRate, nicProfile.getNetworkRate());
assertFalse(nicProfile.isSecurityGroupEnabled());
assertEquals("testtag", nicProfile.getName());
}
}
}

View File

@ -141,6 +141,8 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat
List<HostVO> listByHostCapability(Host.Type type, Long clusterId, Long podId, long dcId, String hostCapabilty);
List<HostVO> listByClusterHypervisorTypeAndHostCapability(Long clusterId, HypervisorType hypervisorType, String hostCapabilty);
List<HostVO> listByClusterAndHypervisorType(long clusterId, HypervisorType hypervisorType);
HostVO findByName(String name);

View File

@ -344,6 +344,7 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
ClusterHypervisorSearch.and("hypervisor", ClusterHypervisorSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ);
ClusterHypervisorSearch.and("type", ClusterHypervisorSearch.entity().getType(), SearchCriteria.Op.EQ);
ClusterHypervisorSearch.and("status", ClusterHypervisorSearch.entity().getStatus(), SearchCriteria.Op.EQ);
ClusterHypervisorSearch.and("resourceState", ClusterHypervisorSearch.entity().getResourceState(), SearchCriteria.Op.EQ);
ClusterHypervisorSearch.done();
UnmanagedDirectConnectSearch = createSearchBuilder();
@ -1507,12 +1508,42 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
return listBy(sc);
}
@Override
public List<HostVO> listByClusterHypervisorTypeAndHostCapability(Long clusterId, HypervisorType hypervisorType, String hostCapabilty) {
SearchBuilder<DetailVO> hostCapabilitySearch = _detailsDao.createSearchBuilder();
DetailVO tagEntity = hostCapabilitySearch.entity();
hostCapabilitySearch.and("capability", tagEntity.getName(), SearchCriteria.Op.EQ);
hostCapabilitySearch.and("value", tagEntity.getValue(), SearchCriteria.Op.EQ);
SearchBuilder<HostVO> hostSearch = createSearchBuilder();
HostVO entity = hostSearch.entity();
hostSearch.and("clusterId", entity.getClusterId(), SearchCriteria.Op.EQ);
hostSearch.and("hypervisor", entity.getHypervisorType(), SearchCriteria.Op.EQ);
hostSearch.and("type", entity.getType(), SearchCriteria.Op.EQ);
hostSearch.and("status", entity.getStatus(), SearchCriteria.Op.EQ);
hostSearch.and("resourceState", entity.getResourceState(), SearchCriteria.Op.EQ);
hostSearch.join("hostCapabilitySearch", hostCapabilitySearch, entity.getId(), tagEntity.getHostId(), JoinBuilder.JoinType.INNER);
SearchCriteria<HostVO> sc = hostSearch.create();
sc.setJoinParameters("hostCapabilitySearch", "value", Boolean.toString(true));
sc.setJoinParameters("hostCapabilitySearch", "capability", hostCapabilty);
sc.setParameters("clusterId", clusterId);
sc.setParameters("hypervisor", hypervisorType);
sc.setParameters("type", Type.Routing);
sc.setParameters("status", Status.Up);
sc.setParameters("resourceState", ResourceState.Enabled);
return listBy(sc);
}
@Override
public List<HostVO> listByClusterAndHypervisorType(long clusterId, HypervisorType hypervisorType) {
SearchCriteria<HostVO> sc = ClusterHypervisorSearch.create();
sc.setParameters("clusterId", clusterId);
sc.setParameters("hypervisor", hypervisorType);
sc.setParameters("type", Type.Routing);
sc.setParameters("status", Status.Up);
sc.setParameters("resourceState", ResourceState.Enabled);
return listBy(sc);
}

View File

@ -21,6 +21,7 @@ import java.util.Map;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.ScopeType;
import com.cloud.storage.Storage;
import com.cloud.storage.StoragePoolStatus;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
@ -126,6 +127,10 @@ public interface PrimaryDataStoreDao extends GenericDao<StoragePoolVO, Long> {
List<StoragePoolVO> findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType, String keyword);
List<StoragePoolVO> findZoneWideStoragePoolsByHypervisorAndPoolType(long dataCenterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType);
List<StoragePoolVO> findClusterWideStoragePoolsByHypervisorAndPoolType(long clusterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType);
List<StoragePoolVO> findLocalStoragePoolsByHostAndTags(long hostId, String[] tags);
List<StoragePoolVO> listLocalStoragePoolByPath(long datacenterId, String path);

View File

@ -28,6 +28,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.storage.Storage;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import org.apache.commons.collections.CollectionUtils;
@ -621,6 +622,28 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
return sc.list();
}
@Override
public List<StoragePoolVO> findZoneWideStoragePoolsByHypervisorAndPoolType(long dataCenterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType) {
QueryBuilder<StoragePoolVO> sc = QueryBuilder.create(StoragePoolVO.class);
sc.and(sc.entity().getDataCenterId(), Op.EQ, dataCenterId);
sc.and(sc.entity().getStatus(), Op.EQ, StoragePoolStatus.Up);
sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE);
sc.and(sc.entity().getHypervisor(), Op.EQ, hypervisorType);
sc.and(sc.entity().getPoolType(), Op.EQ, poolType);
return sc.list();
}
@Override
public List<StoragePoolVO> findClusterWideStoragePoolsByHypervisorAndPoolType(long clusterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType) {
QueryBuilder<StoragePoolVO> sc = QueryBuilder.create(StoragePoolVO.class);
sc.and(sc.entity().getClusterId(), Op.EQ, clusterId);
sc.and(sc.entity().getStatus(), Op.EQ, StoragePoolStatus.Up);
sc.and(sc.entity().getScope(), Op.EQ, ScopeType.CLUSTER);
sc.and(sc.entity().getHypervisor(), Op.EQ, hypervisorType);
sc.and(sc.entity().getPoolType(), Op.EQ, poolType);
return sc.list();
}
@Override
public void deletePoolTags(long poolId) {
_tagsDao.deleteTags(poolId);

View File

@ -16,6 +16,7 @@
// under the License.
package com.cloud.hypervisor.kvm.resource;
import static com.cloud.host.Host.HOST_INSTANCE_CONVERSION;
import static com.cloud.host.Host.HOST_VOLUME_ENCRYPTION;
import java.io.BufferedReader;
@ -305,6 +306,16 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
public static final String TUNGSTEN_PATH = "scripts/vm/network/tungsten";
public static final String INSTANCE_CONVERSION_SUPPORTED_CHECK_CMD = "virt-v2v --version";
// virt-v2v --version => sample output: virt-v2v 1.42.0rhel=8,release=22.module+el8.10.0+1590+a67ab969
public static final String OVF_EXPORT_SUPPORTED_CHECK_CMD = "ovftool --version";
// ovftool --version => sample output: VMware ovftool 4.6.0 (build-21452615)
public static final String OVF_EXPORT_TOOl_GET_VERSION_CMD = "ovftool --version | awk '{print $3}'";
public static final String WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD = "rpm -qa | grep -i virtio-win";
public static final String UBUNTU_WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD = "dpkg -l virtio-win";
public static final String UBUNTU_NBDKIT_PKG_CHECK_CMD = "dpkg -l nbdkit";
private String modifyVlanPath;
private String versionStringPath;
private String patchScriptPath;
@ -3634,6 +3645,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
cmd.setGatewayIpAddress(localGateway);
cmd.setIqn(getIqn());
cmd.getHostDetails().put(HOST_VOLUME_ENCRYPTION, String.valueOf(hostSupportsVolumeEncryption()));
cmd.getHostDetails().put(HOST_INSTANCE_CONVERSION, String.valueOf(hostSupportsInstanceConversion()));
HealthCheckResult healthCheckResult = getHostHealthCheckResult();
if (healthCheckResult != HealthCheckResult.IGNORE) {
cmd.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS);
@ -5137,6 +5149,48 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return false;
}
public boolean hostSupportsInstanceConversion() {
int exitValue = Script.runSimpleBashScriptForExitValue(INSTANCE_CONVERSION_SUPPORTED_CHECK_CMD);
if (isUbuntuHost() && exitValue == 0) {
exitValue = Script.runSimpleBashScriptForExitValue(UBUNTU_NBDKIT_PKG_CHECK_CMD);
}
return exitValue == 0;
}
public boolean hostSupportsWindowsGuestConversion() {
if (isUbuntuHost()) {
int exitValue = Script.runSimpleBashScriptForExitValue(UBUNTU_WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD);
return exitValue == 0;
}
int exitValue = Script.runSimpleBashScriptForExitValue(WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD);
return exitValue == 0;
}
public boolean hostSupportsOvfExport() {
int exitValue = Script.runSimpleBashScriptForExitValue(OVF_EXPORT_SUPPORTED_CHECK_CMD);
return exitValue == 0;
}
public boolean ovfExportToolSupportsParallelThreads() {
String ovfExportToolVersion = Script.runSimpleBashScript(OVF_EXPORT_TOOl_GET_VERSION_CMD);
if (StringUtils.isBlank(ovfExportToolVersion)) {
return false;
}
String[] ovfExportToolVersions = ovfExportToolVersion.trim().split("\\.");
if (ovfExportToolVersions.length > 1) {
try {
int majorVersion = Integer.parseInt(ovfExportToolVersions[0]);
int minorVersion = Integer.parseInt(ovfExportToolVersions[1]);
//ovftool version >= 4.4 supports parallel threads
if (majorVersion > 4 || (majorVersion == 4 && minorVersion >= 4)) {
return true;
}
} catch (NumberFormatException ignored) {
}
}
return false;
}
private void setCpuTopology(CpuModeDef cmd, int vCpusInDef, Map<String, String> details) {
if (!enableManuallySettingCpuTopologyOnKvmVm) {
s_logger.debug(String.format("Skipping manually setting CPU topology on VM's XML due to it is disabled in agent.properties {\"property\": \"%s\", \"value\": %s}.",

View File

@ -0,0 +1,57 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
package com.cloud.hypervisor.kvm.resource.wrapper;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckConvertInstanceAnswer;
import com.cloud.agent.api.CheckConvertInstanceCommand;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
@ResourceWrapper(handles = CheckConvertInstanceCommand.class)
public class LibvirtCheckConvertInstanceCommandWrapper extends CommandWrapper<CheckConvertInstanceCommand, Answer, LibvirtComputingResource> {
private static final Logger s_logger = Logger.getLogger(LibvirtCheckConvertInstanceCommandWrapper.class);
@Override
public Answer execute(CheckConvertInstanceCommand cmd, LibvirtComputingResource serverResource) {
if (!serverResource.hostSupportsInstanceConversion()) {
String msg = String.format("Cannot convert the instance from VMware as the virt-v2v binary is not found on host %s. " +
"Please install virt-v2v%s on the host before attempting the instance conversion.", serverResource.getPrivateIp(), serverResource.isUbuntuHost()? ", nbdkit" : "");
s_logger.info(msg);
return new CheckConvertInstanceAnswer(cmd, false, msg);
}
if (cmd.getCheckWindowsGuestConversionSupport() && !serverResource.hostSupportsWindowsGuestConversion()) {
String msg = String.format("Cannot convert the instance from VMware as the virtio-win package is not found on host %s. " +
"Please install virtio-win package on the host before attempting the windows guest instance conversion.", serverResource.getPrivateIp());
s_logger.info(msg);
return new CheckConvertInstanceAnswer(cmd, false, msg);
}
if (serverResource.hostSupportsOvfExport()) {
return new CheckConvertInstanceAnswer(cmd, true, true, "");
}
return new CheckConvertInstanceAnswer(cmd, true, "");
}
}

View File

@ -65,8 +65,6 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
private static final List<Hypervisor.HypervisorType> supportedInstanceConvertSourceHypervisors =
List.of(Hypervisor.HypervisorType.VMware);
protected static final String checkIfConversionIsSupportedCommand = "which virt-v2v";
@Override
public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serverResource) {
RemoteInstanceTO sourceInstance = cmd.getSourceInstance();
@ -77,9 +75,9 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation();
long timeout = (long) cmd.getWait() * 1000;
if (!isInstanceConversionSupportedOnHost()) {
if (cmd.getCheckConversionSupport() && !serverResource.hostSupportsInstanceConversion()) {
String msg = String.format("Cannot convert the instance %s from VMware as the virt-v2v binary is not found. " +
"Please install virt-v2v on the host before attempting the instance conversion", sourceInstanceName);
"Please install virt-v2v%s on the host before attempting the instance conversion.", sourceInstanceName, serverResource.isUbuntuHost()? ", nbdkit" : "");
s_logger.info(msg);
return new ConvertInstanceAnswer(cmd, false, msg);
}
@ -94,21 +92,48 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr();
KVMStoragePool temporaryStoragePool = getTemporaryStoragePool(conversionTemporaryLocation, storagePoolMgr);
s_logger.info(String.format("Attempting to convert the instance %s from %s to KVM",
sourceInstanceName, sourceHypervisorType));
final String convertInstanceUrl = getConvertInstanceUrl(sourceInstance);
final String temporaryConvertUuid = UUID.randomUUID().toString();
final String temporaryPasswordFilePath = createTemporaryPasswordFileAndRetrievePath(sourceInstance);
final String temporaryConvertPath = temporaryStoragePool.getLocalPath();
String ovfTemplateDirOnConversionLocation;
String sourceOVFDirPath;
boolean ovfExported = false;
if (cmd.getExportOvfToConversionLocation()) {
String exportInstanceOVAUrl = getExportInstanceOVAUrl(sourceInstance);
if (StringUtils.isBlank(exportInstanceOVAUrl)) {
String err = String.format("Couldn't export OVA for the VM %s, due to empty url", sourceInstanceName);
s_logger.error(err);
return new ConvertInstanceAnswer(cmd, false, err);
}
int noOfThreads = cmd.getThreadsCountToExportOvf();
if (noOfThreads > 1 && !serverResource.ovfExportToolSupportsParallelThreads()) {
noOfThreads = 0;
}
ovfTemplateDirOnConversionLocation = UUID.randomUUID().toString();
temporaryStoragePool.createFolder(ovfTemplateDirOnConversionLocation);
sourceOVFDirPath = String.format("%s/%s/", temporaryConvertPath, ovfTemplateDirOnConversionLocation);
ovfExported = exportOVAFromVMOnVcenter(exportInstanceOVAUrl, sourceOVFDirPath, noOfThreads, timeout);
if (!ovfExported) {
String err = String.format("Export OVA for the VM %s failed", sourceInstanceName);
s_logger.error(err);
return new ConvertInstanceAnswer(cmd, false, err);
}
sourceOVFDirPath = String.format("%s%s/", sourceOVFDirPath, sourceInstanceName);
} else {
ovfTemplateDirOnConversionLocation = cmd.getTemplateDirOnConversionLocation();
sourceOVFDirPath = String.format("%s/%s/", temporaryConvertPath, ovfTemplateDirOnConversionLocation);
}
s_logger.info(String.format("Attempting to convert the OVF %s of the instance %s from %s to KVM", ovfTemplateDirOnConversionLocation, sourceInstanceName, sourceHypervisorType));
final String temporaryConvertUuid = UUID.randomUUID().toString();
boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled();
try {
boolean result = performInstanceConversion(convertInstanceUrl, sourceInstanceName, temporaryPasswordFilePath,
temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled);
boolean result = performInstanceConversion(sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid,
timeout, verboseModeEnabled);
if (!result) {
String err = String.format("The virt-v2v conversion of the instance %s failed. " +
"Please check the agent logs for the virt-v2v output", sourceInstanceName);
String err = String.format("The virt-v2v conversion for the OVF %s failed. " +
"Please check the agent logs for the virt-v2v output", ovfTemplateDirOnConversionLocation);
s_logger.error(err);
return new ConvertInstanceAnswer(cmd, false, err);
}
@ -133,8 +158,11 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
s_logger.error(error, e);
return new ConvertInstanceAnswer(cmd, false, error);
} finally {
s_logger.debug("Cleaning up instance conversion temporary password file");
Script.runSimpleBashScript(String.format("rm -rf %s", temporaryPasswordFilePath));
if (ovfExported && StringUtils.isNotBlank(ovfTemplateDirOnConversionLocation)) {
String sourceOVFDir = String.format("%s/%s", temporaryConvertPath, ovfTemplateDirOnConversionLocation);
s_logger.debug("Cleaning up exported OVA at dir " + sourceOVFDir);
Script.runSimpleBashScript("rm -rf " + sourceOVFDir);
}
if (conversionTemporaryLocation instanceof NfsTO) {
s_logger.debug("Cleaning up secondary storage temporary location");
storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid());
@ -158,6 +186,27 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
supportedInstanceConvertSourceHypervisors.contains(sourceHypervisorType);
}
private String getExportInstanceOVAUrl(RemoteInstanceTO sourceInstance) {
String url = null;
if (sourceInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
url = getExportOVAUrlFromRemoteInstance(sourceInstance);
}
return url;
}
private String getExportOVAUrlFromRemoteInstance(RemoteInstanceTO vmwareInstance) {
String vcenter = vmwareInstance.getVcenterHost();
String username = vmwareInstance.getVcenterUsername();
String password = vmwareInstance.getVcenterPassword();
String datacenter = vmwareInstance.getDatacenterName();
String vm = vmwareInstance.getInstanceName();
String encodedUsername = encodeUsername(username);
String encodedPassword = encodeUsername(password);
return String.format("vi://%s:%s@%s/%s/vm/%s",
encodedUsername, encodedPassword, vcenter, datacenter, vm);
}
protected List<KVMPhysicalDisk> getTemporaryDisksFromParsedXml(KVMStoragePool pool, LibvirtDomainXMLParser xmlParser, String convertedBasePath) {
List<LibvirtVMDef.DiskDef> disksDefs = xmlParser.getDisks();
disksDefs = disksDefs.stream().filter(x -> x.getDiskType() == LibvirtVMDef.DiskDef.DiskType.FILE &&
@ -207,11 +256,6 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
Script.runSimpleBashScript(String.format("rm -f %s/%s*.xml", temporaryStoragePool.getLocalPath(), temporaryConvertUuid));
}
protected boolean isInstanceConversionSupportedOnHost() {
int exitValue = Script.runSimpleBashScriptForExitValue(checkIfConversionIsSupportedCommand);
return exitValue == 0;
}
protected void sanitizeDisksPath(List<LibvirtVMDef.DiskDef> disks) {
for (LibvirtVMDef.DiskDef disk : disks) {
String[] diskPathParts = disk.getDiskPath().split("/");
@ -237,6 +281,11 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
s_logger.error(err);
continue;
}
if (destinationPool.getType() != Storage.StoragePoolType.NetworkFilesystem) {
String err = String.format("Storage pool by URI: %s is not an NFS storage", poolPath);
s_logger.error(err);
continue;
}
KVMPhysicalDisk sourceDisk = temporaryDisks.get(i);
if (s_logger.isDebugEnabled()) {
String msg = String.format("Trying to copy converted instance disk number %s from the temporary location %s" +
@ -308,25 +357,45 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
String sourceHostIp = null;
String sourcePath = null;
String storagePoolMountPoint = Script.runSimpleBashScript(String.format("mount | grep %s", storagePool.getLocalPath()));
s_logger.debug(String.format("NFS Storage pool: %s - local path: %s, mount point: %s", storagePool.getUuid(), storagePool.getLocalPath(), storagePoolMountPoint));
if (StringUtils.isNotEmpty(storagePoolMountPoint)) {
String[] res = storagePoolMountPoint.strip().split(" ");
res = res[0].split(":");
if (res.length > 1) {
sourceHostIp = res[0].strip();
sourcePath = res[1].strip();
}
}
return new Pair<>(sourceHostIp, sourcePath);
}
protected boolean performInstanceConversion(String convertInstanceUrl, String sourceInstanceName,
String temporaryPasswordFilePath,
private boolean exportOVAFromVMOnVcenter(String vmExportUrl,
String targetOvfDir,
int noOfThreads,
long timeout) {
Script script = new Script("ovftool", timeout, s_logger);
script.add("--noSSLVerify");
if (noOfThreads > 1) {
script.add(String.format("--parallelThreads=%s", noOfThreads));
}
script.add(vmExportUrl);
script.add(targetOvfDir);
String logPrefix = "export ovf";
OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix);
script.execute(outputLogger);
int exitValue = script.getExitValue();
return exitValue == 0;
}
protected boolean performInstanceConversion(String sourceOVFDirPath,
String temporaryConvertFolder,
String temporaryConvertUuid,
long timeout, boolean verboseModeEnabled) {
Script script = new Script("virt-v2v", timeout, s_logger);
script.add("--root", "first");
script.add("-ic", convertInstanceUrl);
script.add(sourceInstanceName);
script.add("--password-file", temporaryPasswordFilePath);
script.add("-i", "ova");
script.add(sourceOVFDirPath);
script.add("-o", "local");
script.add("-os", temporaryConvertFolder);
script.add("-of", "qcow2");
@ -335,44 +404,13 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
script.add("-v");
}
String logPrefix = String.format("virt-v2v source: %s %s progress", convertInstanceUrl, sourceInstanceName);
String logPrefix = String.format("virt-v2v ovf source: %s progress", sourceOVFDirPath);
OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix);
script.execute(outputLogger);
int exitValue = script.getExitValue();
return exitValue == 0;
}
private String createTemporaryPasswordFileAndRetrievePath(RemoteInstanceTO sourceInstance) {
String password = null;
if (sourceInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
password = sourceInstance.getVcenterPassword();
}
String passwordFile = String.format("/tmp/vmw-%s", UUID.randomUUID());
String msg = String.format("Creating a temporary password file for VMware instance %s conversion on: %s", sourceInstance.getInstanceName(), passwordFile);
s_logger.debug(msg);
Script.runSimpleBashScriptForExitValueAvoidLogging(String.format("echo \"%s\" > %s", password, passwordFile));
return passwordFile;
}
private String getConvertInstanceUrl(RemoteInstanceTO sourceInstance) {
String url = null;
if (sourceInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
url = getConvertInstanceUrlFromVmware(sourceInstance);
}
return url;
}
private String getConvertInstanceUrlFromVmware(RemoteInstanceTO vmwareInstance) {
String vcenter = vmwareInstance.getVcenterHost();
String datacenter = vmwareInstance.getDatacenterName();
String username = vmwareInstance.getVcenterUsername();
String host = vmwareInstance.getHostName();
String cluster = vmwareInstance.getClusterName();
String encodedUsername = encodeUsername(username);
return String.format("vpx://%s@%s/%s/%s/%s?no_verify=1",
encodedUsername, vcenter, datacenter, cluster, host);
}
protected LibvirtDomainXMLParser parseMigratedVMXmlDomain(String installPath) throws IOException {
String xmlPath = String.format("%s.xml", installPath);
if (!new File(xmlPath).exists()) {

View File

@ -0,0 +1,67 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
package com.cloud.hypervisor.kvm.resource.wrapper;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.apache.commons.lang3.StringUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckConvertInstanceCommand;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
@RunWith(MockitoJUnitRunner.class)
public class LibvirtCheckConvertInstanceCommandWrapperTest {
@Spy
private LibvirtCheckConvertInstanceCommandWrapper checkConvertInstanceCommandWrapper = Mockito.spy(LibvirtCheckConvertInstanceCommandWrapper.class);
@Mock
private LibvirtComputingResource libvirtComputingResourceMock;
@Mock
CheckConvertInstanceCommand checkConvertInstanceCommandMock;
@Before
public void setUp() {
}
@Test
public void testCheckInstanceCommand_success() {
Mockito.when(libvirtComputingResourceMock.hostSupportsInstanceConversion()).thenReturn(true);
Answer answer = checkConvertInstanceCommandWrapper.execute(checkConvertInstanceCommandMock, libvirtComputingResourceMock);
assertTrue(answer.getResult());
}
@Test
public void testCheckInstanceCommand_failure() {
Mockito.when(libvirtComputingResourceMock.hostSupportsInstanceConversion()).thenReturn(false);
Answer answer = checkConvertInstanceCommandWrapper.execute(checkConvertInstanceCommandMock, libvirtComputingResourceMock);
assertFalse(answer.getResult());
assertTrue(StringUtils.isNotBlank(answer.getDetails()));
}
}

View File

@ -71,12 +71,6 @@ public class LibvirtConvertInstanceCommandWrapperTest {
private static final String secondaryPoolUrl = "nfs://192.168.1.1/secondary";
private static final String vmName = "VmToImport";
private static final String hostName = "VmwareHost1";
private static final String vmwareVcenter = "192.168.1.2";
private static final String vmwareDatacenter = "Datacenter";
private static final String vmwareCluster = "Cluster";
private static final String vmwareUsername = "administrator@vsphere.local";
private static final String vmwarePassword = "password";
@Before
public void setUp() {
@ -88,15 +82,6 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2));
}
@Test
public void testIsInstanceConversionSupportedOnHost() {
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(0);
boolean supported = convertInstanceCommandWrapper.isInstanceConversionSupportedOnHost();
Assert.assertTrue(supported);
}
}
@Test
public void testAreSourceAndDestinationHypervisorsSupported() {
boolean supported = convertInstanceCommandWrapper.areSourceAndDestinationHypervisorsSupported(Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.KVM);
@ -191,6 +176,7 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Mockito.when(destDisk.getPath()).thenReturn("xyz");
Mockito.when(storagePoolManager.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, destinationPoolUuid))
.thenReturn(destinationPool);
Mockito.when(destinationPool.getType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
Mockito.when(storagePoolManager.copyPhysicalDisk(Mockito.eq(sourceDisk), Mockito.anyString(), Mockito.eq(destinationPool), Mockito.anyInt()))
.thenReturn(destDisk);
@ -244,21 +230,16 @@ public class LibvirtConvertInstanceCommandWrapperTest {
RemoteInstanceTO remoteInstanceTO = Mockito.mock(RemoteInstanceTO.class);
Mockito.when(remoteInstanceTO.getHypervisorType()).thenReturn(hypervisorType);
Mockito.when(remoteInstanceTO.getInstanceName()).thenReturn(vmName);
Mockito.when(remoteInstanceTO.getHostName()).thenReturn(hostName);
Mockito.when(remoteInstanceTO.getVcenterHost()).thenReturn(vmwareVcenter);
Mockito.when(remoteInstanceTO.getDatacenterName()).thenReturn(vmwareDatacenter);
Mockito.when(remoteInstanceTO.getClusterName()).thenReturn(vmwareCluster);
Mockito.when(remoteInstanceTO.getVcenterUsername()).thenReturn(vmwareUsername);
Mockito.when(remoteInstanceTO.getVcenterPassword()).thenReturn(vmwarePassword);
return remoteInstanceTO;
}
private ConvertInstanceCommand getConvertInstanceCommand(RemoteInstanceTO remoteInstanceTO, Hypervisor.HypervisorType hypervisorType) {
private ConvertInstanceCommand getConvertInstanceCommand(RemoteInstanceTO remoteInstanceTO, Hypervisor.HypervisorType hypervisorType, boolean checkConversionSupport) {
ConvertInstanceCommand cmd = Mockito.mock(ConvertInstanceCommand.class);
Mockito.when(cmd.getSourceInstance()).thenReturn(remoteInstanceTO);
Mockito.when(cmd.getDestinationHypervisorType()).thenReturn(hypervisorType);
Mockito.when(cmd.getWait()).thenReturn(14400);
Mockito.when(cmd.getConversionTemporaryLocation()).thenReturn(secondaryDataStore);
Mockito.when(cmd.getCheckConversionSupport()).thenReturn(checkConversionSupport);
return cmd;
}
@ -266,8 +247,8 @@ public class LibvirtConvertInstanceCommandWrapperTest {
public void testExecuteConvertUnsupportedOnTheHost() {
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.VMware);
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM);
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(1);
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM, true);
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtComputingResource.INSTANCE_CONVERSION_SUPPORTED_CHECK_CMD)).thenReturn(1);
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
Assert.assertFalse(answer.getResult());
}
@ -277,8 +258,8 @@ public class LibvirtConvertInstanceCommandWrapperTest {
public void testExecuteConvertUnsupportedHypervisors() {
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.XenServer);
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM);
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(0);
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM, true);
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtComputingResource.INSTANCE_CONVERSION_SUPPORTED_CHECK_CMD)).thenReturn(0);
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
Assert.assertFalse(answer.getResult());
}
@ -287,7 +268,7 @@ public class LibvirtConvertInstanceCommandWrapperTest {
@Test
public void testExecuteConvertFailure() {
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.VMware);
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM);
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM, true);
String localMountPoint = "/mnt/xyz";
Mockito.when(temporaryPool.getLocalPath()).thenReturn(localMountPoint);
@ -297,15 +278,15 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Mockito.when(mock.getExitValue()).thenReturn(1);
})
) {
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(0);
Mockito.when(Script.runSimpleBashScriptForExitValueAvoidLogging(Mockito.anyString())).thenReturn(0);
Mockito.when(libvirtComputingResourceMock.hostSupportsInstanceConversion()).thenReturn(true);
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtComputingResource.INSTANCE_CONVERSION_SUPPORTED_CHECK_CMD)).thenReturn(0);
Mockito.when(Script.runSimpleBashScriptForExitValue(Mockito.anyString())).thenReturn(0);
Mockito.when(Script.runSimpleBashScript(Mockito.anyString())).thenReturn("");
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
Assert.assertFalse(answer.getResult());
Mockito.verify(convertInstanceCommandWrapper).performInstanceConversion(Mockito.anyString(),
Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean());
Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean());
}
}
}

View File

@ -18,6 +18,7 @@ package com.cloud.hypervisor.guru;
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
@ -28,10 +29,12 @@ import java.util.UUID;
import javax.inject.Inject;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.util.VmwareClient;
import com.cloud.hypervisor.vmware.util.VmwareHelper;
import com.cloud.utils.script.Script;
import com.cloud.vm.VmDetailConstants;
import com.vmware.vim25.VirtualMachinePowerState;
import org.apache.cloudstack.acl.ControlledEntity;
@ -42,12 +45,14 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.storage.NfsMountManager;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DownloadCommand;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
@ -195,6 +200,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
@Inject DiskOfferingDao diskOfferingDao;
@Inject PhysicalNetworkDao physicalNetworkDao;
@Inject StoragePoolHostDao storagePoolHostDao;
@Inject NfsMountManager mountManager;
protected VMwareGuru() {
super();
@ -1342,7 +1348,13 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
DatacenterMO dataCenterMO) throws Exception {
HostMO sourceHost = vmMo.getRunningHost();
String cloneName = UUID.randomUUID().toString();
DatastoreMO datastoreMO = vmMo.getAllDatastores().get(0); //pick the first datastore
List<DatastoreMO> vmDatastores = vmMo.getAllDatastores();
if (CollectionUtils.isEmpty(vmDatastores)) {
String err = String.format("Unable to fetch datastores, could not clone VM %s for migration from VMware", vmName);
s_logger.error(err);
throw new CloudRuntimeException(err);
}
DatastoreMO datastoreMO = vmDatastores.get(0); //pick the first datastore
ManagedObjectReference morPool = vmMo.getRunningHost().getHyperHostOwnerResourcePool();
boolean result = vmMo.createFullClone(cloneName, dataCenterMO.getVmFolder(), morPool, datastoreMO.getMor(), Storage.ProvisioningType.THIN);
VirtualMachineMO clonedVM = dataCenterMO.findVm(cloneName);
@ -1351,14 +1363,23 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
s_logger.error(err);
throw new CloudRuntimeException(err);
}
relocateClonedVMToSourceHost(clonedVM, sourceHost);
return clonedVM;
}
private String createOVFTemplateOfVM(VirtualMachineMO vmMO, DataStoreTO convertLocation, int threadsCountToExportOvf) throws Exception {
String dataStoreUrl = getDataStoreUrlForTemplate(convertLocation);
String vmOvfName = UUID.randomUUID().toString();
String vmOvfCreationPath = createDirOnStorage(vmOvfName, dataStoreUrl, null);
s_logger.debug(String.format("Creating OVF %s for the VM %s at %s", vmOvfName, vmMO.getName(), vmOvfCreationPath));
vmMO.exportVm(vmOvfCreationPath, vmOvfName, false, false, threadsCountToExportOvf);
s_logger.debug(String.format("Created OVF %s for the VM %s at %s", vmOvfName, vmMO.getName(), vmOvfCreationPath));
return vmOvfName;
}
@Override
public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName,
Map<String, String> params) {
s_logger.debug(String.format("Cloning VM %s on external vCenter %s", vmName, hostIp));
public Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map<String, String> params) {
String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST);
String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
@ -1369,25 +1390,45 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter);
VirtualMachineMO vmMo = dataCenterMO.findVm(vmName);
if (vmMo == null) {
String err = String.format("Cannot find VM with name %s on %s/%s", vmName, vcenter, datacenter);
String err = String.format("Cannot find VM with name %s on vCenter %s/%s", vmName, vcenter, datacenter);
s_logger.error(err);
throw new CloudRuntimeException(err);
}
VirtualMachinePowerState sourceVmPowerState = vmMo.getPowerState();
if (sourceVmPowerState == VirtualMachinePowerState.POWERED_ON && isWindowsVm(vmMo)) {
s_logger.debug(String.format("VM %s is a Windows VM and its Running, cannot be imported." +
"Please gracefully shut it down before attempting the import",
vmName));
if (sourceVmPowerState == VirtualMachinePowerState.POWERED_OFF) {
// Don't clone for powered off VMs, can export OVF from it
UnmanagedInstanceTO instanceTO = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
return new Pair<>(instanceTO, false);
}
if (sourceVmPowerState == VirtualMachinePowerState.POWERED_ON) {
if (isWindowsVm(vmMo)) {
String err = String.format("VM %s is a Windows VM and its Running, cannot be imported." +
" Please gracefully shut it down before attempting the import", vmName);
s_logger.error(err);
throw new CloudRuntimeException(err);
}
if (isVMOnStandaloneHost(vmMo)) { // or datacenter.equalsIgnoreCase("ha-datacenter")? [Note: default datacenter name on standalone host: ha-datacenter]
String err = String.format("VM %s might be on standalone host and is Running, cannot be imported." +
" Please shut it down before attempting the import", vmName);
s_logger.error(err);
throw new CloudRuntimeException(err);
}
}
s_logger.debug(String.format("Cloning VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO);
s_logger.debug(String.format("VM %s cloned successfully", vmName));
s_logger.debug(String.format("VM %s cloned successfully, to VM %s", vmName, clonedVM.getName()));
UnmanagedInstanceTO clonedInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), clonedVM);
setNicsFromSourceVM(clonedInstance, vmMo);
setDisksFromSourceVM(clonedInstance, vmMo);
clonedInstance.setCloneSourcePowerState(sourceVmPowerState == VirtualMachinePowerState.POWERED_ON ? UnmanagedInstanceTO.PowerState.PowerOn : UnmanagedInstanceTO.PowerState.PowerOff);
return clonedInstance;
return new Pair<>(clonedInstance, true);
} catch (CloudRuntimeException cre) {
throw cre;
} catch (Exception e) {
String err = String.format("Error cloning VM: %s from external vCenter %s: %s", vmName, vcenter, e.getMessage());
String err = String.format("Error while finding or cloning VM: %s from vCenter %s: %s", vmName, vcenter, e.getMessage());
s_logger.error(err, e);
throw new CloudRuntimeException(err, e);
}
@ -1398,7 +1439,12 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
return sourceInstance.getOperatingSystem().toLowerCase().contains("windows");
}
private void setNicsFromSourceVM(UnmanagedInstanceTO clonedInstance, VirtualMachineMO vmMo) throws Exception {
private boolean isVMOnStandaloneHost(VirtualMachineMO vmMo) throws Exception {
UnmanagedInstanceTO sourceInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
return StringUtils.isEmpty(sourceInstance.getClusterName());
}
private void setDisksFromSourceVM(UnmanagedInstanceTO clonedInstance, VirtualMachineMO vmMo) throws Exception {
UnmanagedInstanceTO sourceInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
List<UnmanagedInstanceTO.Disk> sourceDisks = sourceInstance.getDisks();
List<UnmanagedInstanceTO.Disk> clonedDisks = clonedInstance.getDisks();
@ -1410,12 +1456,40 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
}
@Override
public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
s_logger.debug(String.format("Removing VM %s on external vCenter %s", vmName, hostIp));
public String createVMTemplateOutOfBand(String hostIp, String vmName, Map<String, String> params, DataStoreTO templateLocation, int threadsCountToExportOvf) {
String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST);
String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD);
s_logger.debug(String.format("Creating template of the VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
try {
VmwareContext context = connectToVcenter(vcenter, username, password);
DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter);
VirtualMachineMO vmMo = dataCenterMO.findVm(vmName);
if (vmMo == null) {
String err = String.format("Cannot find VM with name %s on vCenter %s/%s, to create template file", vmName, vcenter, datacenter);
s_logger.error(err);
throw new CloudRuntimeException(err);
}
String ovaTemplate = createOVFTemplateOfVM(vmMo, templateLocation, threadsCountToExportOvf);
s_logger.debug(String.format("OVF %s created successfully on the datastore", ovaTemplate));
return ovaTemplate;
} catch (Exception e) {
String err = String.format("Error create template file of the VM: %s from vCenter %s: %s", vmName, vcenter, e.getMessage());
s_logger.error(err, e);
throw new CloudRuntimeException(err, e);
}
}
@Override
public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST);
String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD);
s_logger.debug(String.format("Removing cloned VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
try {
VmwareContext context = connectToVcenter(vcenter, username, password);
DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter);
@ -1426,11 +1500,97 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
s_logger.error(err);
return false;
}
return vmMo.destroy();
} catch (Exception e) {
String err = String.format("Error destroying external VM %s: %s", vmName, e.getMessage());
String err = String.format("Error destroying cloned VM %s: %s", vmName, e.getMessage());
s_logger.error(err, e);
return false;
}
}
@Override
public boolean removeVMTemplateOutOfBand(DataStoreTO templateLocation, String templateDir) {
s_logger.debug(String.format("Removing template %s", templateDir));
try {
String dataStoreUrl = getDataStoreUrlForTemplate(templateLocation);
return deleteDirOnStorage(templateDir, dataStoreUrl, null);
} catch (Exception e) {
String err = String.format("Error removing template file %s: %s", templateDir, e.getMessage());
s_logger.error(err, e);
return false;
}
}
private String getDataStoreUrlForTemplate(DataStoreTO templateLocation) {
String dataStoreUrl = null;
if (templateLocation instanceof NfsTO) {
NfsTO nfsStore = (NfsTO) templateLocation;
dataStoreUrl = nfsStore.getUrl();
} else if (templateLocation instanceof PrimaryDataStoreTO) {
PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) templateLocation;
if (primaryDataStoreTO.getPoolType().equals(Storage.StoragePoolType.NetworkFilesystem)) {
String psHost = primaryDataStoreTO.getHost();
String psPath = primaryDataStoreTO.getPath();
dataStoreUrl = "nfs://" + psHost + File.separator + psPath;
}
}
if (dataStoreUrl == null) {
throw new CloudRuntimeException("Only NFS storage is supported for template creation");
}
return dataStoreUrl;
}
private String createDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception {
String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion);
s_logger.debug("Create dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName);
String dirMountPath = mountPoint + File.separator + dirName;
createDir(dirMountPath);
return dirMountPath;
}
private void createDir(String dirName) throws Exception {
synchronized (dirName.intern()) {
Script command = new Script("mkdir", s_logger);
command.add("-p");
command.add(dirName);
String cmdResult = command.execute();
if (cmdResult != null) {
String msg = "Unable to create directory: " + dirName + ", error msg: " + cmdResult;
s_logger.error(msg);
throw new Exception(msg);
}
}
}
private boolean deleteDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception {
try {
String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion);
s_logger.debug("Delete dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName);
String dirMountPath = mountPoint + File.separator + dirName;
deleteDir(dirMountPath);
return true;
} catch (Exception e) {
String err = String.format("Unable to delete dir %s: %s", dirName, e.getMessage());
s_logger.error(err, e);
return false;
}
}
private void deleteDir(String dirName) throws Exception {
synchronized (dirName.intern()) {
Script command = new Script("rm", s_logger);
command.add("-rf");
command.add(dirName);
String cmdResult = command.execute();
if (cmdResult != null) {
String msg = "Unable to delete directory: " + dirName + ", error msg: " + cmdResult;
s_logger.error(msg);
throw new Exception(msg);
}
}
}
}

View File

@ -17,21 +17,37 @@
package com.cloud.hypervisor.guru;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.cloudstack.storage.NfsMountManager;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@ -41,9 +57,19 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.MigrateVmToPoolCommand;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.util.VmwareClient;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.cloud.hypervisor.vmware.util.VmwareHelper;
import com.cloud.storage.Storage;
import com.cloud.storage.Storage.ProvisioningType;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
@ -51,8 +77,15 @@ import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.Pair;
import com.cloud.utils.UuidUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.VmDetailConstants;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.ServiceContent;
import com.vmware.vim25.VimPortType;
import com.vmware.vim25.VirtualMachinePowerState;
@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@ -79,6 +112,21 @@ public class VMwareGuruTest {
AutoCloseable closeable;
@Mock
NfsMountManager mountManager;
private static MockedStatic<VmwareHelper> mockedVmwareHelper;
@BeforeClass
public static void init() {
mockedVmwareHelper = Mockito.mockStatic(VmwareHelper.class);
}
@AfterClass
public static void close() {
mockedVmwareHelper.close();
}
@Before
public void testSetUp() throws Exception {
closeable = MockitoAnnotations.openMocks(this);
@ -155,4 +203,415 @@ public class VMwareGuruTest {
assertEquals(expected, result);
}
@Test(expected=CloudRuntimeException.class)
public void testCloneHypervisorVM_NoExternalVM() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(null).when(mockDatacenterMO).findVm(vmName);
})) {
vMwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(hostIp, vmName, params);
}
}
@Test(expected=CloudRuntimeException.class)
public void testCloneHypervisorVM_WindowsVMRunning() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
HostMO hostMo = Mockito.mock(HostMO.class);
Mockito.doReturn(VirtualMachinePowerState.POWERED_ON).when(vmMo).getPowerState();
Mockito.doReturn(hostMo).when(vmMo).getRunningHost();
UnmanagedInstanceTO instance = Mockito.mock(UnmanagedInstanceTO.class);
Mockito.doReturn("Windows 2019").when(instance).getOperatingSystem();
when(VmwareHelper.getUnmanagedInstance(hostMo, vmMo)).thenReturn(instance);
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName);
})) {
vMwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(hostIp, vmName, params);
}
}
@Test(expected=CloudRuntimeException.class)
public void testCloneHypervisorVM_GetDatastoresFailed() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
HostMO hostMo = Mockito.mock(HostMO.class);
Mockito.doReturn(VirtualMachinePowerState.POWERED_ON).when(vmMo).getPowerState();
Mockito.doReturn(hostMo).when(vmMo).getRunningHost();
UnmanagedInstanceTO instance = Mockito.mock(UnmanagedInstanceTO.class);
Mockito.doReturn("CentOS").when(instance).getOperatingSystem();
Mockito.doReturn("test-cluster").when(instance).getClusterName();
when(VmwareHelper.getUnmanagedInstance(hostMo, vmMo)).thenReturn(instance);
List<DatastoreMO> datastores = new ArrayList<>();
Mockito.doReturn(datastores).when(vmMo).getAllDatastores();
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName);
})) {
vMwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(hostIp, vmName, params);
}
}
@Test(expected=CloudRuntimeException.class)
public void testCloneHypervisorVM_CloneVMFailed() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
HostMO hostMo = Mockito.mock(HostMO.class);
Mockito.doReturn(VirtualMachinePowerState.POWERED_ON).when(vmMo).getPowerState();
Mockito.doReturn(hostMo).when(vmMo).getRunningHost();
Mockito.doReturn(mor).when(hostMo).getHyperHostOwnerResourcePool();
UnmanagedInstanceTO instance = Mockito.mock(UnmanagedInstanceTO.class);
Mockito.doReturn("CentOS").when(instance).getOperatingSystem();
Mockito.doReturn("test-cluster").when(instance).getClusterName();
when(VmwareHelper.getUnmanagedInstance(hostMo, vmMo)).thenReturn(instance);
DatastoreMO datastoreMO = Mockito.mock(DatastoreMO.class);
Mockito.doReturn(mor).when(datastoreMO).getMor();
List<DatastoreMO> datastores = new ArrayList<>();
datastores.add(datastoreMO);
Mockito.doReturn(datastores).when(vmMo).getAllDatastores();
Mockito.lenient().doReturn(false).when(vmMo).createFullClone(anyString(), any(ManagedObjectReference.class), any(ManagedObjectReference.class), any(ManagedObjectReference.class), any(Storage.ProvisioningType.class));
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(anyString());
Mockito.doReturn(mor).when(mockDatacenterMO).getVmFolder();
Mockito.doReturn(mor).when(mockDatacenterMO).getMor();
})) {
vMwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(hostIp, vmName, params);
}
}
@Test
public void testCloneHypervisorVM() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
HostMO hostMo = Mockito.mock(HostMO.class);
Mockito.doReturn(VirtualMachinePowerState.POWERED_ON).when(vmMo).getPowerState();
Mockito.doReturn(hostMo).when(vmMo).getRunningHost();
Mockito.doReturn(mor).when(hostMo).getHyperHostOwnerResourcePool();
Mockito.doReturn(mor).when(hostMo).getMor();
DatastoreMO datastoreMO = Mockito.mock(DatastoreMO.class);
Mockito.doReturn(mor).when(datastoreMO).getMor();
List<DatastoreMO> datastores = new ArrayList<>();
datastores.add(datastoreMO);
Mockito.doReturn(datastores).when(vmMo).getAllDatastores();
Mockito.lenient().doReturn(true).when(vmMo).createFullClone(anyString(), any(ManagedObjectReference.class), any(ManagedObjectReference.class), any(ManagedObjectReference.class), any(Storage.ProvisioningType.class));
UnmanagedInstanceTO instance = Mockito.mock(UnmanagedInstanceTO.class);
Mockito.doReturn("CentOS").when(instance).getOperatingSystem();
Mockito.doReturn("test-cluster").when(instance).getClusterName();
when(VmwareHelper.getUnmanagedInstance(hostMo, vmMo)).thenReturn(instance);
UnmanagedInstanceTO.Disk disk = Mockito.mock(UnmanagedInstanceTO.Disk.class);
Mockito.doReturn("1").when(disk).getDiskId();
List<UnmanagedInstanceTO.Disk> disks = new ArrayList<>();
disks.add(disk);
Mockito.doReturn(disks).when(instance).getDisks();
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(anyString());
Mockito.doReturn(mor).when(mockDatacenterMO).getVmFolder();
Mockito.doReturn(mor).when(mockDatacenterMO).getMor();
})) {
Pair<UnmanagedInstanceTO, Boolean> clonedVm = vMwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(hostIp, vmName, params);
assertNotNull(clonedVm);
}
}
@Test(expected=CloudRuntimeException.class)
public void testCreateVMTemplateFileOutOfBand_NoClonedVM() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "cloned-test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
DataStoreTO dataStore = Mockito.mock(DataStoreTO.class);
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(null).when(mockDatacenterMO).findVm(vmName);
})) {
vMwareGuru.createVMTemplateOutOfBand(hostIp, vmName, params, dataStore, -1);
}
}
@Test
public void testCreateVMTemplateFileOutOfBand() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "cloned-test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
Mockito.doNothing().when(vmMo).exportVm(anyString(), anyString(), anyBoolean(), anyBoolean(), anyInt());
NfsTO dataStore = Mockito.mock(NfsTO.class);
Mockito.doReturn("nfs://10.1.1.4/testdir").when(dataStore).getUrl();
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName);
})) {
String vmTemplate = vMwareGuru.createVMTemplateOutOfBand(hostIp, vmName, params, dataStore, -1);
assertNotNull(vmTemplate);
assertTrue(UuidUtils.isUuid(vmTemplate));
}
}
@Test
public void testRemoveClonedHypervisorVM_NoClonedVM() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "cloned-test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(null).when(mockDatacenterMO).findVm(vmName);
})) {
boolean result = vMwareGuru.removeClonedHypervisorVMOutOfBand(hostIp, vmName, params);
assertFalse(result);
}
}
@Test
public void testRemoveClonedHypervisorVM() throws Exception {
String vCenterHost = "10.1.1.2";
String datacenterName = "datacenter";
String hostIp = "10.1.1.3";
String vmName = "cloned-test-vm";
Map<String, String> params = new HashMap<>();
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost);
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username");
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password");
ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class);
ServiceContent serviceContent = Mockito.mock(ServiceContent.class);
VimPortType vimPort = Mockito.mock(VimPortType.class);
VmwareClient vimClient = spy(new VmwareClient(vCenterHost));
VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost));
Mockito.doReturn(vimClient).when(vmwareContext).getVimClient();
Mockito.doReturn(mor).when(vmwareContext).getRootFolder();
Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString());
DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName));
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
Mockito.doReturn(true).when(vmMo).destroy();
try (MockedConstruction<VmwareClient> ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> {
Mockito.doReturn(vimPort).when(mockVmwareClient).getService();
Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent();
Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString());
Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder();
}); MockedConstruction<VmwareContext> ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> {
Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient();
Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder();
}); MockedConstruction<DatacenterMO> ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> {
Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName);
})) {
boolean result = vMwareGuru.removeClonedHypervisorVMOutOfBand(hostIp, vmName, params);
assertTrue(result);
}
}
@Test
public void testRemoveVMTemplateFileOutOfBand() throws Exception {
NfsTO dataStore = Mockito.mock(NfsTO.class);
Mockito.doReturn("nfs://10.1.1.4/testdir").when(dataStore).getUrl();
String templateDir = "f887b7b3-3d1f-4a7d-93e5-3147f58866c6";
boolean result = vMwareGuru.removeVMTemplateOutOfBand(dataStore, templateDir);
assertTrue(result);
}
}

View File

@ -5127,7 +5127,7 @@ public class ApiResponseHelper implements ResponseGenerator {
} else if (instance.getHostName() != null) {
response.setHostName(instance.getHostName());
}
response.setPowerState(instance.getPowerState().toString());
response.setPowerState((instance.getPowerState() != null)? instance.getPowerState().toString() : UnmanagedInstanceTO.PowerState.PowerUnknown.toString());
response.setCpuCores(instance.getCpuCores());
response.setCpuSpeed(instance.getCpuSpeed());
response.setCpuCoresPerSocket(instance.getCpuCoresPerSocket());

View File

@ -247,7 +247,7 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
host.getHypervisorType() == Hypervisor.HypervisorType.Custom)) {
//only kvm has the requirement to return host details
try {
hostResponse.setDetails(hostDetails);
hostResponse.setDetails(hostDetails, host.getHypervisorType());
} catch (Exception e) {
s_logger.debug("failed to get host details", e);
}

View File

@ -467,9 +467,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
private long _defaultPageSize = Long.parseLong(Config.DefaultPageSize.getDefaultValue());
private static final String DOMAIN_NAME_PATTERN = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{1,63}$";
protected Set<String> configValuesForValidation;
private Set<String> weightBasedParametersForValidation;
private Set<String> overprovisioningFactorsForValidation;
private Set<String> configValuesForValidation = new HashSet<String>();
private Set<String> weightBasedParametersForValidation = new HashSet<String>();
private Set<String> overprovisioningFactorsForValidation = new HashSet<String>();
public static final ConfigKey<Boolean> SystemVMUseLocalStorage = new ConfigKey<Boolean>(Boolean.class, "system.vm.use.local.storage", "Advanced", "false",
"Indicates whether to use local storage pools or shared storage pools for system VMs.", false, ConfigKey.Scope.Zone, null);
@ -530,8 +530,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
return true;
}
private void populateConfigValuesForValidationSet() {
configValuesForValidation = new HashSet<String>();
protected void populateConfigValuesForValidationSet() {
configValuesForValidation.add("event.purge.interval");
configValuesForValidation.add("account.cleanup.interval");
configValuesForValidation.add("alert.wait");
@ -559,10 +558,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
configValuesForValidation.add(StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.key());
configValuesForValidation.add(UserDataManager.VM_USERDATA_MAX_LENGTH_STRING);
configValuesForValidation.add(UnmanagedVMsManager.RemoteKvmInstanceDisksCopyTimeout.key());
configValuesForValidation.add(UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.key());
}
private void weightBasedParametersForValidation() {
weightBasedParametersForValidation = new HashSet<String>();
weightBasedParametersForValidation.add(AlertManager.CPUCapacityThreshold.key());
weightBasedParametersForValidation.add(AlertManager.StorageAllocatedCapacityThreshold.key());
weightBasedParametersForValidation.add(AlertManager.StorageCapacityThreshold.key());
@ -582,11 +581,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
weightBasedParametersForValidation.add(CapacityManager.SecondaryStorageCapacityThreshold.key());
weightBasedParametersForValidation.add(ClusterDrsService.ClusterDrsImbalanceThreshold.key());
weightBasedParametersForValidation.add(ClusterDrsService.ClusterDrsImbalanceSkipThreshold.key());
}
private void overProvisioningFactorsForValidation() {
overprovisioningFactorsForValidation = new HashSet<String>();
overprovisioningFactorsForValidation.add(CapacityManager.MemOverprovisioningFactor.key());
overprovisioningFactorsForValidation.add(CapacityManager.CpuOverprovisioningFactor.key());
overprovisioningFactorsForValidation.add(CapacityManager.StorageOverprovisioningFactor.key());
@ -1173,8 +1170,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
return new Pair<Configuration, String>(_configDao.findByName(name), newValue);
}
private String validateConfigurationValue(final String name, String value, final String scope) {
protected String validateConfigurationValue(final String name, String value, final String scope) {
final ConfigurationVO cfg = _configDao.findByName(name);
if (cfg == null) {
s_logger.error("Missing configuration variable " + name + " in configuration table");
@ -1256,23 +1252,25 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
return null;
}
if (type.equals(Integer.class) && NetworkModel.MACIdentifier.key().equalsIgnoreCase(name)) {
if (type.equals(Integer.class)) {
try {
final int val = Integer.parseInt(value);
if (NetworkModel.MACIdentifier.key().equalsIgnoreCase(name)) {
//The value need to be between 0 to 255 because the mac generation needs a value of 8 bit
//0 value is considered as disable.
if(val < 0 || val > 255){
throw new InvalidParameterValueException(name + " value should be between 0 and 255. 0 value will disable this feature");
}
} catch (final NumberFormatException e) {
s_logger.error("There was an error trying to parse the integer value for:" + name);
throw new InvalidParameterValueException("There was an error trying to parse the integer value for:" + name);
}
if (UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.key().equalsIgnoreCase(name) || UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles.key().equalsIgnoreCase(name)) {
if (val > 10) {
throw new InvalidParameterValueException("Please enter a value between 0 and 10 for the configuration parameter: " + name + ", -1 will disable it");
}
}
if (type.equals(Integer.class) && configValuesForValidation.contains(name)) {
try {
final int val = Integer.parseInt(value);
if (configValuesForValidation.contains(name)) {
if (val <= 0) {
throw new InvalidParameterValueException("Please enter a positive value for the configuration parameter:" + name);
}
@ -1292,9 +1290,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
throw new InvalidParameterValueException("Please enter a value less than 1048576 for the configuration parameter:" + name);
}
}
}
} catch (final NumberFormatException e) {
s_logger.error("There was an error trying to parse the integer value for:" + name);
throw new InvalidParameterValueException("There was an error trying to parse the integer value for:" + name);
s_logger.error("There was an error trying to parse the integer value for configuration parameter: " + name);
throw new InvalidParameterValueException("There was an error trying to parse the integer value for configuration parameter: " + name);
}
}
@ -1305,8 +1304,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
throw new InvalidParameterValueException("Please enter a value between 0 and 1 for the configuration parameter: " + name);
}
} catch (final NumberFormatException e) {
s_logger.error("There was an error trying to parse the float value for:" + name);
throw new InvalidParameterValueException("There was an error trying to parse the float value for:" + name);
s_logger.error("There was an error trying to parse the float value for configuration parameter: " + name);
throw new InvalidParameterValueException("There was an error trying to parse the float value for configuration parameter: " + name);
}
}

View File

@ -35,6 +35,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
@ -377,7 +378,7 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
}
@Override
public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
public Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map<String, String> params) {
s_logger.error("Unsupported operation: cannot clone external VM");
return null;
}
@ -387,4 +388,16 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
s_logger.error("Unsupported operation: cannot remove external VM");
return false;
}
@Override
public String createVMTemplateOutOfBand(String hostIp, String vmName, Map<String, String> params, DataStoreTO templateLocation, int threadsCountToExportOvf) {
s_logger.error("Unsupported operation: cannot create template file");
return null;
}
@Override
public boolean removeVMTemplateOutOfBand(DataStoreTO templateLocation, String templateDir) {
s_logger.error("Unsupported operation: cannot remove template file");
return false;
}
}

View File

@ -3834,7 +3834,6 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
MountDisabledStoragePool,
VmwareCreateCloneFull,
VmwareAllowParallelExecution,
ConvertVmwareInstanceToKvmTimeout,
DataStoreDownloadFollowRedirects
};
}

View File

@ -19,6 +19,8 @@ package org.apache.cloudstack.vm;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckConvertInstanceAnswer;
import com.cloud.agent.api.CheckConvertInstanceCommand;
import com.cloud.agent.api.CheckVolumeAnswer;
import com.cloud.agent.api.CheckVolumeCommand;
import com.cloud.agent.api.ConvertInstanceAnswer;
@ -95,7 +97,6 @@ import com.cloud.storage.ScopeType;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateVO;
@ -687,7 +688,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
// Check for duplicate hostname in network, get all vms hostNames in the network
List<String> hostNames = vmDao.listDistinctHostNames(network.getId());
if (CollectionUtils.isNotEmpty(hostNames) && hostNames.contains(hostName)) {
throw new InvalidParameterValueException(String.format("VM with Name [%s] already exists in the network [%s] domain [%s]. Cannot import another VM with the same name. Pleasy try again with a different name.", hostName, network, network.getNetworkDomain()));
throw new InvalidParameterValueException(String.format("VM with Name [%s] already exists in the network [%s] domain [%s]. Cannot import another VM with the same name. Please try again with a different name.", hostName, network, network.getNetworkDomain()));
}
}
@ -1531,17 +1532,30 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return userVm;
}
private UnmanagedInstanceTO cloneSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM) {
private Pair<UnmanagedInstanceTO, Boolean> getSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username,
String password, String clusterName, String sourceHostName,
String sourceVM) {
HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
Map<String, String> params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName,
username, password, clusterName, sourceHostName, sourceVM);
return vmwareGuru.cloneHypervisorVMOutOfBand(sourceHostName, sourceVM, params);
return vmwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(sourceHostName, sourceVM, params);
}
private String createOvfTemplateOfSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username,
String password, String clusterName, String sourceHostName,
String sourceVMwareInstanceName, DataStoreTO convertLocation, int threadsCountToExportOvf) {
HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
Map<String, String> params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName,
username, password, clusterName, sourceHostName, sourceVMwareInstanceName);
return vmwareGuru.createVMTemplateOutOfBand(sourceHostName, sourceVMwareInstanceName, params, convertLocation, threadsCountToExportOvf);
}
protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster destinationCluster, VMTemplateVO template,
String sourceVM, String displayName, String hostName,
String sourceVMName, String displayName, String hostName,
Account caller, Account owner, long userId,
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap,
Map<String, Long> nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap,
@ -1568,7 +1582,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
if (existingVcenterId != null) {
VmwareDatacenterVO existingDC = vmwareDatacenterDao.findById(existingVcenterId);
if (existingDC == null) {
String err = String.format("Cannot find any existing Vmware DC with ID %s", existingVcenterId);
String err = String.format("Cannot find any existing VMware DC with ID %s", existingVcenterId);
LOGGER.error(err);
throw new CloudRuntimeException(err);
}
@ -1578,21 +1592,52 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
password = existingDC.getPassword();
}
UnmanagedInstanceTO clonedInstance = null;
boolean isClonedInstance = false;
UnmanagedInstanceTO sourceVMwareInstance = null;
DataStoreTO temporaryConvertLocation = null;
String ovfTemplateOnConvertLocation = null;
try {
HostVO convertHost = selectInstanceConversionKVMHostInCluster(destinationCluster, convertInstanceHostId);
CheckConvertInstanceAnswer conversionSupportAnswer = checkConversionSupportOnHost(convertHost, sourceVMName, false);
LOGGER.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" +
" from VMware to KVM ", convertHost.getId(), convertHost.getName(), sourceVMName));
temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId);
List<StoragePoolVO> convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster);
long importStartTime = System.currentTimeMillis();
Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName);
sourceVMwareInstance = sourceInstanceDetails.first();
isClonedInstance = sourceInstanceDetails.second();
boolean isWindowsVm = sourceVMwareInstance.getOperatingSystem().toLowerCase().contains("windows");
if (isWindowsVm) {
checkConversionSupportOnHost(convertHost, sourceVMName, true);
}
String instanceName = getGeneratedInstanceName(owner);
clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
clusterName, sourceHostName, sourceVM);
checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced);
UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password,
sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, convertStoragePoolId);
sanitizeConvertedInstance(convertedInstance, clonedInstance);
checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, sourceVMwareInstance, nicNetworkMap, nicIpAddressMap, forced);
UnmanagedInstanceTO convertedInstance;
if (cmd.getForceMsToImportVmFiles() || !conversionSupportAnswer.isOvfExportSupported()) {
// Uses MS for OVF export to temporary conversion location
int noOfThreads = UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.value();
ovfTemplateOnConvertLocation = createOvfTemplateOfSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
clusterName, sourceHostName, sourceVMwareInstance.getName(), temporaryConvertLocation, noOfThreads);
convertedInstance = convertVmwareInstanceToKVMWithOVFOnConvertLocation(sourceVMName, sourceVMwareInstance, convertHost, convertStoragePools,
temporaryConvertLocation, ovfTemplateOnConvertLocation);
} else {
// Uses KVM Host for OVF export to temporary conversion location, through ovftool
convertedInstance = convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(sourceVMName, sourceVMwareInstance, convertHost, convertStoragePools,
temporaryConvertLocation, vcenter, username, password, datacenterName);
}
sanitizeConvertedInstance(convertedInstance, sourceVMwareInstance);
UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null,
template, displayName, hostName, caller, owner, userId,
serviceOffering, dataDiskOfferingMap,
nicNetworkMap, nicIpAddressMap,
details, false, forced, false);
LOGGER.debug(String.format("VM %s imported successfully", sourceVM));
long timeElapsedInSecs = (System.currentTimeMillis() - importStartTime) / 1000;
LOGGER.debug(String.format("VMware VM %s imported successfully to CloudStack instance %s (%s), Time taken: %d secs, OVF files imported from %s, Source VMware VM details - OS: %s, PowerState: %s, Disks: %s, NICs: %s",
sourceVMName, instanceName, displayName, timeElapsedInSecs, (ovfTemplateOnConvertLocation != null)? "MS" : "KVM Host", sourceVMwareInstance.getOperatingSystem(), sourceVMwareInstance.getPowerState(), sourceVMwareInstance.getDisks(), sourceVMwareInstance.getNics()));
return userVm;
} catch (CloudRuntimeException e) {
LOGGER.error(String.format("Error importing VM: %s", e.getMessage()), e);
@ -1600,20 +1645,25 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
cmd.getEventDescription(), null, null, 0);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
} finally {
removeClonedInstance(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM);
if (isClonedInstance && sourceVMwareInstance != null) {
removeClonedInstance(vcenter, datacenterName, username, password, sourceHostName, sourceVMwareInstance.getName(), sourceVMName);
}
if (temporaryConvertLocation != null && StringUtils.isNotBlank(ovfTemplateOnConvertLocation)) {
removeTemplate(temporaryConvertLocation, ovfTemplateOnConvertLocation);
}
}
}
private void checkNetworkingBeforeConvertingVmwareInstance(DataCenter zone, Account owner, String instanceName,
String hostName, UnmanagedInstanceTO clonedInstance,
String hostName, UnmanagedInstanceTO sourceVMwareInstance,
Map<String, Long> nicNetworkMap,
Map<String, Network.IpAddresses> nicIpAddressMap,
boolean forced) {
List<UnmanagedInstanceTO.Nic> nics = clonedInstance.getNics();
List<UnmanagedInstanceTO.Nic> nics = sourceVMwareInstance.getNics();
List<Long> networkIds = new ArrayList<>(nicNetworkMap.values());
if (nics.size() != networkIds.size()) {
String msg = String.format("Different number of nics found on instance %s: %s vs %s nics provided",
clonedInstance.getName(), nics.size(), networkIds.size());
sourceVMwareInstance.getName(), nics.size(), networkIds.size());
LOGGER.error(msg);
throw new CloudRuntimeException(msg);
}
@ -1641,8 +1691,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
private void checkUnmanagedNicAndNetworkMacAddressForImport(NetworkVO network, UnmanagedInstanceTO.Nic nic, boolean forced) {
NicVO existingNic = nicDao.findByNetworkIdAndMacAddress(network.getId(), nic.getMacAddress());
if (existingNic != null && !forced) {
String err = String.format("NIC with MAC address = %s exists on network with ID = %s and forced flag is disabled",
nic.getMacAddress(), network.getId());
String err = String.format("NIC with MAC address %s already exists on network with ID %s and forced flag is disabled. " +
"Retry with forced flag enabled if a new MAC address to be generated.", nic.getMacAddress(), network.getUuid());
LOGGER.error(err);
throw new CloudRuntimeException(err);
}
@ -1657,38 +1707,44 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return VirtualMachineName.getVmName(id, owner.getId(), instanceSuffix);
}
private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, UnmanagedInstanceTO clonedInstance) {
convertedInstance.setCpuCores(clonedInstance.getCpuCores());
convertedInstance.setCpuSpeed(clonedInstance.getCpuSpeed());
convertedInstance.setCpuCoresPerSocket(clonedInstance.getCpuCoresPerSocket());
convertedInstance.setMemory(clonedInstance.getMemory());
private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, UnmanagedInstanceTO sourceVMwareInstance) {
convertedInstance.setCpuCores(sourceVMwareInstance.getCpuCores());
convertedInstance.setCpuSpeed(sourceVMwareInstance.getCpuSpeed());
convertedInstance.setCpuCoresPerSocket(sourceVMwareInstance.getCpuCoresPerSocket());
convertedInstance.setMemory(sourceVMwareInstance.getMemory());
convertedInstance.setPowerState(UnmanagedInstanceTO.PowerState.PowerOff);
List<UnmanagedInstanceTO.Disk> convertedInstanceDisks = convertedInstance.getDisks();
List<UnmanagedInstanceTO.Disk> clonedInstanceDisks = clonedInstance.getDisks();
List<UnmanagedInstanceTO.Disk> sourceVMwareInstanceDisks = sourceVMwareInstance.getDisks();
for (int i = 0; i < convertedInstanceDisks.size(); i++) {
UnmanagedInstanceTO.Disk disk = convertedInstanceDisks.get(i);
disk.setDiskId(clonedInstanceDisks.get(i).getDiskId());
disk.setDiskId(sourceVMwareInstanceDisks.get(i).getDiskId());
}
List<UnmanagedInstanceTO.Nic> convertedInstanceNics = convertedInstance.getNics();
List<UnmanagedInstanceTO.Nic> clonedInstanceNics = clonedInstance.getNics();
if (CollectionUtils.isEmpty(convertedInstanceNics) && CollectionUtils.isNotEmpty(clonedInstanceNics)) {
for (UnmanagedInstanceTO.Nic nic : clonedInstanceNics) {
List<UnmanagedInstanceTO.Nic> sourceVMwareInstanceNics = sourceVMwareInstance.getNics();
if (CollectionUtils.isEmpty(convertedInstanceNics) && CollectionUtils.isNotEmpty(sourceVMwareInstanceNics)) {
for (UnmanagedInstanceTO.Nic nic : sourceVMwareInstanceNics) {
// In case the NICs information is not parsed from the converted XML domain, use the cloned instance NICs with virtio adapter
nic.setAdapterType("virtio");
}
convertedInstance.setNics(clonedInstanceNics);
} else {
convertedInstance.setNics(sourceVMwareInstanceNics);
for (int i = 0; i < convertedInstanceNics.size(); i++) {
UnmanagedInstanceTO.Nic nic = convertedInstanceNics.get(i);
nic.setNicId(clonedInstanceNics.get(i).getNicId());
nic.setNicId(sourceVMwareInstanceNics.get(i).getNicId());
}
} else if (CollectionUtils.isNotEmpty(convertedInstanceNics) && CollectionUtils.isNotEmpty(sourceVMwareInstanceNics)
&& convertedInstanceNics.size() == sourceVMwareInstanceNics.size()) {
for (int i = 0; i < convertedInstanceNics.size(); i++) {
UnmanagedInstanceTO.Nic nic = convertedInstanceNics.get(i);
nic.setNicId(sourceVMwareInstanceNics.get(i).getNicId());
if (nic.getMacAddress() == null) {
nic.setMacAddress(sourceVMwareInstanceNics.get(i).getMacAddress());
}
}
}
}
private void removeClonedInstance(String vcenter, String datacenterName,
String username, String password,
String sourceHostName, String clonedInstanceName,
String sourceVM) {
private void removeClonedInstance(String vcenter, String datacenterName, String username, String password,
String sourceHostName, String clonedInstanceName, String sourceVM) {
HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
Map<String, String> params = createParamsForRemoveClonedInstance(vcenter, datacenterName, username, password, sourceVM);
boolean result = vmwareGuru.removeClonedHypervisorVMOutOfBand(sourceHostName, clonedInstanceName, params);
@ -1698,10 +1754,23 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
LOGGER.warn(msg);
return;
}
LOGGER.debug(String.format("Removed the cloned instance %s from VMWare datacenter %s:%s",
LOGGER.debug(String.format("Removed the cloned instance %s from VMWare datacenter %s/%s",
clonedInstanceName, vcenter, datacenterName));
}
private void removeTemplate(DataStoreTO convertLocation, String ovfTemplateOnConvertLocation) {
HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
boolean result = vmwareGuru.removeVMTemplateOutOfBand(convertLocation, ovfTemplateOnConvertLocation);
if (!result) {
String msg = String.format("Could not remove the template file %s on datastore %s",
ovfTemplateOnConvertLocation, convertLocation.getUrl());
LOGGER.warn(msg);
return;
}
LOGGER.debug(String.format("Removed the template file %s on datastore %s",
ovfTemplateOnConvertLocation, convertLocation.getUrl()));
}
private Map<String, String> createParamsForRemoveClonedInstance(String vcenter, String datacenterName, String username,
String password, String sourceVM) {
Map<String, String> params = new HashMap<>();
@ -1712,7 +1781,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return params;
}
private HostVO selectInstanceConvertionKVMHostInCluster(Cluster destinationCluster, Long convertInstanceHostId) {
private HostVO selectInstanceConversionKVMHostInCluster(Cluster destinationCluster, Long convertInstanceHostId) {
if (convertInstanceHostId != null) {
HostVO selectedHost = hostDao.findById(convertInstanceHostId);
if (selectedHost == null) {
@ -1729,41 +1798,62 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
return selectedHost;
}
List<HostVO> hosts = hostDao.listByClusterAndHypervisorType(destinationCluster.getId(), destinationCluster.getHypervisorType());
if (CollectionUtils.isEmpty(hosts)) {
String err = String.format("Could not find any running %s host in cluster %s",
// Auto select host with conversion capability
List<HostVO> hosts = hostDao.listByClusterHypervisorTypeAndHostCapability(destinationCluster.getId(), destinationCluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION);
if (CollectionUtils.isNotEmpty(hosts)) {
return hosts.get(new Random().nextInt(hosts.size()));
}
// Try without host capability check
hosts = hostDao.listByClusterAndHypervisorType(destinationCluster.getId(), destinationCluster.getHypervisorType());
if (CollectionUtils.isNotEmpty(hosts)) {
return hosts.get(new Random().nextInt(hosts.size()));
}
String err = String.format("Could not find any suitable %s host in cluster %s to perform the instance conversion",
destinationCluster.getHypervisorType(), destinationCluster.getName());
LOGGER.error(err);
throw new CloudRuntimeException(err);
}
List<HostVO> filteredHosts = hosts.stream()
.filter(x -> x.getResourceState() == ResourceState.Enabled)
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(filteredHosts)) {
String err = String.format("Could not find a %s host in cluster %s to perform the instance conversion",
destinationCluster.getHypervisorType(), destinationCluster.getName());
private CheckConvertInstanceAnswer checkConversionSupportOnHost(HostVO convertHost, String sourceVM, boolean checkWindowsGuestConversionSupport) {
LOGGER.debug(String.format("Checking the %s conversion support on the host %s (%s)", checkWindowsGuestConversionSupport? "windows guest" : "", convertHost.getId(), convertHost.getName()));
CheckConvertInstanceCommand cmd = new CheckConvertInstanceCommand(checkWindowsGuestConversionSupport);
int timeoutSeconds = 60;
cmd.setWait(timeoutSeconds);
CheckConvertInstanceAnswer checkConvertInstanceAnswer;
try {
checkConvertInstanceAnswer = (CheckConvertInstanceAnswer) agentManager.send(convertHost.getId(), cmd);
} catch (AgentUnavailableException | OperationTimedoutException e) {
String err = String.format("Failed to check %s conversion support on the host %s for converting instance %s from VMware to KVM due to: %s",
checkWindowsGuestConversionSupport? "windows guest" : "", convertHost.getName(), sourceVM, e.getMessage());
LOGGER.error(err);
throw new CloudRuntimeException(err);
}
return filteredHosts.get(new Random().nextInt(filteredHosts.size()));
if (!checkConvertInstanceAnswer.getResult()) {
String err = String.format("The host %s doesn't support conversion of instance %s from VMware to KVM due to: %s",
convertHost.getName(), sourceVM, checkConvertInstanceAnswer.getDetails());
LOGGER.error(err);
throw new CloudRuntimeException(err);
}
private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String datacenterName, String clusterName,
String username, String password, String hostName,
UnmanagedInstanceTO clonedInstance, Cluster destinationCluster,
Long convertInstanceHostId, Long convertStoragePoolId) {
HostVO convertHost = selectInstanceConvertionKVMHostInCluster(destinationCluster, convertInstanceHostId);
String vmName = clonedInstance.getName();
LOGGER.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" +
" from VMware to KVM ", convertHost.getId(), convertHost.getName(), vmName));
return checkConvertInstanceAnswer;
}
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(hostName, vmName,
vcenter, datacenterName, clusterName, username, password);
DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, convertHost);
List<String> destinationStoragePools = selectInstanceConvertionStoragePools(destinationCluster, clonedInstance.getDisks());
private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation(String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost,
List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation,
String ovfTemplateDirConvertLocation) {
LOGGER.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) using OVF %s on conversion datastore",
sourceVM, convertHost.getId(), convertHost.getName(), ovfTemplateDirConvertLocation));
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVM);
List<String> destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks());
ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO,
Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation);
int timeoutSeconds = StorageManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, ovfTemplateDirConvertLocation, false, false);
int timeoutSeconds = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
cmd.setWait(timeoutSeconds);
Answer convertAnswer;
@ -1777,17 +1867,68 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
if (!convertAnswer.getResult()) {
String err = String.format("The convert process failed for instance %s from Vmware to KVM on host %s: %s",
vmName, convertHost.getName(), convertAnswer.getDetails());
String err = String.format("The convert process failed for instance %s from VMware to KVM on host %s: %s",
sourceVM, convertHost.getName(), convertAnswer.getDetails());
LOGGER.error(err);
throw new CloudRuntimeException(err);
}
return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance();
}
private List<String> selectInstanceConvertionStoragePools(Cluster destinationCluster, List<UnmanagedInstanceTO.Disk> disks) {
private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost,
List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation,
String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) {
LOGGER.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) after OVF export through ovftool",
sourceVM, convertHost.getId(), convertHost.getName()));
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVMwareInstance.getName(), vcenterHost, vcenterUsername, vcenterPassword, datacenterName);
List<String> destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks());
ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO,
Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, null, false, true);
int timeoutSeconds = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
cmd.setWait(timeoutSeconds);
int noOfThreads = UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles.value();
if (noOfThreads == 0) {
// Use no. of threads as the disks count
noOfThreads = sourceVMwareInstance.getDisks().size();
}
cmd.setThreadsCountToExportOvf(noOfThreads);
Answer convertAnswer;
try {
convertAnswer = agentManager.send(convertHost.getId(), cmd);
} catch (AgentUnavailableException | OperationTimedoutException e) {
String err = String.format("Could not send the convert instance command to host %s (%s) due to: %s",
convertHost.getId(), convertHost.getName(), e.getMessage());
LOGGER.error(err, e);
throw new CloudRuntimeException(err);
}
if (!convertAnswer.getResult()) {
String err = String.format("The convert process failed for instance %s from VMware to KVM on host %s: %s",
sourceVM, convertHost.getName(), convertAnswer.getDetails());
LOGGER.error(err);
throw new CloudRuntimeException(err);
}
return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance();
}
private List<StoragePoolVO> findInstanceConversionStoragePoolsInCluster(Cluster destinationCluster) {
List<StoragePoolVO> pools = new ArrayList<>();
List<StoragePoolVO> clusterPools = primaryDataStoreDao.findClusterWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem);
pools.addAll(clusterPools);
List<StoragePoolVO> zonePools = primaryDataStoreDao.findZoneWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem);
pools.addAll(zonePools);
if (pools.isEmpty()) {
String msg = String.format("Cannot find suitable storage pools in cluster %s for the conversion", destinationCluster.getName());
LOGGER.error(msg);
throw new CloudRuntimeException(msg);
}
return pools;
}
private List<String> selectInstanceConversionStoragePools(List<StoragePoolVO> pools, List<UnmanagedInstanceTO.Disk> disks) {
List<String> storagePools = new ArrayList<>(disks.size());
List<StoragePoolVO> pools = primaryDataStoreDao.listPoolsByCluster(destinationCluster.getId());
//TODO: Choose pools by capacity
for (UnmanagedInstanceTO.Disk disk : disks) {
Long capacity = disk.getCapacity();
@ -1801,7 +1942,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
throw new CloudRuntimeException(msg);
}
protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId, HostVO convertHost) {
protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId) {
if (convertStoragePoolId != null) {
StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId);
if (selectedStoragePool == null) {
@ -1812,11 +1953,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
logFailureAndThrowException(String.format("Cannot use the storage pool %s for the instance conversion as " +
"it is not in the scope of the cluster %s", selectedStoragePool.getName(), destinationCluster.getName()));
}
if (selectedStoragePool.getScope() == ScopeType.HOST &&
storagePoolHostDao.findByPoolHost(selectedStoragePool.getId(), convertHost.getId()) == null) {
logFailureAndThrowException(String.format("The storage pool %s is not a local storage pool for the host %s", selectedStoragePool.getName(), convertHost.getName()));
if (selectedStoragePool.getScope() == ScopeType.HOST) {
logFailureAndThrowException(String.format("The storage pool %s is a local storage pool and not supported for temporary conversion location, cluster and zone wide NFS storage pools are supported", selectedStoragePool.getName()));
} else if (selectedStoragePool.getPoolType() != Storage.StoragePoolType.NetworkFilesystem) {
logFailureAndThrowException(String.format("The storage pool %s is not supported for temporary conversion location, supported pools are NFS storage pools", selectedStoragePool.getName()));
logFailureAndThrowException(String.format("The storage pool %s is not supported for temporary conversion location, only NFS storage pools are supported", selectedStoragePool.getName()));
}
return dataStoreManager.getPrimaryDataStore(convertStoragePoolId).getTO();
} else {
@ -2505,7 +2645,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[]{
UnmanageVMPreserveNic,
RemoteKvmInstanceDisksCopyTimeout
RemoteKvmInstanceDisksCopyTimeout,
ConvertVmwareInstanceToKvmTimeout,
ThreadsOnMSToImportVMwareVMFiles,
ThreadsOnKVMHostToImportVMwareVMFiles
};
}
}

View File

@ -16,6 +16,9 @@
// under the License.
package com.cloud.configuration;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.storage.StorageManager;
import com.cloud.utils.net.NetUtils;
@ -31,8 +34,11 @@ import com.cloud.user.User;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.db.SearchCriteria;
import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.vm.UnmanagedVMsManager;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -47,7 +53,6 @@ import org.mockito.Spy;
import java.util.ArrayList;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class ConfigurationManagerImplTest {
@Mock
@ -65,6 +70,8 @@ public class ConfigurationManagerImplTest {
@Mock
Domain domainMock;
@Mock
ConfigurationDao configDaoMock;
@Mock
DataCenterDao zoneDaoMock;
@Mock
DomainDao domainDaoMock;
@ -300,6 +307,66 @@ public class ConfigurationManagerImplTest {
configurationManagerImplSpy.validateIpAddressRelatedConfigValues("config.iprange", "192.168.1.1-192.168.1.100");
}
@Test
public void testValidateInvalidConfiguration() {
Mockito.doReturn(null).when(configDaoMock).findByName(Mockito.anyString());
String msg = configurationManagerImplSpy.validateConfigurationValue("test.config.name", "testvalue", ConfigKey.Scope.Global.toString());
Assert.assertEquals("Invalid configuration variable.", msg);
}
@Test
public void testValidateInvalidScopeForConfiguration() {
ConfigurationVO cfg = mock(ConfigurationVO.class);
when(cfg.getScope()).thenReturn(ConfigKey.Scope.Account.toString());
Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
String msg = configurationManagerImplSpy.validateConfigurationValue("test.config.name", "testvalue", ConfigKey.Scope.Domain.toString());
Assert.assertEquals("Invalid scope id provided for the parameter test.config.name", msg);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateConfig_ThreadsOnKVMHostToTransferVMwareVMFiles_Failure() {
ConfigurationVO cfg = mock(ConfigurationVO.class);
when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString());
ConfigKey<Integer> configKey = UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles;
Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key());
configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "11", configKey.scope().toString());
}
@Test
public void testValidateConfig_ThreadsOnKVMHostToTransferVMwareVMFiles_Success() {
ConfigurationVO cfg = mock(ConfigurationVO.class);
when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString());
ConfigKey<Integer> configKey = UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles;
Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key());
String msg = configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "10", configKey.scope().toString());
Assert.assertNull(msg);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateConfig_ConvertVmwareInstanceToKvmTimeout_Failure() {
ConfigurationVO cfg = mock(ConfigurationVO.class);
when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString());
ConfigKey<Integer> configKey = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout;
Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key());
configurationManagerImplSpy.populateConfigValuesForValidationSet();
configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "0", configKey.scope().toString());
}
@Test
public void testValidateConfig_ConvertVmwareInstanceToKvmTimeout_Success() {
ConfigurationVO cfg = mock(ConfigurationVO.class);
when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString());
ConfigKey<Integer> configKey = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout;
Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key());
configurationManagerImplSpy.populateConfigValuesForValidationSet();
String msg = configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "9", configKey.scope().toString());
Assert.assertNull(msg);
}
@Test
public void validateDomainTestInvalidIdThrowException() {
Mockito.doReturn(null).when(domainDaoMock).findById(invalidId);

View File

@ -19,6 +19,8 @@ package org.apache.cloudstack.vm;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckConvertInstanceAnswer;
import com.cloud.agent.api.CheckConvertInstanceCommand;
import com.cloud.agent.api.CheckVolumeAnswer;
import com.cloud.agent.api.CheckVolumeCommand;
import com.cloud.agent.api.ConvertInstanceAnswer;
@ -147,6 +149,7 @@ import java.util.UUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
@ -586,6 +589,7 @@ public class UnmanagedVMsManagerImplTest {
String host = "192.168.1.10";
String vmName = "TestInstanceFromVmware";
instance.setName(vmName);
String tmplFileName = "5b8d689a-e61a-4ac3-9b76-e121ff90fbd3";
long newVmId = 2L;
long networkId = 1L;
when(vmDao.getNextInSequence(Long.class, "id")).thenReturn(newVmId);
@ -614,8 +618,10 @@ public class UnmanagedVMsManagerImplTest {
HypervisorGuru vmwareGuru = mock(HypervisorGuru.class);
when(hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware)).thenReturn(vmwareGuru);
when(vmwareGuru.cloneHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(instance);
when(vmwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(anyString(), anyString(), anyMap())).thenReturn(new Pair<>(instance, true));
when(vmwareGuru.removeClonedHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(true);
when(vmwareGuru.createVMTemplateOutOfBand(anyString(), anyString(), anyMap(), any(DataStoreTO.class), anyInt())).thenReturn(tmplFileName);
when(vmwareGuru.removeVMTemplateOutOfBand(any(DataStoreTO.class), anyString())).thenReturn(true);
HostVO convertHost = mock(HostVO.class);
long convertHostId = 1L;
@ -639,6 +645,7 @@ public class UnmanagedVMsManagerImplTest {
when(destPool.getDataCenterId()).thenReturn(zoneId);
when(destPool.getClusterId()).thenReturn(null);
when(destPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
StoragePoolVO zoneDestPool = mock(StoragePoolVO.class);
if (selectTemporaryStorage) {
long temporaryStoragePoolId = 1L;
when(importVmCmd.getConvertStoragePoolId()).thenReturn(temporaryStoragePoolId);
@ -650,8 +657,9 @@ public class UnmanagedVMsManagerImplTest {
when(imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs")).thenReturn(imageStoreVO);
when(dataStoreManager.getDataStore(1L, DataStoreRole.Image)).thenReturn(dataStore);
}
when(primaryDataStoreDao.listPoolsByCluster(clusterId)).thenReturn(List.of(destPool));
when(primaryDataStoreDao.listPoolByHostPath(Mockito.anyString(), Mockito.anyString())).thenReturn(List.of(destPool));
when(primaryDataStoreDao.findClusterWideStoragePoolsByHypervisorAndPoolType(clusterId, Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem)).thenReturn(List.of(destPool));
when(primaryDataStoreDao.findZoneWideStoragePoolsByHypervisorAndPoolType(zoneId, Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem)).thenReturn(List.of(zoneDestPool));
if (VcenterParameter.EXISTING == vcenterParameter) {
VmwareDatacenterVO datacenterVO = mock(VmwareDatacenterVO.class);
@ -679,17 +687,25 @@ public class UnmanagedVMsManagerImplTest {
when(vmwareDatacenterDao.findById(existingDatacenterId)).thenReturn(null);
}
ConvertInstanceAnswer answer = mock(ConvertInstanceAnswer.class);
when(answer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);
when(answer.getConvertedInstance()).thenReturn(instance);
CheckConvertInstanceAnswer checkConvertInstanceAnswer = mock(CheckConvertInstanceAnswer.class);
when(checkConvertInstanceAnswer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);
if (VcenterParameter.AGENT_UNAVAILABLE != vcenterParameter) {
when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(ConvertInstanceCommand.class))).thenReturn(answer);
when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(CheckConvertInstanceCommand.class))).thenReturn(checkConvertInstanceAnswer);
}
ConvertInstanceAnswer convertInstanceAnswer = mock(ConvertInstanceAnswer.class);
when(convertInstanceAnswer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);
when(convertInstanceAnswer.getConvertedInstance()).thenReturn(instance);
if (VcenterParameter.AGENT_UNAVAILABLE != vcenterParameter) {
when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(ConvertInstanceCommand.class))).thenReturn(convertInstanceAnswer);
}
try (MockedStatic<UsageEventUtils> ignored = Mockito.mockStatic(UsageEventUtils.class)) {
unmanagedVMsManager.importVm(importVmCmd);
verify(vmwareGuru).cloneHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap());
verify(vmwareGuru).getHypervisorVMOutOfBandAndCloneIfRequired(Mockito.eq(host), Mockito.eq(vmName), anyMap());
verify(vmwareGuru).createVMTemplateOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class), anyInt());
verify(vmwareGuru).removeClonedHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap());
verify(vmwareGuru).removeVMTemplateOutOfBand(any(DataStoreTO.class), anyString());
}
}
@ -797,7 +813,7 @@ public class UnmanagedVMsManagerImplTest {
long poolId = 1L;
when(primaryDataStoreDao.findById(poolId)).thenReturn(null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
}
@Test(expected = CloudRuntimeException.class)
@ -808,7 +824,7 @@ public class UnmanagedVMsManagerImplTest {
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
Mockito.when(pool.getClusterId()).thenReturn(100L);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
}
@Test(expected = CloudRuntimeException.class)
@ -818,9 +834,7 @@ public class UnmanagedVMsManagerImplTest {
StoragePoolVO pool = mock(StoragePoolVO.class);
Mockito.when(pool.getScope()).thenReturn(ScopeType.HOST);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
HostVO convertHost = Mockito.mock(HostVO.class);
Mockito.when(convertHost.getId()).thenReturn(1L);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, convertHost);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
}
@Test(expected = CloudRuntimeException.class)
@ -831,15 +845,14 @@ public class UnmanagedVMsManagerImplTest {
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
Mockito.when(pool.getClusterId()).thenReturn(1L);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
HostVO convertHost = Mockito.mock(HostVO.class);
Mockito.when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, convertHost);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
}
@Test(expected = CloudRuntimeException.class)
public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() {
ClusterVO cluster = getClusterForTests();
Mockito.when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null);
}
}

View File

@ -918,6 +918,7 @@
"label.for": "for",
"label.forbidden": "Forbidden",
"label.forced": "Force",
"label.force.ms.to.import.vm.files": "Force MS to import VM file(s) to temporary storage",
"label.force.stop": "Force stop",
"label.force.reboot": "Force reboot",
"label.forceencap": "Force UDP encapsulation of ESP packets",
@ -1049,6 +1050,7 @@
"label.installwizard.subtitle": "This guide will aid you in setting up your CloudStack™ installation",
"label.installwizard.title": "Hello and welcome to CloudStack™",
"label.instance": "Instance",
"label.instance.conversion.support": "Instance Conversion Supported",
"label.instance.groups": "Instance groups",
"label.instance.name": "Instance name",
"label.instancename": "Internal name",
@ -2039,6 +2041,7 @@
"label.suitable": "Suitable",
"label.summary": "Summary",
"label.sunday": "Sunday",
"label.supported": "Supported",
"label.supportedservices": "Supported services",
"label.supportsautoscaling": "Supports auto scaling",
"label.supportsha": "Supports HA",
@ -3116,11 +3119,13 @@
"message.select.destination.image.stores": "Please select Image Store(s) to which data is to be migrated to",
"message.select.disk.offering": "Please select a disk offering for disk",
"message.select.end.date.and.time": "Select an end date & time.",
"message.select.kvm.host.instance.conversion": "(Optional) Select a KVM host in the cluster to perform the instance conversion through virt-v2v",
"message.select.load.balancer.rule": "Please select a load balancer rule for your AutoScale Instance group.",
"message.select.migration.policy": "Please select a migration policy.",
"message.select.nic.network": "Please select a Network for NIC",
"message.select.security.groups": "Please select security group(s) for your new Instance.",
"message.select.start.date.and.time": "Select a start date & time.",
"message.select.temporary.storage.instance.conversion": "(Optional) Select a Storage temporary destination for the converted disks through virt-v2v",
"message.select.zone.description": "Select type of zone basic/advanced.",
"message.select.zone.hint": "This is the type of zone deployment that you want to use. Basic zone: provides a single Network where each Instance is assigned an IP directly from the Network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering). Advanced zone: For more sophisticated Network topologies. This Network model provides the most flexibility in defining guest Networks and providing custom Network offerings such as firewall, VPN, or load balancer support.",
"message.server.description": "NFS, iSCSI, or PreSetup: IP address or DNS name of the storage device. VMWare PreSetup: IP address or DNS name of the vCenter server. Linstor: http(s) url of the linstor-controller.",

View File

@ -48,6 +48,14 @@
</div>
</div>
</a-list-item>
<a-list-item v-if="host.instanceconversionsupported">
<div>
<strong>{{ $t('label.instance.conversion.support') }}</strong>
<div>
{{ host.instanceconversionsupported }}
</div>
</div>
</a-list-item>
<a-list-item v-if="host.hosttags">
<div>
<strong>{{ $t('label.hosttags') }}</strong>

View File

@ -158,7 +158,7 @@
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
:resourceKey="cluster.id"
:selectOptions="kvmHostsForConversion"
:checkBoxLabel="'(Optional) Select a KVM host in the cluster to perform the instance conversion through virt-v2v'"
:checkBoxLabel="$t('message.select.kvm.host.instance.conversion')"
:defaultCheckBoxValue="false"
:reversed="false"
@handle-checkselectpair-change="updateSelectedKvmHostForConversion"
@ -167,11 +167,11 @@
<a-form-item name="convertstorageoption" ref="convertstorageoption">
<check-box-select-pair
layout="vertical"
style="margin-bottom: 20px"
style="margin-bottom: 5px"
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
:resourceKey="cluster.id"
:selectOptions="storageOptionsForConversion"
:checkBoxLabel="'(Optional) Select a Storage temporary destination for the converted disks through virt-v2v'"
:checkBoxLabel="$t('message.select.temporary.storage.instance.conversion')"
:defaultCheckBoxValue="false"
:reversed="false"
@handle-checkselectpair-change="updateSelectedStorageOptionForConversion"
@ -192,6 +192,12 @@
</a-select-option>
</a-select>
</a-form-item>
<a-form-item name="forcemstoimportvmfiles" ref="forcemstoimportvmfiles" v-if="selectedVmwareVcenter">
<template #label>
<tooltip-label :title="$t('label.force.ms.to.import.vm.files')" :tooltip="apiParams.forcemstoimportvmfiles.description"/>
</template>
<a-switch v-model:checked="form.forcemstoimportvmfiles" @change="val => { switches.forceMsToImportVmFiles = val }" />
</a-form-item>
<a-form-item name="serviceofferingid" ref="serviceofferingid">
<template #label>
<tooltip-label :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/>
@ -518,6 +524,12 @@ export default {
this.apiConfig.params.forEach(param => {
this.apiParams[param.name] = param
})
this.apiConfig = this.$store.getters.apis.importVm || {}
this.apiConfig.params.forEach(param => {
if (!(param.name in this.apiParams)) {
this.apiParams[param.name] = param
}
})
},
created () {
this.initForm()
@ -696,6 +708,7 @@ export default {
rootdiskid: 0,
migrateallowed: this.switches.migrateAllowed,
forced: this.switches.forced,
forcemstoimportvmfiles: this.switches.forceMsToImportVmFiles,
domainid: null,
account: null
})
@ -909,13 +922,18 @@ export default {
resourcestate: 'Enabled'
}).then(json => {
this.kvmHostsForConversion = json.listhostsresponse.host || []
this.kvmHostsForConversion.map(host => {
if (host.instanceconversionsupported !== null && host.instanceconversionsupported !== undefined && host.instanceconversionsupported) {
host.name = host.name + ' (' + this.$t('label.supported') + ')'
}
})
})
},
fetchStoragePoolsForConversion () {
if (this.selectedStorageOptionForConversion === 'primary') {
api('listStoragePools', {
zoneid: this.cluster.zoneid,
state: 'Up'
status: 'Up'
}).then(json => {
this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || []
})
@ -924,7 +942,7 @@ export default {
api('listStoragePools', {
scope: 'HOST',
ipaddress: kvmHost.ipaddress,
state: 'Up'
status: 'Up'
}).then(json => {
this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || []
})
@ -1084,8 +1102,9 @@ export default {
if (this.selectedStoragePoolForConversion) {
params.convertinstancepoolid = this.selectedStoragePoolForConversion
}
params.forcemstoimportvmfiles = values.forcemstoimportvmfiles
}
var keys = ['hostname', 'domainid', 'projectid', 'account', 'migrateallowed', 'forced']
var keys = ['hostname', 'domainid', 'projectid', 'account', 'migrateallowed', 'forced', 'forcemstoimportvmfiles']
if (this.templateType !== 'auto') {
keys.push('templateid')
}

View File

@ -144,7 +144,12 @@ public class BaseMO {
public int getCustomFieldKey(String morType, String fieldName) throws Exception {
assert (morType != null);
CustomFieldsManagerMO cfmMo = new CustomFieldsManagerMO(_context, _context.getServiceContent().getCustomFieldsManager());
ManagedObjectReference cfmMor = _context.getServiceContent().getCustomFieldsManager();
if (cfmMor == null) {
return 0;
}
CustomFieldsManagerMO cfmMo = new CustomFieldsManagerMO(_context, cfmMor);
return cfmMo.getCustomFieldKey(morType, fieldName);
}

View File

@ -32,9 +32,12 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException;
@ -1798,6 +1801,10 @@ public class VirtualMachineMO extends BaseMO {
}
public void exportVm(String exportDir, String exportName, boolean packToOva, boolean leaveOvaFileOnly) throws Exception {
exportVm(exportDir, exportName, packToOva, leaveOvaFileOnly, -1);
}
public void exportVm(String exportDir, String exportName, boolean packToOva, boolean leaveOvaFileOnly, int threadsCountToExportOvf) throws Exception {
ManagedObjectReference morOvf = _context.getServiceContent().getOvfManager();
VirtualMachineRuntimeInfo runtimeInfo = getRuntimeInfo();
@ -1827,18 +1834,31 @@ public class VirtualMachineMO extends BaseMO {
final HttpNfcLeaseMO.ProgressReporter progressReporter = leaseMo.createProgressReporter();
boolean success = false;
List<String> fileNames = new ArrayList<String>();
List<String> fileNames = new ArrayList<>();
try {
HttpNfcLeaseInfo leaseInfo = leaseMo.getLeaseInfo();
final long totalBytes = leaseInfo.getTotalDiskCapacityInKB() * 1024;
long totalBytesDownloaded = 0;
AtomicLong totalBytesDownloaded = new AtomicLong(0L);
List<HttpNfcLeaseDeviceUrl> deviceUrls = leaseInfo.getDeviceUrl();
s_logger.info("volss: copy vmdk and ovf file starts " + System.currentTimeMillis());
if (deviceUrls != null) {
OvfFile[] ovfFiles = new OvfFile[deviceUrls.size()];
for (int i = 0; i < deviceUrls.size(); i++) {
int deviceUrlsCount = deviceUrls.size();
boolean parallelDownload = false;
if (threadsCountToExportOvf >= 0 && deviceUrlsCount > 1) {
if (threadsCountToExportOvf == 0) {
threadsCountToExportOvf = deviceUrlsCount;
parallelDownload = true;
} else if (threadsCountToExportOvf > 1) {
parallelDownload = true;
}
}
OvfFile[] ovfFiles = new OvfFile[deviceUrlsCount];
List<CompletableFuture<Long>> futures = new ArrayList<>();
ExecutorService executor = null;
for (int i = 0; i < deviceUrlsCount; i++) {
String deviceId = deviceUrls.get(i).getKey();
Long diskFileSize = deviceUrls.get(i).getFileSize();
String deviceUrlStr = deviceUrls.get(i).getUrl();
String orgDiskFileName = deviceUrlStr.substring(deviceUrlStr.lastIndexOf("/") + 1);
String diskFileName = String.format("%s-disk%d%s", exportName, i, VmwareHelper.getFileExtension(orgDiskFileName, ".vmdk"));
@ -1847,8 +1867,9 @@ public class VirtualMachineMO extends BaseMO {
String diskLocalPath = exportDir + File.separator + diskFileName;
fileNames.add(diskLocalPath);
if (!parallelDownload) {
if (s_logger.isInfoEnabled()) {
s_logger.info("Download VMDK file for export. url: " + deviceUrlStr);
s_logger.info("Download VMDK file for export url: " + deviceUrlStr + ", size: " + diskFileSize);
}
long lengthOfDiskFile = _context.downloadVmdkFile(diskUrlStr, diskLocalPath, totalBytesDownloaded, new ActionDelegate<Long>() {
@Override
@ -1859,13 +1880,64 @@ public class VirtualMachineMO extends BaseMO {
progressReporter.reportProgress((int)(param * 100 / totalBytes));
}
});
totalBytesDownloaded += lengthOfDiskFile;
totalBytesDownloaded.addAndGet(lengthOfDiskFile);
OvfFile ovfFile = new OvfFile();
ovfFile.setPath(diskFileName);
ovfFile.setDeviceId(deviceId);
ovfFile.setSize(lengthOfDiskFile);
ovfFiles[i] = ovfFile;
} else {
String diskUrl = diskUrlStr;
executor = Executors.newFixedThreadPool(Math.min(threadsCountToExportOvf, deviceUrlsCount));
if (s_logger.isInfoEnabled()) {
s_logger.info("Download VMDK file for export url: " + deviceUrlStr + ", size: " + diskFileSize);
}
CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
long lengthOfDiskFile = 0;
try {
lengthOfDiskFile = _context.downloadVmdkFile(diskUrl, diskLocalPath, totalBytesDownloaded, new ActionDelegate<Long>() {
@Override
public void action(Long param) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Download progress " + param + "/" + toHumanReadableSize(totalBytes));
}
progressReporter.reportProgress((int)(param * 100 / totalBytes));
}
});
} catch (Exception e) {
s_logger.error("Error on downloading VMDK file for export url: " + diskUrl, e);
}
return lengthOfDiskFile;
}, executor);
futures.add(future);
OvfFile ovfFile = new OvfFile();
ovfFile.setPath(diskFileName);
ovfFile.setDeviceId(deviceId);
ovfFile.setSize(0L);
ovfFiles[i] = ovfFile;
}
}
if (parallelDownload) {
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
List<Long> diskFileLengths = allFutures.exceptionally(e -> {
s_logger.error("Error on downloading VMDK files: " + e.getMessage());
return null;
}).thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList())).join();
executor.shutdown();
if (CollectionUtils.isNotEmpty(diskFileLengths)) {
int i = 0;
for (Long diskFileLength : diskFileLengths) {
if (diskFileLength != null) {
totalBytesDownloaded.addAndGet(diskFileLength);
ovfFiles[i].setSize(diskFileLength);
}
i++;
}
}
}
// write OVF descriptor file
@ -1892,8 +1964,10 @@ public class VirtualMachineMO extends BaseMO {
command.add("-cf", exportName + ".ova");
command.add(exportName + ".ovf"); // OVF file should be the first file in OVA archive
for (String name : fileNames) {
if (!name.endsWith(".ovf")) {
command.add((new File(name).getName()));
}
}
s_logger.info("Package OVA with command: " + command.toString());
command.execute();

View File

@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
@ -469,7 +470,7 @@ public class VmwareContext {
}
}
public long downloadVmdkFile(String urlString, String localFileName, long totalBytesDownloaded, ActionDelegate<Long> progressUpdater) throws Exception {
public long downloadVmdkFile(String urlString, String localFileName, AtomicLong totalBytesDownloaded, ActionDelegate<Long> progressUpdater) throws Exception {
HttpURLConnection conn = getRawHTTPConnection(urlString);
String cookie = _vimClient.getServiceCookie();
@ -495,10 +496,10 @@ public class VmwareContext {
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
bytesWritten += len;
totalBytesDownloaded += len;
totalBytesDownloaded.addAndGet(len);
if (progressUpdater != null)
progressUpdater.action(new Long(totalBytesDownloaded));
progressUpdater.action(new Long(totalBytesDownloaded.get()));
}
} finally {
if (in != null)

View File

@ -807,8 +807,13 @@ public class VmwareHelper {
instance.setMemory(configSummary.getMemorySizeMB());
}
try {
ClusterMO clusterMo = new ClusterMO(hyperHost.getContext(), hyperHost.getHyperHostCluster());
instance.setClusterName(clusterMo.getName());
} catch (Exception e) {
s_logger.warn("Unable to get unmanaged instance cluster info, due to: " + e.getMessage());
}
instance.setHostName(hyperHost.getHyperHostName());
if (StringUtils.isEmpty(instance.getOperatingSystemId()) && configSummary != null) {
@ -838,7 +843,7 @@ public class VmwareHelper {
instance.setDisks(getUnmanageInstanceDisks(vmMo));
instance.setNics(getUnmanageInstanceNics(hyperHost, vmMo));
} catch (Exception e) {
s_logger.info("Unable to retrieve unmanaged instance info. " + e.getMessage());
s_logger.error("Unable to retrieve unmanaged instance info, due to: " + e.getMessage());
}
return instance;
}