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); 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> * Data type: Boolean.<br>
* Default value: <code>false</code> * Default value: <code>false</code>
*/ */

View File

@ -18,40 +18,39 @@
*/ */
package com.cloud.agent.api.to; package com.cloud.agent.api.to;
import java.io.Serializable;
import com.cloud.agent.api.LogLevel; import com.cloud.agent.api.LogLevel;
import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor;
import java.io.Serializable;
public class RemoteInstanceTO implements Serializable { public class RemoteInstanceTO implements Serializable {
private Hypervisor.HypervisorType hypervisorType; private Hypervisor.HypervisorType hypervisorType;
private String hostName;
private String instanceName; 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 // TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes
private String vcenterUsername; private String vcenterUsername;
@LogLevel(LogLevel.Log4jLevel.Off) @LogLevel(LogLevel.Log4jLevel.Off)
private String vcenterPassword; private String vcenterPassword;
private String vcenterHost; private String vcenterHost;
private String datacenterName; private String datacenterName;
private String clusterName;
public RemoteInstanceTO() { public RemoteInstanceTO() {
} }
public RemoteInstanceTO(String hostName, String instanceName, String vcenterHost, public RemoteInstanceTO(String instanceName) {
String datacenterName, String clusterName, this.hypervisorType = Hypervisor.HypervisorType.VMware;
String vcenterUsername, String vcenterPassword) { this.instanceName = instanceName;
}
public RemoteInstanceTO(String instanceName, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) {
this.hypervisorType = Hypervisor.HypervisorType.VMware; this.hypervisorType = Hypervisor.HypervisorType.VMware;
this.hostName = hostName;
this.instanceName = instanceName; this.instanceName = instanceName;
this.vcenterHost = vcenterHost; this.vcenterHost = vcenterHost;
this.datacenterName = datacenterName;
this.clusterName = clusterName;
this.vcenterUsername = vcenterUsername; this.vcenterUsername = vcenterUsername;
this.vcenterPassword = vcenterPassword; this.vcenterPassword = vcenterPassword;
this.datacenterName = datacenterName;
} }
public Hypervisor.HypervisorType getHypervisorType() { public Hypervisor.HypervisorType getHypervisorType() {
@ -62,10 +61,6 @@ public class RemoteInstanceTO implements Serializable {
return this.instanceName; return this.instanceName;
} }
public String getHostName() {
return this.hostName;
}
public String getVcenterUsername() { public String getVcenterUsername() {
return vcenterUsername; return vcenterUsername;
} }
@ -81,8 +76,4 @@ public class RemoteInstanceTO implements Serializable {
public String getDatacenterName() { public String getDatacenterName() {
return datacenterName; 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_UEFI_ENABLE = "host.uefi.enable";
public static final String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; 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. * @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 org.apache.cloudstack.framework.config.ConfigKey;
import com.cloud.agent.api.Command; 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.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.hypervisor.Hypervisor.HypervisorType; 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. * 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 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 * @return a list of commands to perform for a successful migration
*/ */
List<Command> finalizeMigrate(VirtualMachine vm, Map<Volume, StoragePool> volumeToPool); 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 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 * @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, Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map<String, String> params);
Map<String, String> params);
/** /**
* Removes a VM created as a clone of a VM on an external host * 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 * @param params hypervisor specific additional parameters
* @return true if the operation succeeds, false if not * @return true if the operation succeeds, false if not
*/ */
boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params);
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 = "forced";
public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage"; public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
public static final String FORCE_DELETE_HOST = "forcedeletehost"; 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 FORMAT = "format";
public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork"; public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
public static final String FOR_SYSTEM_VMS = "forsystemvms"; 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 NEXT_ACL_RULE_ID = "nextaclruleid";
public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash"; public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash";
public static final String IMAGE_PATH = "imagepath"; 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_DNS1 = "internaldns1";
public static final String INTERNAL_DNS2 = "internaldns2"; public static final String INTERNAL_DNS2 = "internaldns2";
public static final String INTERNET_PROTOCOL = "internetprotocol"; 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.VmwareDatacenterResponse;
import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.vm.VmImportService; import org.apache.cloudstack.vm.VmImportService;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -118,40 +119,44 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
description = "Temp Path on external host for disk image copy" ) description = "Temp Path on external host for disk image copy" )
private String tmpPath; private String tmpPath;
// Import from Vmware to KVM migration parameters // Import from VMware to KVM migration parameters
@Parameter(name = ApiConstants.EXISTING_VCENTER_ID, @Parameter(name = ApiConstants.EXISTING_VCENTER_ID,
type = CommandType.UUID, type = CommandType.UUID,
entityType = VmwareDatacenterResponse.class, 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; private Long existingVcenterId;
@Parameter(name = ApiConstants.HOST_IP, @Parameter(name = ApiConstants.HOST_IP,
type = BaseCmd.CommandType.STRING, 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; private String hostip;
@Parameter(name = ApiConstants.VCENTER, @Parameter(name = ApiConstants.VCENTER,
type = CommandType.STRING, 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; private String vcenter;
@Parameter(name = ApiConstants.DATACENTER_NAME, type = CommandType.STRING, @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; private String datacenterName;
@Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, @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; private String clusterName;
@Parameter(name = ApiConstants.CONVERT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, @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; private Long convertInstanceHostId;
@Parameter(name = ApiConstants.CONVERT_INSTANCE_STORAGE_POOL_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, @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; 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 /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -200,6 +205,10 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
return convertStoragePoolId; return convertStoragePoolId;
} }
public Boolean getForceMsToImportVmFiles() {
return BooleanUtils.toBooleanDefaultIfNull(forceMsToImportVmFiles, false);
}
public String getHypervisor() { public String getHypervisor() {
return hypervisor; return hypervisor;
} }

View File

@ -29,6 +29,7 @@ import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
import com.cloud.host.Host; import com.cloud.host.Host;
import com.cloud.host.Status; import com.cloud.host.Status;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.serializer.Param; import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName; 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") @Param(description = "true if the host supports encryption", since = "4.18")
private Boolean encryptionSupported; 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 @Override
public String getObjectId() { public String getObjectId() {
return this.getId(); return this.getId();
@ -526,7 +531,7 @@ public class HostResponse extends BaseResponseWithAnnotations {
this.username = username; this.username = username;
} }
public void setDetails(Map details) { public void setDetails(Map details, Hypervisor.HypervisorType hypervisorType) {
if (details == null) { if (details == null) {
return; return;
@ -547,6 +552,15 @@ public class HostResponse extends BaseResponseWithAnnotations {
this.setEncryptionSupported(new Boolean(false)); // default 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; this.details = detailsCopy;
} }
@ -737,6 +751,10 @@ public class HostResponse extends BaseResponseWithAnnotations {
this.encryptionSupported = encryptionSupported; this.encryptionSupported = encryptionSupported;
} }
public void setInstanceConversionSupported(Boolean instanceConversionSupported) {
this.instanceConversionSupported = instanceConversionSupported;
}
public Boolean getIsTagARule() { public Boolean getIsTagARule() {
return isTagARule; return isTagARule;
} }

View File

@ -17,6 +17,8 @@
package org.apache.cloudstack.vm; package org.apache.cloudstack.vm;
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.util.List; import java.util.List;
public class UnmanagedInstanceTO { public class UnmanagedInstanceTO {
@ -317,6 +319,16 @@ public class UnmanagedInstanceTO {
public int getDatastorePort() { public int getDatastorePort() {
return datastorePort; return datastorePort;
} }
@Override
public String toString() {
return "Disk {" +
"diskId='" + diskId + '\'' +
", capacity=" + toHumanReadableSize(capacity) +
", controller='" + controller + '\'' +
", controllerUnit=" + controllerUnit +
"}";
}
} }
public static class Nic { public static class Nic {
@ -409,5 +421,14 @@ public class UnmanagedInstanceTO {
public void setPciSlot(String pciSlot) { public void setPciSlot(String pciSlot) {
this.pciSlot = 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, ConfigKey.Scope.Global,
null); 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) { static boolean isSupported(Hypervisor.HypervisorType hypervisorType) {
return hypervisorType == VMware || hypervisorType == KVM; return hypervisorType == VMware || hypervisorType == KVM;
} }

View File

@ -23,6 +23,8 @@ import org.junit.Test;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.cloud.hypervisor.Hypervisor;
public final class HostResponseTest extends TestCase { public final class HostResponseTest extends TestCase {
private static final String VALID_KEY = "validkey"; private static final String VALID_KEY = "validkey";
@ -32,7 +34,7 @@ public final class HostResponseTest extends TestCase {
public void testSetDetailsNull() { public void testSetDetailsNull() {
final HostResponse hostResponse = new HostResponse(); final HostResponse hostResponse = new HostResponse();
hostResponse.setDetails(null); hostResponse.setDetails(null, null);
assertEquals(null, hostResponse.getDetails()); assertEquals(null, hostResponse.getDetails());
@ -51,7 +53,7 @@ public final class HostResponseTest extends TestCase {
final Map expectedDetails = new HashedMap(); final Map expectedDetails = new HashedMap();
expectedDetails.put(VALID_KEY, VALID_VALUE); expectedDetails.put(VALID_KEY, VALID_VALUE);
hostResponse.setDetails(details); hostResponse.setDetails(details, Hypervisor.HypervisorType.KVM);
final Map actualDetails = hostResponse.getDetails(); final Map actualDetails = hostResponse.getDetails();
assertTrue(details != actualDetails); assertTrue(details != actualDetails);
@ -70,7 +72,7 @@ public final class HostResponseTest extends TestCase {
final Map expectedDetails = new HashedMap(); final Map expectedDetails = new HashedMap();
expectedDetails.put(VALID_KEY, VALID_VALUE); expectedDetails.put(VALID_KEY, VALID_VALUE);
hostResponse.setDetails(details); hostResponse.setDetails(details, Hypervisor.HypervisorType.KVM);
final Map actualDetails = hostResponse.getDetails(); final Map actualDetails = hostResponse.getDetails();
assertTrue(details != actualDetails); 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 Hypervisor.HypervisorType destinationHypervisorType;
private List<String> destinationStoragePools; private List<String> destinationStoragePools;
private DataStoreTO conversionTemporaryLocation; private DataStoreTO conversionTemporaryLocation;
private String templateDirOnConversionLocation;
private boolean checkConversionSupport;
private boolean exportOvfToConversionLocation;
private int threadsCountToExportOvf = 0;
public ConvertInstanceCommand() { public ConvertInstanceCommand() {
} }
public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, 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.sourceInstance = sourceInstance;
this.destinationHypervisorType = destinationHypervisorType; this.destinationHypervisorType = destinationHypervisorType;
this.destinationStoragePools = destinationStoragePools; this.destinationStoragePools = destinationStoragePools;
this.conversionTemporaryLocation = conversionTemporaryLocation; this.conversionTemporaryLocation = conversionTemporaryLocation;
this.templateDirOnConversionLocation = templateDirOnConversionLocation;
this.checkConversionSupport = checkConversionSupport;
this.exportOvfToConversionLocation = exportOvfToConversionLocation;
} }
public RemoteInstanceTO getSourceInstance() { public RemoteInstanceTO getSourceInstance() {
@ -56,6 +64,26 @@ public class ConvertInstanceCommand extends Command {
return conversionTemporaryLocation; 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 @Override
public boolean executeInSequence() { public boolean executeInSequence() {
return false; return false;

View File

@ -97,14 +97,6 @@ public interface StorageManager extends StorageService {
true, true,
ConfigKey.Scope.Global, ConfigKey.Scope.Global,
null); 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, ConfigKey<Boolean> KvmAutoConvergence = new ConfigKey<>(Boolean.class,
"kvm.auto.convergence", "kvm.auto.convergence",
"Storage", "Storage",

View File

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

View File

@ -4607,10 +4607,16 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
final NicVO vo = Transaction.execute(new TransactionCallback<NicVO>() { final NicVO vo = Transaction.execute(new TransactionCallback<NicVO>() {
@Override @Override
public NicVO doInTransaction(TransactionStatus status) { public NicVO doInTransaction(TransactionStatus status) {
NicVO existingNic = _nicDao.findByNetworkIdAndMacAddress(network.getId(), macAddress); if (StringUtils.isBlank(macAddress)) {
String macAddressToPersist = 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) { 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()); NicVO vo = new NicVO(network.getGuruName(), vm.getId(), network.getId(), vm.getType());
vo.setMacAddress(macAddressToPersist); 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), final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network),
_networkModel.getNetworkTag(vm.getHypervisorType(), 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) { 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) { private String generateNewMacAddressIfForced(Network network, String macAddress, boolean forced) {
if (!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"); " and forced flag is disabled");
} }
try { try {

View File

@ -17,6 +17,7 @@
package org.apache.cloudstack.engine.orchestration; package org.apache.cloudstack.engine.orchestration;
import static org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService.NetworkLockTimeout; 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.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@ -31,8 +32,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter;
import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
import com.cloud.network.IpAddressManager; import com.cloud.network.IpAddressManager;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -40,6 +43,7 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
import org.mockito.Matchers; import org.mockito.Matchers;
import org.mockito.MockedStatic;
import org.mockito.Mockito; import org.mockito.Mockito;
import com.cloud.api.query.dao.DomainRouterJoinDao; 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.network.vpc.VpcVO;
import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.utils.db.EntityManager; 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.exception.CloudRuntimeException;
import com.cloud.utils.net.Ip; import com.cloud.utils.net.Ip;
import com.cloud.vm.DomainRouterVO; 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)).acquireInLockTable(networkId, NetworkLockTimeout.value());
verify(testOrchestrator._networksDao, times(1)).releaseFromLockTable(networkId); 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> 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); List<HostVO> listByClusterAndHypervisorType(long clusterId, HypervisorType hypervisorType);
HostVO findByName(String name); 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("hypervisor", ClusterHypervisorSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ);
ClusterHypervisorSearch.and("type", ClusterHypervisorSearch.entity().getType(), SearchCriteria.Op.EQ); ClusterHypervisorSearch.and("type", ClusterHypervisorSearch.entity().getType(), SearchCriteria.Op.EQ);
ClusterHypervisorSearch.and("status", ClusterHypervisorSearch.entity().getStatus(), SearchCriteria.Op.EQ); ClusterHypervisorSearch.and("status", ClusterHypervisorSearch.entity().getStatus(), SearchCriteria.Op.EQ);
ClusterHypervisorSearch.and("resourceState", ClusterHypervisorSearch.entity().getResourceState(), SearchCriteria.Op.EQ);
ClusterHypervisorSearch.done(); ClusterHypervisorSearch.done();
UnmanagedDirectConnectSearch = createSearchBuilder(); UnmanagedDirectConnectSearch = createSearchBuilder();
@ -1507,12 +1508,42 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
return listBy(sc); 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) { public List<HostVO> listByClusterAndHypervisorType(long clusterId, HypervisorType hypervisorType) {
SearchCriteria<HostVO> sc = ClusterHypervisorSearch.create(); SearchCriteria<HostVO> sc = ClusterHypervisorSearch.create();
sc.setParameters("clusterId", clusterId); sc.setParameters("clusterId", clusterId);
sc.setParameters("hypervisor", hypervisorType); sc.setParameters("hypervisor", hypervisorType);
sc.setParameters("type", Type.Routing); sc.setParameters("type", Type.Routing);
sc.setParameters("status", Status.Up); sc.setParameters("status", Status.Up);
sc.setParameters("resourceState", ResourceState.Enabled);
return listBy(sc); return listBy(sc);
} }

View File

@ -21,6 +21,7 @@ import java.util.Map;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.ScopeType; import com.cloud.storage.ScopeType;
import com.cloud.storage.Storage;
import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolStatus;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter; 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> 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> findLocalStoragePoolsByHostAndTags(long hostId, String[] tags);
List<StoragePoolVO> listLocalStoragePoolByPath(long datacenterId, String path); List<StoragePoolVO> listLocalStoragePoolByPath(long datacenterId, String path);

View File

@ -28,6 +28,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import com.cloud.storage.Storage;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter; import com.cloud.utils.db.Filter;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
@ -621,6 +622,28 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
return sc.list(); 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 @Override
public void deletePoolTags(long poolId) { public void deletePoolTags(long poolId) {
_tagsDao.deleteTags(poolId); _tagsDao.deleteTags(poolId);

View File

@ -16,6 +16,7 @@
// under the License. // under the License.
package com.cloud.hypervisor.kvm.resource; 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 static com.cloud.host.Host.HOST_VOLUME_ENCRYPTION;
import java.io.BufferedReader; 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 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 modifyVlanPath;
private String versionStringPath; private String versionStringPath;
private String patchScriptPath; private String patchScriptPath;
@ -3634,6 +3645,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
cmd.setGatewayIpAddress(localGateway); cmd.setGatewayIpAddress(localGateway);
cmd.setIqn(getIqn()); cmd.setIqn(getIqn());
cmd.getHostDetails().put(HOST_VOLUME_ENCRYPTION, String.valueOf(hostSupportsVolumeEncryption())); cmd.getHostDetails().put(HOST_VOLUME_ENCRYPTION, String.valueOf(hostSupportsVolumeEncryption()));
cmd.getHostDetails().put(HOST_INSTANCE_CONVERSION, String.valueOf(hostSupportsInstanceConversion()));
HealthCheckResult healthCheckResult = getHostHealthCheckResult(); HealthCheckResult healthCheckResult = getHostHealthCheckResult();
if (healthCheckResult != HealthCheckResult.IGNORE) { if (healthCheckResult != HealthCheckResult.IGNORE) {
cmd.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS); cmd.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS);
@ -5137,6 +5149,48 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return false; 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) { private void setCpuTopology(CpuModeDef cmd, int vCpusInDef, Map<String, String> details) {
if (!enableManuallySettingCpuTopologyOnKvmVm) { 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}.", 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 = private static final List<Hypervisor.HypervisorType> supportedInstanceConvertSourceHypervisors =
List.of(Hypervisor.HypervisorType.VMware); List.of(Hypervisor.HypervisorType.VMware);
protected static final String checkIfConversionIsSupportedCommand = "which virt-v2v";
@Override @Override
public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serverResource) { public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serverResource) {
RemoteInstanceTO sourceInstance = cmd.getSourceInstance(); RemoteInstanceTO sourceInstance = cmd.getSourceInstance();
@ -77,9 +75,9 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation(); DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation();
long timeout = (long) cmd.getWait() * 1000; 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. " + 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); s_logger.info(msg);
return new ConvertInstanceAnswer(cmd, false, msg); return new ConvertInstanceAnswer(cmd, false, msg);
} }
@ -94,21 +92,48 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr(); final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr();
KVMStoragePool temporaryStoragePool = getTemporaryStoragePool(conversionTemporaryLocation, storagePoolMgr); 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(); 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(); boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled();
try { try {
boolean result = performInstanceConversion(convertInstanceUrl, sourceInstanceName, temporaryPasswordFilePath, boolean result = performInstanceConversion(sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid,
temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled); timeout, verboseModeEnabled);
if (!result) { if (!result) {
String err = String.format("The virt-v2v conversion of the instance %s failed. " + String err = String.format("The virt-v2v conversion for the OVF %s failed. " +
"Please check the agent logs for the virt-v2v output", sourceInstanceName); "Please check the agent logs for the virt-v2v output", ovfTemplateDirOnConversionLocation);
s_logger.error(err); s_logger.error(err);
return new ConvertInstanceAnswer(cmd, false, err); return new ConvertInstanceAnswer(cmd, false, err);
} }
@ -133,8 +158,11 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
s_logger.error(error, e); s_logger.error(error, e);
return new ConvertInstanceAnswer(cmd, false, error); return new ConvertInstanceAnswer(cmd, false, error);
} finally { } finally {
s_logger.debug("Cleaning up instance conversion temporary password file"); if (ovfExported && StringUtils.isNotBlank(ovfTemplateDirOnConversionLocation)) {
Script.runSimpleBashScript(String.format("rm -rf %s", temporaryPasswordFilePath)); 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) { if (conversionTemporaryLocation instanceof NfsTO) {
s_logger.debug("Cleaning up secondary storage temporary location"); s_logger.debug("Cleaning up secondary storage temporary location");
storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid()); storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid());
@ -158,6 +186,27 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
supportedInstanceConvertSourceHypervisors.contains(sourceHypervisorType); 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) { protected List<KVMPhysicalDisk> getTemporaryDisksFromParsedXml(KVMStoragePool pool, LibvirtDomainXMLParser xmlParser, String convertedBasePath) {
List<LibvirtVMDef.DiskDef> disksDefs = xmlParser.getDisks(); List<LibvirtVMDef.DiskDef> disksDefs = xmlParser.getDisks();
disksDefs = disksDefs.stream().filter(x -> x.getDiskType() == LibvirtVMDef.DiskDef.DiskType.FILE && 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)); 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) { protected void sanitizeDisksPath(List<LibvirtVMDef.DiskDef> disks) {
for (LibvirtVMDef.DiskDef disk : disks) { for (LibvirtVMDef.DiskDef disk : disks) {
String[] diskPathParts = disk.getDiskPath().split("/"); String[] diskPathParts = disk.getDiskPath().split("/");
@ -237,6 +281,11 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
s_logger.error(err); s_logger.error(err);
continue; 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); KVMPhysicalDisk sourceDisk = temporaryDisks.get(i);
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
String msg = String.format("Trying to copy converted instance disk number %s from the temporary location %s" + 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 sourceHostIp = null;
String sourcePath = null; String sourcePath = null;
String storagePoolMountPoint = Script.runSimpleBashScript(String.format("mount | grep %s", storagePool.getLocalPath())); 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)) { if (StringUtils.isNotEmpty(storagePoolMountPoint)) {
String[] res = storagePoolMountPoint.strip().split(" "); String[] res = storagePoolMountPoint.strip().split(" ");
res = res[0].split(":"); res = res[0].split(":");
sourceHostIp = res[0].strip(); if (res.length > 1) {
sourcePath = res[1].strip(); sourceHostIp = res[0].strip();
sourcePath = res[1].strip();
}
} }
return new Pair<>(sourceHostIp, sourcePath); return new Pair<>(sourceHostIp, sourcePath);
} }
protected boolean performInstanceConversion(String convertInstanceUrl, String sourceInstanceName, private boolean exportOVAFromVMOnVcenter(String vmExportUrl,
String temporaryPasswordFilePath, String targetOvfDir,
String temporaryConvertFolder, int noOfThreads,
String temporaryConvertUuid, long timeout) {
long timeout, boolean verboseModeEnabled) { 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 script = new Script("virt-v2v", timeout, s_logger);
script.add("--root", "first"); script.add("--root", "first");
script.add("-ic", convertInstanceUrl); script.add("-i", "ova");
script.add(sourceInstanceName); script.add(sourceOVFDirPath);
script.add("--password-file", temporaryPasswordFilePath);
script.add("-o", "local"); script.add("-o", "local");
script.add("-os", temporaryConvertFolder); script.add("-os", temporaryConvertFolder);
script.add("-of", "qcow2"); script.add("-of", "qcow2");
@ -335,44 +404,13 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
script.add("-v"); 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); OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix);
script.execute(outputLogger); script.execute(outputLogger);
int exitValue = script.getExitValue(); int exitValue = script.getExitValue();
return exitValue == 0; 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 { protected LibvirtDomainXMLParser parseMigratedVMXmlDomain(String installPath) throws IOException {
String xmlPath = String.format("%s.xml", installPath); String xmlPath = String.format("%s.xml", installPath);
if (!new File(xmlPath).exists()) { 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 secondaryPoolUrl = "nfs://192.168.1.1/secondary";
private static final String vmName = "VmToImport"; 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 @Before
public void setUp() { public void setUp() {
@ -88,15 +82,6 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2)); 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 @Test
public void testAreSourceAndDestinationHypervisorsSupported() { public void testAreSourceAndDestinationHypervisorsSupported() {
boolean supported = convertInstanceCommandWrapper.areSourceAndDestinationHypervisorsSupported(Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.KVM); 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(destDisk.getPath()).thenReturn("xyz");
Mockito.when(storagePoolManager.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, destinationPoolUuid)) Mockito.when(storagePoolManager.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, destinationPoolUuid))
.thenReturn(destinationPool); .thenReturn(destinationPool);
Mockito.when(destinationPool.getType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
Mockito.when(storagePoolManager.copyPhysicalDisk(Mockito.eq(sourceDisk), Mockito.anyString(), Mockito.eq(destinationPool), Mockito.anyInt())) Mockito.when(storagePoolManager.copyPhysicalDisk(Mockito.eq(sourceDisk), Mockito.anyString(), Mockito.eq(destinationPool), Mockito.anyInt()))
.thenReturn(destDisk); .thenReturn(destDisk);
@ -244,21 +230,16 @@ public class LibvirtConvertInstanceCommandWrapperTest {
RemoteInstanceTO remoteInstanceTO = Mockito.mock(RemoteInstanceTO.class); RemoteInstanceTO remoteInstanceTO = Mockito.mock(RemoteInstanceTO.class);
Mockito.when(remoteInstanceTO.getHypervisorType()).thenReturn(hypervisorType); Mockito.when(remoteInstanceTO.getHypervisorType()).thenReturn(hypervisorType);
Mockito.when(remoteInstanceTO.getInstanceName()).thenReturn(vmName); 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; 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); ConvertInstanceCommand cmd = Mockito.mock(ConvertInstanceCommand.class);
Mockito.when(cmd.getSourceInstance()).thenReturn(remoteInstanceTO); Mockito.when(cmd.getSourceInstance()).thenReturn(remoteInstanceTO);
Mockito.when(cmd.getDestinationHypervisorType()).thenReturn(hypervisorType); Mockito.when(cmd.getDestinationHypervisorType()).thenReturn(hypervisorType);
Mockito.when(cmd.getWait()).thenReturn(14400); Mockito.when(cmd.getWait()).thenReturn(14400);
Mockito.when(cmd.getConversionTemporaryLocation()).thenReturn(secondaryDataStore); Mockito.when(cmd.getConversionTemporaryLocation()).thenReturn(secondaryDataStore);
Mockito.when(cmd.getCheckConversionSupport()).thenReturn(checkConversionSupport);
return cmd; return cmd;
} }
@ -266,8 +247,8 @@ public class LibvirtConvertInstanceCommandWrapperTest {
public void testExecuteConvertUnsupportedOnTheHost() { public void testExecuteConvertUnsupportedOnTheHost() {
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) { try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.VMware); RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.VMware);
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM); ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM, true);
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(1); Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtComputingResource.INSTANCE_CONVERSION_SUPPORTED_CHECK_CMD)).thenReturn(1);
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock); Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
Assert.assertFalse(answer.getResult()); Assert.assertFalse(answer.getResult());
} }
@ -277,8 +258,8 @@ public class LibvirtConvertInstanceCommandWrapperTest {
public void testExecuteConvertUnsupportedHypervisors() { public void testExecuteConvertUnsupportedHypervisors() {
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) { try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.XenServer); RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.XenServer);
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM); ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM, true);
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(0); Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtComputingResource.INSTANCE_CONVERSION_SUPPORTED_CHECK_CMD)).thenReturn(0);
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock); Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
Assert.assertFalse(answer.getResult()); Assert.assertFalse(answer.getResult());
} }
@ -287,7 +268,7 @@ public class LibvirtConvertInstanceCommandWrapperTest {
@Test @Test
public void testExecuteConvertFailure() { public void testExecuteConvertFailure() {
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.VMware); 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"; String localMountPoint = "/mnt/xyz";
Mockito.when(temporaryPool.getLocalPath()).thenReturn(localMountPoint); Mockito.when(temporaryPool.getLocalPath()).thenReturn(localMountPoint);
@ -297,15 +278,15 @@ public class LibvirtConvertInstanceCommandWrapperTest {
Mockito.when(mock.getExitValue()).thenReturn(1); Mockito.when(mock.getExitValue()).thenReturn(1);
}) })
) { ) {
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(0); Mockito.when(libvirtComputingResourceMock.hostSupportsInstanceConversion()).thenReturn(true);
Mockito.when(Script.runSimpleBashScriptForExitValueAvoidLogging(Mockito.anyString())).thenReturn(0); 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(""); Mockito.when(Script.runSimpleBashScript(Mockito.anyString())).thenReturn("");
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock); Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
Assert.assertFalse(answer.getResult()); Assert.assertFalse(answer.getResult());
Mockito.verify(convertInstanceCommandWrapper).performInstanceConversion(Mockito.anyString(), 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 static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.io.File;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -28,10 +29,12 @@ import java.util.UUID;
import javax.inject.Inject; import javax.inject.Inject;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.util.VmwareClient; import com.cloud.hypervisor.vmware.util.VmwareClient;
import com.cloud.hypervisor.vmware.util.VmwareHelper; import com.cloud.hypervisor.vmware.util.VmwareHelper;
import com.cloud.utils.script.Script;
import com.cloud.vm.VmDetailConstants; import com.cloud.vm.VmDetailConstants;
import com.vmware.vim25.VirtualMachinePowerState; import com.vmware.vim25.VirtualMachinePowerState;
import org.apache.cloudstack.acl.ControlledEntity; 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.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable; 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.CopyCommand;
import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DownloadCommand; import org.apache.cloudstack.storage.command.DownloadCommand;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; 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.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo; import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
@ -195,6 +200,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
@Inject DiskOfferingDao diskOfferingDao; @Inject DiskOfferingDao diskOfferingDao;
@Inject PhysicalNetworkDao physicalNetworkDao; @Inject PhysicalNetworkDao physicalNetworkDao;
@Inject StoragePoolHostDao storagePoolHostDao; @Inject StoragePoolHostDao storagePoolHostDao;
@Inject NfsMountManager mountManager;
protected VMwareGuru() { protected VMwareGuru() {
super(); super();
@ -1342,7 +1348,13 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
DatacenterMO dataCenterMO) throws Exception { DatacenterMO dataCenterMO) throws Exception {
HostMO sourceHost = vmMo.getRunningHost(); HostMO sourceHost = vmMo.getRunningHost();
String cloneName = UUID.randomUUID().toString(); 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(); ManagedObjectReference morPool = vmMo.getRunningHost().getHyperHostOwnerResourcePool();
boolean result = vmMo.createFullClone(cloneName, dataCenterMO.getVmFolder(), morPool, datastoreMO.getMor(), Storage.ProvisioningType.THIN); boolean result = vmMo.createFullClone(cloneName, dataCenterMO.getVmFolder(), morPool, datastoreMO.getMor(), Storage.ProvisioningType.THIN);
VirtualMachineMO clonedVM = dataCenterMO.findVm(cloneName); VirtualMachineMO clonedVM = dataCenterMO.findVm(cloneName);
@ -1351,14 +1363,23 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
s_logger.error(err); s_logger.error(err);
throw new CloudRuntimeException(err); throw new CloudRuntimeException(err);
} }
relocateClonedVMToSourceHost(clonedVM, sourceHost); relocateClonedVMToSourceHost(clonedVM, sourceHost);
return clonedVM; 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 @Override
public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, public Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map<String, String> params) {
Map<String, String> params) {
s_logger.debug(String.format("Cloning VM %s on external vCenter %s", vmName, hostIp));
String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST);
String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME); 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); DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter);
VirtualMachineMO vmMo = dataCenterMO.findVm(vmName); VirtualMachineMO vmMo = dataCenterMO.findVm(vmName);
if (vmMo == null) { 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); s_logger.error(err);
throw new CloudRuntimeException(err); throw new CloudRuntimeException(err);
} }
VirtualMachinePowerState sourceVmPowerState = vmMo.getPowerState(); VirtualMachinePowerState sourceVmPowerState = vmMo.getPowerState();
if (sourceVmPowerState == VirtualMachinePowerState.POWERED_ON && isWindowsVm(vmMo)) { if (sourceVmPowerState == VirtualMachinePowerState.POWERED_OFF) {
s_logger.debug(String.format("VM %s is a Windows VM and its Running, cannot be imported." + // Don't clone for powered off VMs, can export OVF from it
"Please gracefully shut it down before attempting the import", UnmanagedInstanceTO instanceTO = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
vmName)); 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); 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); 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); 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) { } 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); s_logger.error(err, e);
throw new CloudRuntimeException(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"); 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); UnmanagedInstanceTO sourceInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
List<UnmanagedInstanceTO.Disk> sourceDisks = sourceInstance.getDisks(); List<UnmanagedInstanceTO.Disk> sourceDisks = sourceInstance.getDisks();
List<UnmanagedInstanceTO.Disk> clonedDisks = clonedInstance.getDisks(); List<UnmanagedInstanceTO.Disk> clonedDisks = clonedInstance.getDisks();
@ -1410,12 +1456,40 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
} }
@Override @Override
public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) { public String createVMTemplateOutOfBand(String hostIp, String vmName, Map<String, String> params, DataStoreTO templateLocation, int threadsCountToExportOvf) {
s_logger.debug(String.format("Removing VM %s on external vCenter %s", vmName, hostIp));
String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST);
String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME); String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD); 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 { try {
VmwareContext context = connectToVcenter(vcenter, username, password); VmwareContext context = connectToVcenter(vcenter, username, password);
DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter); DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter);
@ -1426,11 +1500,97 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
s_logger.error(err); s_logger.error(err);
return false; return false;
} }
return vmMo.destroy(); return vmMo.destroy();
} catch (Exception e) { } 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); s_logger.error(err, e);
return false; 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; package com.cloud.hypervisor.guru;
import static org.junit.Assert.assertEquals; 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.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
import org.junit.After; import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.Spy; 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.Command;
import com.cloud.agent.api.MigrateVmToPoolCommand; 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.dc.ClusterDetailsDao;
import com.cloud.host.HostVO; import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao; 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.Storage.ProvisioningType;
import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.StoragePoolHostVO;
@ -51,8 +77,15 @@ import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO; import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.Pair; 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.VirtualMachine;
import com.cloud.vm.VirtualMachineManager; 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) @RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class) @ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@ -79,6 +112,21 @@ public class VMwareGuruTest {
AutoCloseable closeable; 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 @Before
public void testSetUp() throws Exception { public void testSetUp() throws Exception {
closeable = MockitoAnnotations.openMocks(this); closeable = MockitoAnnotations.openMocks(this);
@ -155,4 +203,415 @@ public class VMwareGuruTest {
assertEquals(expected, result); 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) { } else if (instance.getHostName() != null) {
response.setHostName(instance.getHostName()); 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.setCpuCores(instance.getCpuCores());
response.setCpuSpeed(instance.getCpuSpeed()); response.setCpuSpeed(instance.getCpuSpeed());
response.setCpuCoresPerSocket(instance.getCpuCoresPerSocket()); response.setCpuCoresPerSocket(instance.getCpuCoresPerSocket());

View File

@ -247,7 +247,7 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
host.getHypervisorType() == Hypervisor.HypervisorType.Custom)) { host.getHypervisorType() == Hypervisor.HypervisorType.Custom)) {
//only kvm has the requirement to return host details //only kvm has the requirement to return host details
try { try {
hostResponse.setDetails(hostDetails); hostResponse.setDetails(hostDetails, host.getHypervisorType());
} catch (Exception e) { } catch (Exception e) {
s_logger.debug("failed to get host details", 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 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}$"; 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> configValuesForValidation = new HashSet<String>();
private Set<String> weightBasedParametersForValidation; private Set<String> weightBasedParametersForValidation = new HashSet<String>();
private Set<String> overprovisioningFactorsForValidation; 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", 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); "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; return true;
} }
private void populateConfigValuesForValidationSet() { protected void populateConfigValuesForValidationSet() {
configValuesForValidation = new HashSet<String>();
configValuesForValidation.add("event.purge.interval"); configValuesForValidation.add("event.purge.interval");
configValuesForValidation.add("account.cleanup.interval"); configValuesForValidation.add("account.cleanup.interval");
configValuesForValidation.add("alert.wait"); 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(StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.key());
configValuesForValidation.add(UserDataManager.VM_USERDATA_MAX_LENGTH_STRING); configValuesForValidation.add(UserDataManager.VM_USERDATA_MAX_LENGTH_STRING);
configValuesForValidation.add(UnmanagedVMsManager.RemoteKvmInstanceDisksCopyTimeout.key()); configValuesForValidation.add(UnmanagedVMsManager.RemoteKvmInstanceDisksCopyTimeout.key());
configValuesForValidation.add(UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.key());
} }
private void weightBasedParametersForValidation() { private void weightBasedParametersForValidation() {
weightBasedParametersForValidation = new HashSet<String>();
weightBasedParametersForValidation.add(AlertManager.CPUCapacityThreshold.key()); weightBasedParametersForValidation.add(AlertManager.CPUCapacityThreshold.key());
weightBasedParametersForValidation.add(AlertManager.StorageAllocatedCapacityThreshold.key()); weightBasedParametersForValidation.add(AlertManager.StorageAllocatedCapacityThreshold.key());
weightBasedParametersForValidation.add(AlertManager.StorageCapacityThreshold.key()); weightBasedParametersForValidation.add(AlertManager.StorageCapacityThreshold.key());
@ -582,11 +581,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
weightBasedParametersForValidation.add(CapacityManager.SecondaryStorageCapacityThreshold.key()); weightBasedParametersForValidation.add(CapacityManager.SecondaryStorageCapacityThreshold.key());
weightBasedParametersForValidation.add(ClusterDrsService.ClusterDrsImbalanceThreshold.key()); weightBasedParametersForValidation.add(ClusterDrsService.ClusterDrsImbalanceThreshold.key());
weightBasedParametersForValidation.add(ClusterDrsService.ClusterDrsImbalanceSkipThreshold.key()); weightBasedParametersForValidation.add(ClusterDrsService.ClusterDrsImbalanceSkipThreshold.key());
} }
private void overProvisioningFactorsForValidation() { private void overProvisioningFactorsForValidation() {
overprovisioningFactorsForValidation = new HashSet<String>();
overprovisioningFactorsForValidation.add(CapacityManager.MemOverprovisioningFactor.key()); overprovisioningFactorsForValidation.add(CapacityManager.MemOverprovisioningFactor.key());
overprovisioningFactorsForValidation.add(CapacityManager.CpuOverprovisioningFactor.key()); overprovisioningFactorsForValidation.add(CapacityManager.CpuOverprovisioningFactor.key());
overprovisioningFactorsForValidation.add(CapacityManager.StorageOverprovisioningFactor.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); 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); final ConfigurationVO cfg = _configDao.findByName(name);
if (cfg == null) { if (cfg == null) {
s_logger.error("Missing configuration variable " + name + " in configuration table"); s_logger.error("Missing configuration variable " + name + " in configuration table");
@ -1256,45 +1252,48 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
return null; return null;
} }
if (type.equals(Integer.class) && NetworkModel.MACIdentifier.key().equalsIgnoreCase(name)) { if (type.equals(Integer.class)) {
try { try {
final int val = Integer.parseInt(value); final int val = Integer.parseInt(value);
//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 (type.equals(Integer.class) && configValuesForValidation.contains(name)) { if (NetworkModel.MACIdentifier.key().equalsIgnoreCase(name)) {
try { //The value need to be between 0 to 255 because the mac generation needs a value of 8 bit
final int val = Integer.parseInt(value); //0 value is considered as disable.
if (val <= 0) { if(val < 0 || val > 255){
throw new InvalidParameterValueException("Please enter a positive value for the configuration parameter:" + name); throw new InvalidParameterValueException(name + " value should be between 0 and 255. 0 value will disable this feature");
}
if ("vm.password.length".equalsIgnoreCase(name) && val < 6) {
throw new InvalidParameterValueException("Please enter a value greater than 5 for the configuration parameter:" + name);
}
if ("remote.access.vpn.psk.length".equalsIgnoreCase(name)) {
if (val < 8) {
throw new InvalidParameterValueException("Please enter a value greater than 7 for the configuration parameter:" + name);
}
if (val > 256) {
throw new InvalidParameterValueException("Please enter a value less than 257 for the configuration parameter:" + name);
} }
} }
if (UserDataManager.VM_USERDATA_MAX_LENGTH_STRING.equalsIgnoreCase(name)) {
if (val > 1048576) { if (UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.key().equalsIgnoreCase(name) || UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles.key().equalsIgnoreCase(name)) {
throw new InvalidParameterValueException("Please enter a value less than 1048576 for the configuration parameter:" + 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 (configValuesForValidation.contains(name)) {
if (val <= 0) {
throw new InvalidParameterValueException("Please enter a positive value for the configuration parameter:" + name);
}
if ("vm.password.length".equalsIgnoreCase(name) && val < 6) {
throw new InvalidParameterValueException("Please enter a value greater than 5 for the configuration parameter:" + name);
}
if ("remote.access.vpn.psk.length".equalsIgnoreCase(name)) {
if (val < 8) {
throw new InvalidParameterValueException("Please enter a value greater than 7 for the configuration parameter:" + name);
}
if (val > 256) {
throw new InvalidParameterValueException("Please enter a value less than 257 for the configuration parameter:" + name);
}
}
if (UserDataManager.VM_USERDATA_MAX_LENGTH_STRING.equalsIgnoreCase(name)) {
if (val > 1048576) {
throw new InvalidParameterValueException("Please enter a value less than 1048576 for the configuration parameter:" + name);
}
} }
} }
} catch (final NumberFormatException e) { } catch (final NumberFormatException e) {
s_logger.error("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:" + 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); throw new InvalidParameterValueException("Please enter a value between 0 and 1 for the configuration parameter: " + name);
} }
} catch (final NumberFormatException e) { } catch (final NumberFormatException e) {
s_logger.error("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:" + 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 org.apache.log4j.Logger;
import com.cloud.agent.api.Command; 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.DiskTO;
import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VirtualMachineTO;
@ -377,7 +378,7 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
} }
@Override @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"); s_logger.error("Unsupported operation: cannot clone external VM");
return null; return null;
} }
@ -387,4 +388,16 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
s_logger.error("Unsupported operation: cannot remove external VM"); s_logger.error("Unsupported operation: cannot remove external VM");
return false; 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, MountDisabledStoragePool,
VmwareCreateCloneFull, VmwareCreateCloneFull,
VmwareAllowParallelExecution, VmwareAllowParallelExecution,
ConvertVmwareInstanceToKvmTimeout,
DataStoreDownloadFollowRedirects DataStoreDownloadFollowRedirects
}; };
} }

View File

@ -19,6 +19,8 @@ package org.apache.cloudstack.vm;
import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer; 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.CheckVolumeAnswer;
import com.cloud.agent.api.CheckVolumeCommand; import com.cloud.agent.api.CheckVolumeCommand;
import com.cloud.agent.api.ConvertInstanceAnswer; import com.cloud.agent.api.ConvertInstanceAnswer;
@ -95,7 +97,6 @@ import com.cloud.storage.ScopeType;
import com.cloud.storage.Snapshot; import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO; import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePool;
import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateVO; 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 // Check for duplicate hostname in network, get all vms hostNames in the network
List<String> hostNames = vmDao.listDistinctHostNames(network.getId()); List<String> hostNames = vmDao.listDistinctHostNames(network.getId());
if (CollectionUtils.isNotEmpty(hostNames) && hostNames.contains(hostName)) { 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; 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); HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
Map<String, String> params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName, Map<String, String> params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName,
username, password, clusterName, sourceHostName, sourceVM); 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, 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, Account caller, Account owner, long userId,
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap, ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap,
Map<String, Long> nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap, Map<String, Long> nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap,
@ -1568,7 +1582,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
if (existingVcenterId != null) { if (existingVcenterId != null) {
VmwareDatacenterVO existingDC = vmwareDatacenterDao.findById(existingVcenterId); VmwareDatacenterVO existingDC = vmwareDatacenterDao.findById(existingVcenterId);
if (existingDC == null) { 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); LOGGER.error(err);
throw new CloudRuntimeException(err); throw new CloudRuntimeException(err);
} }
@ -1578,21 +1592,52 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
password = existingDC.getPassword(); password = existingDC.getPassword();
} }
UnmanagedInstanceTO clonedInstance = null; boolean isClonedInstance = false;
UnmanagedInstanceTO sourceVMwareInstance = null;
DataStoreTO temporaryConvertLocation = null;
String ovfTemplateOnConvertLocation = null;
try { 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); String instanceName = getGeneratedInstanceName(owner);
clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, sourceVMwareInstance, nicNetworkMap, nicIpAddressMap, forced);
clusterName, sourceHostName, sourceVM); UnmanagedInstanceTO convertedInstance;
checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); if (cmd.getForceMsToImportVmFiles() || !conversionSupportAnswer.isOvfExportSupported()) {
UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password, // Uses MS for OVF export to temporary conversion location
sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, convertStoragePoolId); int noOfThreads = UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.value();
sanitizeConvertedInstance(convertedInstance, clonedInstance); 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, UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null,
template, displayName, hostName, caller, owner, userId, template, displayName, hostName, caller, owner, userId,
serviceOffering, dataDiskOfferingMap, serviceOffering, dataDiskOfferingMap,
nicNetworkMap, nicIpAddressMap, nicNetworkMap, nicIpAddressMap,
details, false, forced, false); 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; return userVm;
} catch (CloudRuntimeException e) { } catch (CloudRuntimeException e) {
LOGGER.error(String.format("Error importing VM: %s", e.getMessage()), 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); cmd.getEventDescription(), null, null, 0);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
} finally { } 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, private void checkNetworkingBeforeConvertingVmwareInstance(DataCenter zone, Account owner, String instanceName,
String hostName, UnmanagedInstanceTO clonedInstance, String hostName, UnmanagedInstanceTO sourceVMwareInstance,
Map<String, Long> nicNetworkMap, Map<String, Long> nicNetworkMap,
Map<String, Network.IpAddresses> nicIpAddressMap, Map<String, Network.IpAddresses> nicIpAddressMap,
boolean forced) { boolean forced) {
List<UnmanagedInstanceTO.Nic> nics = clonedInstance.getNics(); List<UnmanagedInstanceTO.Nic> nics = sourceVMwareInstance.getNics();
List<Long> networkIds = new ArrayList<>(nicNetworkMap.values()); List<Long> networkIds = new ArrayList<>(nicNetworkMap.values());
if (nics.size() != networkIds.size()) { if (nics.size() != networkIds.size()) {
String msg = String.format("Different number of nics found on instance %s: %s vs %s nics provided", 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); LOGGER.error(msg);
throw new CloudRuntimeException(msg); throw new CloudRuntimeException(msg);
} }
@ -1641,8 +1691,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
private void checkUnmanagedNicAndNetworkMacAddressForImport(NetworkVO network, UnmanagedInstanceTO.Nic nic, boolean forced) { private void checkUnmanagedNicAndNetworkMacAddressForImport(NetworkVO network, UnmanagedInstanceTO.Nic nic, boolean forced) {
NicVO existingNic = nicDao.findByNetworkIdAndMacAddress(network.getId(), nic.getMacAddress()); NicVO existingNic = nicDao.findByNetworkIdAndMacAddress(network.getId(), nic.getMacAddress());
if (existingNic != null && !forced) { if (existingNic != null && !forced) {
String err = String.format("NIC with MAC address = %s exists on network with ID = %s and forced flag is disabled", String err = String.format("NIC with MAC address %s already exists on network with ID %s and forced flag is disabled. " +
nic.getMacAddress(), network.getId()); "Retry with forced flag enabled if a new MAC address to be generated.", nic.getMacAddress(), network.getUuid());
LOGGER.error(err); LOGGER.error(err);
throw new CloudRuntimeException(err); throw new CloudRuntimeException(err);
} }
@ -1657,38 +1707,44 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return VirtualMachineName.getVmName(id, owner.getId(), instanceSuffix); return VirtualMachineName.getVmName(id, owner.getId(), instanceSuffix);
} }
private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, UnmanagedInstanceTO clonedInstance) { private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, UnmanagedInstanceTO sourceVMwareInstance) {
convertedInstance.setCpuCores(clonedInstance.getCpuCores()); convertedInstance.setCpuCores(sourceVMwareInstance.getCpuCores());
convertedInstance.setCpuSpeed(clonedInstance.getCpuSpeed()); convertedInstance.setCpuSpeed(sourceVMwareInstance.getCpuSpeed());
convertedInstance.setCpuCoresPerSocket(clonedInstance.getCpuCoresPerSocket()); convertedInstance.setCpuCoresPerSocket(sourceVMwareInstance.getCpuCoresPerSocket());
convertedInstance.setMemory(clonedInstance.getMemory()); convertedInstance.setMemory(sourceVMwareInstance.getMemory());
convertedInstance.setPowerState(UnmanagedInstanceTO.PowerState.PowerOff); convertedInstance.setPowerState(UnmanagedInstanceTO.PowerState.PowerOff);
List<UnmanagedInstanceTO.Disk> convertedInstanceDisks = convertedInstance.getDisks(); 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++) { for (int i = 0; i < convertedInstanceDisks.size(); i++) {
UnmanagedInstanceTO.Disk disk = convertedInstanceDisks.get(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> convertedInstanceNics = convertedInstance.getNics();
List<UnmanagedInstanceTO.Nic> clonedInstanceNics = clonedInstance.getNics(); List<UnmanagedInstanceTO.Nic> sourceVMwareInstanceNics = sourceVMwareInstance.getNics();
if (CollectionUtils.isEmpty(convertedInstanceNics) && CollectionUtils.isNotEmpty(clonedInstanceNics)) { if (CollectionUtils.isEmpty(convertedInstanceNics) && CollectionUtils.isNotEmpty(sourceVMwareInstanceNics)) {
for (UnmanagedInstanceTO.Nic nic : clonedInstanceNics) { 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 // In case the NICs information is not parsed from the converted XML domain, use the cloned instance NICs with virtio adapter
nic.setAdapterType("virtio"); nic.setAdapterType("virtio");
} }
convertedInstance.setNics(clonedInstanceNics); convertedInstance.setNics(sourceVMwareInstanceNics);
} else {
for (int i = 0; i < convertedInstanceNics.size(); i++) { for (int i = 0; i < convertedInstanceNics.size(); i++) {
UnmanagedInstanceTO.Nic nic = convertedInstanceNics.get(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, private void removeClonedInstance(String vcenter, String datacenterName, String username, String password,
String username, String password, String sourceHostName, String clonedInstanceName, String sourceVM) {
String sourceHostName, String clonedInstanceName,
String sourceVM) {
HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware); HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
Map<String, String> params = createParamsForRemoveClonedInstance(vcenter, datacenterName, username, password, sourceVM); Map<String, String> params = createParamsForRemoveClonedInstance(vcenter, datacenterName, username, password, sourceVM);
boolean result = vmwareGuru.removeClonedHypervisorVMOutOfBand(sourceHostName, clonedInstanceName, params); boolean result = vmwareGuru.removeClonedHypervisorVMOutOfBand(sourceHostName, clonedInstanceName, params);
@ -1698,10 +1754,23 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
LOGGER.warn(msg); LOGGER.warn(msg);
return; 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)); 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, private Map<String, String> createParamsForRemoveClonedInstance(String vcenter, String datacenterName, String username,
String password, String sourceVM) { String password, String sourceVM) {
Map<String, String> params = new HashMap<>(); Map<String, String> params = new HashMap<>();
@ -1712,7 +1781,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return params; return params;
} }
private HostVO selectInstanceConvertionKVMHostInCluster(Cluster destinationCluster, Long convertInstanceHostId) { private HostVO selectInstanceConversionKVMHostInCluster(Cluster destinationCluster, Long convertInstanceHostId) {
if (convertInstanceHostId != null) { if (convertInstanceHostId != null) {
HostVO selectedHost = hostDao.findById(convertInstanceHostId); HostVO selectedHost = hostDao.findById(convertInstanceHostId);
if (selectedHost == null) { if (selectedHost == null) {
@ -1729,41 +1798,62 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
} }
return selectedHost; return selectedHost;
} }
List<HostVO> hosts = hostDao.listByClusterAndHypervisorType(destinationCluster.getId(), destinationCluster.getHypervisorType());
if (CollectionUtils.isEmpty(hosts)) { // Auto select host with conversion capability
String err = String.format("Could not find any running %s host in cluster %s", List<HostVO> hosts = hostDao.listByClusterHypervisorTypeAndHostCapability(destinationCluster.getId(), destinationCluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION);
destinationCluster.getHypervisorType(), destinationCluster.getName()); if (CollectionUtils.isNotEmpty(hosts)) {
LOGGER.error(err); return hosts.get(new Random().nextInt(hosts.size()));
throw new CloudRuntimeException(err);
} }
List<HostVO> filteredHosts = hosts.stream()
.filter(x -> x.getResourceState() == ResourceState.Enabled) // Try without host capability check
.collect(Collectors.toList()); hosts = hostDao.listByClusterAndHypervisorType(destinationCluster.getId(), destinationCluster.getHypervisorType());
if (CollectionUtils.isEmpty(filteredHosts)) { if (CollectionUtils.isNotEmpty(hosts)) {
String err = String.format("Could not find a %s host in cluster %s to perform the instance conversion", return hosts.get(new Random().nextInt(hosts.size()));
destinationCluster.getHypervisorType(), destinationCluster.getName());
LOGGER.error(err);
throw new CloudRuntimeException(err);
} }
return filteredHosts.get(new Random().nextInt(filteredHosts.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);
} }
private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String datacenterName, String clusterName, private CheckConvertInstanceAnswer checkConversionSupportOnHost(HostVO convertHost, String sourceVM, boolean checkWindowsGuestConversionSupport) {
String username, String password, String hostName, LOGGER.debug(String.format("Checking the %s conversion support on the host %s (%s)", checkWindowsGuestConversionSupport? "windows guest" : "", convertHost.getId(), convertHost.getName()));
UnmanagedInstanceTO clonedInstance, Cluster destinationCluster, CheckConvertInstanceCommand cmd = new CheckConvertInstanceCommand(checkWindowsGuestConversionSupport);
Long convertInstanceHostId, Long convertStoragePoolId) { int timeoutSeconds = 60;
HostVO convertHost = selectInstanceConvertionKVMHostInCluster(destinationCluster, convertInstanceHostId); cmd.setWait(timeoutSeconds);
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));
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(hostName, vmName, CheckConvertInstanceAnswer checkConvertInstanceAnswer;
vcenter, datacenterName, clusterName, username, password); try {
DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, convertHost); checkConvertInstanceAnswer = (CheckConvertInstanceAnswer) agentManager.send(convertHost.getId(), cmd);
List<String> destinationStoragePools = selectInstanceConvertionStoragePools(destinationCluster, clonedInstance.getDisks()); } 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);
}
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);
}
return checkConvertInstanceAnswer;
}
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, ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO,
Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation); Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, ovfTemplateDirConvertLocation, false, false);
int timeoutSeconds = StorageManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60; int timeoutSeconds = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
cmd.setWait(timeoutSeconds); cmd.setWait(timeoutSeconds);
Answer convertAnswer; Answer convertAnswer;
@ -1777,17 +1867,68 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
} }
if (!convertAnswer.getResult()) { if (!convertAnswer.getResult()) {
String err = String.format("The convert process failed for instance %s from Vmware to KVM on host %s: %s", String err = String.format("The convert process failed for instance %s from VMware to KVM on host %s: %s",
vmName, convertHost.getName(), convertAnswer.getDetails()); sourceVM, convertHost.getName(), convertAnswer.getDetails());
LOGGER.error(err); LOGGER.error(err);
throw new CloudRuntimeException(err); throw new CloudRuntimeException(err);
} }
return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance(); 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<String> storagePools = new ArrayList<>(disks.size());
List<StoragePoolVO> pools = primaryDataStoreDao.listPoolsByCluster(destinationCluster.getId());
//TODO: Choose pools by capacity //TODO: Choose pools by capacity
for (UnmanagedInstanceTO.Disk disk : disks) { for (UnmanagedInstanceTO.Disk disk : disks) {
Long capacity = disk.getCapacity(); Long capacity = disk.getCapacity();
@ -1801,7 +1942,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
throw new CloudRuntimeException(msg); throw new CloudRuntimeException(msg);
} }
protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId, HostVO convertHost) { protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId) {
if (convertStoragePoolId != null) { if (convertStoragePoolId != null) {
StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId); StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId);
if (selectedStoragePool == null) { 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 " + 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())); "it is not in the scope of the cluster %s", selectedStoragePool.getName(), destinationCluster.getName()));
} }
if (selectedStoragePool.getScope() == ScopeType.HOST && if (selectedStoragePool.getScope() == ScopeType.HOST) {
storagePoolHostDao.findByPoolHost(selectedStoragePool.getId(), convertHost.getId()) == null) { 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()));
logFailureAndThrowException(String.format("The storage pool %s is not a local storage pool for the host %s", selectedStoragePool.getName(), convertHost.getName()));
} else if (selectedStoragePool.getPoolType() != Storage.StoragePoolType.NetworkFilesystem) { } 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(); return dataStoreManager.getPrimaryDataStore(convertStoragePoolId).getTO();
} else { } else {
@ -2505,7 +2645,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
public ConfigKey<?>[] getConfigKeys() { public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[]{ return new ConfigKey<?>[]{
UnmanageVMPreserveNic, UnmanageVMPreserveNic,
RemoteKvmInstanceDisksCopyTimeout RemoteKvmInstanceDisksCopyTimeout,
ConvertVmwareInstanceToKvmTimeout,
ThreadsOnMSToImportVMwareVMFiles,
ThreadsOnKVMHostToImportVMwareVMFiles
}; };
} }
} }

View File

@ -16,6 +16,9 @@
// under the License. // under the License.
package com.cloud.configuration; package com.cloud.configuration;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManager;
import com.cloud.utils.net.NetUtils; 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.EntityManager;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd; 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.DiskOfferingDetailVO;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.vm.UnmanagedVMsManager;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -47,7 +53,6 @@ import org.mockito.Spy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class ConfigurationManagerImplTest { public class ConfigurationManagerImplTest {
@Mock @Mock
@ -65,6 +70,8 @@ public class ConfigurationManagerImplTest {
@Mock @Mock
Domain domainMock; Domain domainMock;
@Mock @Mock
ConfigurationDao configDaoMock;
@Mock
DataCenterDao zoneDaoMock; DataCenterDao zoneDaoMock;
@Mock @Mock
DomainDao domainDaoMock; DomainDao domainDaoMock;
@ -300,6 +307,66 @@ public class ConfigurationManagerImplTest {
configurationManagerImplSpy.validateIpAddressRelatedConfigValues("config.iprange", "192.168.1.1-192.168.1.100"); 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 @Test
public void validateDomainTestInvalidIdThrowException() { public void validateDomainTestInvalidIdThrowException() {
Mockito.doReturn(null).when(domainDaoMock).findById(invalidId); 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.AgentManager;
import com.cloud.agent.api.Answer; 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.CheckVolumeAnswer;
import com.cloud.agent.api.CheckVolumeCommand; import com.cloud.agent.api.CheckVolumeCommand;
import com.cloud.agent.api.ConvertInstanceAnswer; 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.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
@ -586,6 +589,7 @@ public class UnmanagedVMsManagerImplTest {
String host = "192.168.1.10"; String host = "192.168.1.10";
String vmName = "TestInstanceFromVmware"; String vmName = "TestInstanceFromVmware";
instance.setName(vmName); instance.setName(vmName);
String tmplFileName = "5b8d689a-e61a-4ac3-9b76-e121ff90fbd3";
long newVmId = 2L; long newVmId = 2L;
long networkId = 1L; long networkId = 1L;
when(vmDao.getNextInSequence(Long.class, "id")).thenReturn(newVmId); when(vmDao.getNextInSequence(Long.class, "id")).thenReturn(newVmId);
@ -614,8 +618,10 @@ public class UnmanagedVMsManagerImplTest {
HypervisorGuru vmwareGuru = mock(HypervisorGuru.class); HypervisorGuru vmwareGuru = mock(HypervisorGuru.class);
when(hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware)).thenReturn(vmwareGuru); 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.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); HostVO convertHost = mock(HostVO.class);
long convertHostId = 1L; long convertHostId = 1L;
@ -639,6 +645,7 @@ public class UnmanagedVMsManagerImplTest {
when(destPool.getDataCenterId()).thenReturn(zoneId); when(destPool.getDataCenterId()).thenReturn(zoneId);
when(destPool.getClusterId()).thenReturn(null); when(destPool.getClusterId()).thenReturn(null);
when(destPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); when(destPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
StoragePoolVO zoneDestPool = mock(StoragePoolVO.class);
if (selectTemporaryStorage) { if (selectTemporaryStorage) {
long temporaryStoragePoolId = 1L; long temporaryStoragePoolId = 1L;
when(importVmCmd.getConvertStoragePoolId()).thenReturn(temporaryStoragePoolId); when(importVmCmd.getConvertStoragePoolId()).thenReturn(temporaryStoragePoolId);
@ -650,8 +657,9 @@ public class UnmanagedVMsManagerImplTest {
when(imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs")).thenReturn(imageStoreVO); when(imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs")).thenReturn(imageStoreVO);
when(dataStoreManager.getDataStore(1L, DataStoreRole.Image)).thenReturn(dataStore); 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.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) { if (VcenterParameter.EXISTING == vcenterParameter) {
VmwareDatacenterVO datacenterVO = mock(VmwareDatacenterVO.class); VmwareDatacenterVO datacenterVO = mock(VmwareDatacenterVO.class);
@ -679,17 +687,25 @@ public class UnmanagedVMsManagerImplTest {
when(vmwareDatacenterDao.findById(existingDatacenterId)).thenReturn(null); when(vmwareDatacenterDao.findById(existingDatacenterId)).thenReturn(null);
} }
ConvertInstanceAnswer answer = mock(ConvertInstanceAnswer.class); CheckConvertInstanceAnswer checkConvertInstanceAnswer = mock(CheckConvertInstanceAnswer.class);
when(answer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE); when(checkConvertInstanceAnswer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);
when(answer.getConvertedInstance()).thenReturn(instance);
if (VcenterParameter.AGENT_UNAVAILABLE != vcenterParameter) { 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)) { try (MockedStatic<UsageEventUtils> ignored = Mockito.mockStatic(UsageEventUtils.class)) {
unmanagedVMsManager.importVm(importVmCmd); 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).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; long poolId = 1L;
when(primaryDataStoreDao.findById(poolId)).thenReturn(null); when(primaryDataStoreDao.findById(poolId)).thenReturn(null);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, null); unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
@ -808,7 +824,7 @@ public class UnmanagedVMsManagerImplTest {
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER); Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
Mockito.when(pool.getClusterId()).thenReturn(100L); Mockito.when(pool.getClusterId()).thenReturn(100L);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, null); unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
@ -818,9 +834,7 @@ public class UnmanagedVMsManagerImplTest {
StoragePoolVO pool = mock(StoragePoolVO.class); StoragePoolVO pool = mock(StoragePoolVO.class);
Mockito.when(pool.getScope()).thenReturn(ScopeType.HOST); Mockito.when(pool.getScope()).thenReturn(ScopeType.HOST);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
HostVO convertHost = Mockito.mock(HostVO.class); unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
Mockito.when(convertHost.getId()).thenReturn(1L);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, convertHost);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
@ -831,15 +845,14 @@ public class UnmanagedVMsManagerImplTest {
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER); Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
Mockito.when(pool.getClusterId()).thenReturn(1L); Mockito.when(pool.getClusterId()).thenReturn(1L);
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
HostVO convertHost = Mockito.mock(HostVO.class);
Mockito.when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD); Mockito.when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD);
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, convertHost); unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() { public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() {
ClusterVO cluster = getClusterForTests(); ClusterVO cluster = getClusterForTests();
Mockito.when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null); 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.for": "for",
"label.forbidden": "Forbidden", "label.forbidden": "Forbidden",
"label.forced": "Force", "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.stop": "Force stop",
"label.force.reboot": "Force reboot", "label.force.reboot": "Force reboot",
"label.forceencap": "Force UDP encapsulation of ESP packets", "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.subtitle": "This guide will aid you in setting up your CloudStack™ installation",
"label.installwizard.title": "Hello and welcome to CloudStack™", "label.installwizard.title": "Hello and welcome to CloudStack™",
"label.instance": "Instance", "label.instance": "Instance",
"label.instance.conversion.support": "Instance Conversion Supported",
"label.instance.groups": "Instance groups", "label.instance.groups": "Instance groups",
"label.instance.name": "Instance name", "label.instance.name": "Instance name",
"label.instancename": "Internal name", "label.instancename": "Internal name",
@ -2039,6 +2041,7 @@
"label.suitable": "Suitable", "label.suitable": "Suitable",
"label.summary": "Summary", "label.summary": "Summary",
"label.sunday": "Sunday", "label.sunday": "Sunday",
"label.supported": "Supported",
"label.supportedservices": "Supported services", "label.supportedservices": "Supported services",
"label.supportsautoscaling": "Supports auto scaling", "label.supportsautoscaling": "Supports auto scaling",
"label.supportsha": "Supports HA", "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.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.disk.offering": "Please select a disk offering for disk",
"message.select.end.date.and.time": "Select an end date & time.", "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.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.migration.policy": "Please select a migration policy.",
"message.select.nic.network": "Please select a Network for NIC", "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.security.groups": "Please select security group(s) for your new Instance.",
"message.select.start.date.and.time": "Select a start date & time.", "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.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.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.", "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>
</div> </div>
</a-list-item> </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"> <a-list-item v-if="host.hosttags">
<div> <div>
<strong>{{ $t('label.hosttags') }}</strong> <strong>{{ $t('label.hosttags') }}</strong>

View File

@ -158,7 +158,7 @@
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter" v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
:resourceKey="cluster.id" :resourceKey="cluster.id"
:selectOptions="kvmHostsForConversion" :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" :defaultCheckBoxValue="false"
:reversed="false" :reversed="false"
@handle-checkselectpair-change="updateSelectedKvmHostForConversion" @handle-checkselectpair-change="updateSelectedKvmHostForConversion"
@ -167,11 +167,11 @@
<a-form-item name="convertstorageoption" ref="convertstorageoption"> <a-form-item name="convertstorageoption" ref="convertstorageoption">
<check-box-select-pair <check-box-select-pair
layout="vertical" layout="vertical"
style="margin-bottom: 20px" style="margin-bottom: 5px"
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter" v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
:resourceKey="cluster.id" :resourceKey="cluster.id"
:selectOptions="storageOptionsForConversion" :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" :defaultCheckBoxValue="false"
:reversed="false" :reversed="false"
@handle-checkselectpair-change="updateSelectedStorageOptionForConversion" @handle-checkselectpair-change="updateSelectedStorageOptionForConversion"
@ -192,6 +192,12 @@
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </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"> <a-form-item name="serviceofferingid" ref="serviceofferingid">
<template #label> <template #label>
<tooltip-label :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/> <tooltip-label :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/>
@ -518,6 +524,12 @@ export default {
this.apiConfig.params.forEach(param => { this.apiConfig.params.forEach(param => {
this.apiParams[param.name] = 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 () { created () {
this.initForm() this.initForm()
@ -696,6 +708,7 @@ export default {
rootdiskid: 0, rootdiskid: 0,
migrateallowed: this.switches.migrateAllowed, migrateallowed: this.switches.migrateAllowed,
forced: this.switches.forced, forced: this.switches.forced,
forcemstoimportvmfiles: this.switches.forceMsToImportVmFiles,
domainid: null, domainid: null,
account: null account: null
}) })
@ -909,13 +922,18 @@ export default {
resourcestate: 'Enabled' resourcestate: 'Enabled'
}).then(json => { }).then(json => {
this.kvmHostsForConversion = json.listhostsresponse.host || [] 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 () { fetchStoragePoolsForConversion () {
if (this.selectedStorageOptionForConversion === 'primary') { if (this.selectedStorageOptionForConversion === 'primary') {
api('listStoragePools', { api('listStoragePools', {
zoneid: this.cluster.zoneid, zoneid: this.cluster.zoneid,
state: 'Up' status: 'Up'
}).then(json => { }).then(json => {
this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || [] this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || []
}) })
@ -924,7 +942,7 @@ export default {
api('listStoragePools', { api('listStoragePools', {
scope: 'HOST', scope: 'HOST',
ipaddress: kvmHost.ipaddress, ipaddress: kvmHost.ipaddress,
state: 'Up' status: 'Up'
}).then(json => { }).then(json => {
this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || [] this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || []
}) })
@ -1084,8 +1102,9 @@ export default {
if (this.selectedStoragePoolForConversion) { if (this.selectedStoragePoolForConversion) {
params.convertinstancepoolid = 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') { if (this.templateType !== 'auto') {
keys.push('templateid') keys.push('templateid')
} }

View File

@ -144,7 +144,12 @@ public class BaseMO {
public int getCustomFieldKey(String morType, String fieldName) throws Exception { public int getCustomFieldKey(String morType, String fieldName) throws Exception {
assert (morType != null); 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); return cfmMo.getCustomFieldKey(morType, fieldName);
} }

View File

@ -32,9 +32,12 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.utils.exception.CloudRuntimeException; 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 { 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(); ManagedObjectReference morOvf = _context.getServiceContent().getOvfManager();
VirtualMachineRuntimeInfo runtimeInfo = getRuntimeInfo(); VirtualMachineRuntimeInfo runtimeInfo = getRuntimeInfo();
@ -1827,18 +1834,31 @@ public class VirtualMachineMO extends BaseMO {
final HttpNfcLeaseMO.ProgressReporter progressReporter = leaseMo.createProgressReporter(); final HttpNfcLeaseMO.ProgressReporter progressReporter = leaseMo.createProgressReporter();
boolean success = false; boolean success = false;
List<String> fileNames = new ArrayList<String>(); List<String> fileNames = new ArrayList<>();
try { try {
HttpNfcLeaseInfo leaseInfo = leaseMo.getLeaseInfo(); HttpNfcLeaseInfo leaseInfo = leaseMo.getLeaseInfo();
final long totalBytes = leaseInfo.getTotalDiskCapacityInKB() * 1024; final long totalBytes = leaseInfo.getTotalDiskCapacityInKB() * 1024;
long totalBytesDownloaded = 0; AtomicLong totalBytesDownloaded = new AtomicLong(0L);
List<HttpNfcLeaseDeviceUrl> deviceUrls = leaseInfo.getDeviceUrl(); List<HttpNfcLeaseDeviceUrl> deviceUrls = leaseInfo.getDeviceUrl();
s_logger.info("volss: copy vmdk and ovf file starts " + System.currentTimeMillis()); s_logger.info("volss: copy vmdk and ovf file starts " + System.currentTimeMillis());
if (deviceUrls != null) { if (deviceUrls != null) {
OvfFile[] ovfFiles = new OvfFile[deviceUrls.size()]; int deviceUrlsCount = deviceUrls.size();
for (int i = 0; i < deviceUrls.size(); i++) { 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(); String deviceId = deviceUrls.get(i).getKey();
Long diskFileSize = deviceUrls.get(i).getFileSize();
String deviceUrlStr = deviceUrls.get(i).getUrl(); String deviceUrlStr = deviceUrls.get(i).getUrl();
String orgDiskFileName = deviceUrlStr.substring(deviceUrlStr.lastIndexOf("/") + 1); String orgDiskFileName = deviceUrlStr.substring(deviceUrlStr.lastIndexOf("/") + 1);
String diskFileName = String.format("%s-disk%d%s", exportName, i, VmwareHelper.getFileExtension(orgDiskFileName, ".vmdk")); String diskFileName = String.format("%s-disk%d%s", exportName, i, VmwareHelper.getFileExtension(orgDiskFileName, ".vmdk"));
@ -1847,25 +1867,77 @@ public class VirtualMachineMO extends BaseMO {
String diskLocalPath = exportDir + File.separator + diskFileName; String diskLocalPath = exportDir + File.separator + diskFileName;
fileNames.add(diskLocalPath); fileNames.add(diskLocalPath);
if (s_logger.isInfoEnabled()) { if (!parallelDownload) {
s_logger.info("Download VMDK file for export. url: " + deviceUrlStr); if (s_logger.isInfoEnabled()) {
} s_logger.info("Download VMDK file for export url: " + deviceUrlStr + ", size: " + diskFileSize);
long lengthOfDiskFile = _context.downloadVmdkFile(diskUrlStr, 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));
} }
}); long lengthOfDiskFile = _context.downloadVmdkFile(diskUrlStr, diskLocalPath, totalBytesDownloaded, new ActionDelegate<Long>() {
totalBytesDownloaded += lengthOfDiskFile; @Override
public void action(Long param) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Download progress " + param + "/" + toHumanReadableSize(totalBytes));
}
progressReporter.reportProgress((int)(param * 100 / totalBytes));
}
});
totalBytesDownloaded.addAndGet(lengthOfDiskFile);
OvfFile ovfFile = new OvfFile(); OvfFile ovfFile = new OvfFile();
ovfFile.setPath(diskFileName); ovfFile.setPath(diskFileName);
ovfFile.setDeviceId(deviceId); ovfFile.setDeviceId(deviceId);
ovfFile.setSize(lengthOfDiskFile); ovfFile.setSize(lengthOfDiskFile);
ovfFiles[i] = ovfFile; 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 // write OVF descriptor file
@ -1892,7 +1964,9 @@ public class VirtualMachineMO extends BaseMO {
command.add("-cf", exportName + ".ova"); command.add("-cf", exportName + ".ova");
command.add(exportName + ".ovf"); // OVF file should be the first file in OVA archive command.add(exportName + ".ovf"); // OVF file should be the first file in OVA archive
for (String name : fileNames) { for (String name : fileNames) {
command.add((new File(name).getName())); if (!name.endsWith(".ovf")) {
command.add((new File(name).getName()));
}
} }
s_logger.info("Package OVA with command: " + command.toString()); s_logger.info("Package OVA with command: " + command.toString());

View File

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

View File

@ -807,8 +807,13 @@ public class VmwareHelper {
instance.setMemory(configSummary.getMemorySizeMB()); instance.setMemory(configSummary.getMemorySizeMB());
} }
ClusterMO clusterMo = new ClusterMO(hyperHost.getContext(), hyperHost.getHyperHostCluster()); try {
instance.setClusterName(clusterMo.getName()); 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()); instance.setHostName(hyperHost.getHyperHostName());
if (StringUtils.isEmpty(instance.getOperatingSystemId()) && configSummary != null) { if (StringUtils.isEmpty(instance.getOperatingSystemId()) && configSummary != null) {
@ -838,7 +843,7 @@ public class VmwareHelper {
instance.setDisks(getUnmanageInstanceDisks(vmMo)); instance.setDisks(getUnmanageInstanceDisks(vmMo));
instance.setNics(getUnmanageInstanceNics(hyperHost, vmMo)); instance.setNics(getUnmanageInstanceNics(hyperHost, vmMo));
} catch (Exception e) { } 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; return instance;
} }