Skip to content

Commit 46f6725

Browse files
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
1 parent 23f8856 commit 46f6725

File tree

43 files changed

+1889
-339
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1889
-339
lines changed

agent/src/main/java/com/cloud/agent/properties/AgentProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ public Property<Integer> getWorkers() {
751751
public static final Property<Integer> IOTHREADS = new Property<>("iothreads", 1);
752752

753753
/**
754-
* Enable verbose mode for virt-v2v Instance Conversion from Vmware to KVM
754+
* Enable verbose mode for virt-v2v Instance Conversion from VMware to KVM
755755
* Data type: Boolean.<br>
756756
* Default value: <code>false</code>
757757
*/

api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,40 +18,39 @@
1818
*/
1919
package com.cloud.agent.api.to;
2020

21+
import java.io.Serializable;
22+
2123
import com.cloud.agent.api.LogLevel;
2224
import com.cloud.hypervisor.Hypervisor;
2325

24-
import java.io.Serializable;
25-
2626
public class RemoteInstanceTO implements Serializable {
2727

2828
private Hypervisor.HypervisorType hypervisorType;
29-
private String hostName;
3029
private String instanceName;
3130

32-
// Vmware Remote Instances parameters
31+
// VMware Remote Instances parameters (required for exporting OVA through ovftool)
3332
// TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes
3433
private String vcenterUsername;
3534
@LogLevel(LogLevel.Log4jLevel.Off)
3635
private String vcenterPassword;
3736
private String vcenterHost;
3837
private String datacenterName;
39-
private String clusterName;
4038

4139
public RemoteInstanceTO() {
4240
}
4341

44-
public RemoteInstanceTO(String hostName, String instanceName, String vcenterHost,
45-
String datacenterName, String clusterName,
46-
String vcenterUsername, String vcenterPassword) {
42+
public RemoteInstanceTO(String instanceName) {
43+
this.hypervisorType = Hypervisor.HypervisorType.VMware;
44+
this.instanceName = instanceName;
45+
}
46+
47+
public RemoteInstanceTO(String instanceName, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) {
4748
this.hypervisorType = Hypervisor.HypervisorType.VMware;
48-
this.hostName = hostName;
4949
this.instanceName = instanceName;
5050
this.vcenterHost = vcenterHost;
51-
this.datacenterName = datacenterName;
52-
this.clusterName = clusterName;
5351
this.vcenterUsername = vcenterUsername;
5452
this.vcenterPassword = vcenterPassword;
53+
this.datacenterName = datacenterName;
5554
}
5655

5756
public Hypervisor.HypervisorType getHypervisorType() {
@@ -62,10 +61,6 @@ public String getInstanceName() {
6261
return this.instanceName;
6362
}
6463

65-
public String getHostName() {
66-
return this.hostName;
67-
}
68-
6964
public String getVcenterUsername() {
7065
return vcenterUsername;
7166
}
@@ -81,8 +76,4 @@ public String getVcenterHost() {
8176
public String getDatacenterName() {
8277
return datacenterName;
8378
}
84-
85-
public String getClusterName() {
86-
return clusterName;
87-
}
8879
}

api/src/main/java/com/cloud/host/Host.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public static String[] toStrings(Host.Type... types) {
5454
}
5555
public static final String HOST_UEFI_ENABLE = "host.uefi.enable";
5656
public static final String HOST_VOLUME_ENCRYPTION = "host.volume.encryption";
57+
public static final String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
5758

5859
/**
5960
* @return name of the machine.

api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.cloudstack.framework.config.ConfigKey;
2424

2525
import com.cloud.agent.api.Command;
26+
import com.cloud.agent.api.to.DataStoreTO;
2627
import com.cloud.agent.api.to.NicTO;
2728
import com.cloud.agent.api.to.VirtualMachineTO;
2829
import com.cloud.hypervisor.Hypervisor.HypervisorType;
@@ -101,21 +102,20 @@ boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backu
101102
* Will generate commands to migrate a vm to a pool. For now this will only work for stopped VMs on Vmware.
102103
*
103104
* @param vm the stopped vm to migrate
104-
* @param destination the primary storage pool to migrate to
105+
* @param volumeToPool the primary storage pools to migrate to
105106
* @return a list of commands to perform for a successful migration
106107
*/
107108
List<Command> finalizeMigrate(VirtualMachine vm, Map<Volume, StoragePool> volumeToPool);
108109

109110

110111
/**
111-
* Will perform a clone of a VM on an external host (if the guru can handle)
112+
* 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)
112113
* @param hostIp VM's source host IP
113-
* @param vmName name of the source VM to clone from
114+
* @param vmName name of the source VM (clone VM name if cloned)
114115
* @param params hypervisor specific additional parameters
115-
* @return a reference to the cloned VM
116+
* @return a reference to the hypervisor or cloned VM, and cloned flag
116117
*/
117-
UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName,
118-
Map<String, String> params);
118+
Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map<String, String> params);
119119

120120
/**
121121
* Removes a VM created as a clone of a VM on an external host
@@ -124,6 +124,23 @@ UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName,
124124
* @param params hypervisor specific additional parameters
125125
* @return true if the operation succeeds, false if not
126126
*/
127-
boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName,
128-
Map<String, String> params);
127+
boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params);
128+
129+
/**
130+
* Create an OVA/OVF template of a VM on an external host (if the guru can handle)
131+
* @param hostIp VM's source host IP
132+
* @param vmName name of the source VM to create template from
133+
* @param params hypervisor specific additional parameters
134+
* @param templateLocation datastore to create the template file
135+
* @return the created template dir/name
136+
*/
137+
String createVMTemplateOutOfBand(String hostIp, String vmName, Map<String, String> params, DataStoreTO templateLocation, int threadsCountToExportOvf);
138+
139+
/**
140+
* Removes the template on the location
141+
* @param templateLocation datastore to remove the template file
142+
* @param templateDir the template dir to remove from datastore
143+
* @return true if the operation succeeds, false if not
144+
*/
145+
boolean removeVMTemplateOutOfBand(DataStoreTO templateLocation, String templateDir);
129146
}

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ public class ApiConstants {
189189
public static final String FORCED = "forced";
190190
public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
191191
public static final String FORCE_DELETE_HOST = "forcedeletehost";
192+
public static final String FORCE_MS_TO_IMPORT_VM_FILES = "forcemstoimportvmfiles";
192193
public static final String FORMAT = "format";
193194
public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
194195
public static final String FOR_SYSTEM_VMS = "forsystemvms";
@@ -236,6 +237,7 @@ public class ApiConstants {
236237
public static final String NEXT_ACL_RULE_ID = "nextaclruleid";
237238
public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash";
238239
public static final String IMAGE_PATH = "imagepath";
240+
public static final String INSTANCE_CONVERSION_SUPPORTED = "instanceconversionsupported";
239241
public static final String INTERNAL_DNS1 = "internaldns1";
240242
public static final String INTERNAL_DNS2 = "internaldns2";
241243
public static final String INTERNET_PROTOCOL = "internetprotocol";

api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.cloudstack.api.response.VmwareDatacenterResponse;
3838
import org.apache.cloudstack.api.response.ZoneResponse;
3939
import org.apache.cloudstack.vm.VmImportService;
40+
import org.apache.commons.lang3.BooleanUtils;
4041
import org.apache.commons.lang3.ObjectUtils;
4142
import org.apache.commons.lang3.StringUtils;
4243
import org.apache.log4j.Logger;
@@ -118,40 +119,44 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
118119
description = "Temp Path on external host for disk image copy" )
119120
private String tmpPath;
120121

121-
// Import from Vmware to KVM migration parameters
122+
// Import from VMware to KVM migration parameters
122123

123124
@Parameter(name = ApiConstants.EXISTING_VCENTER_ID,
124125
type = CommandType.UUID,
125126
entityType = VmwareDatacenterResponse.class,
126-
description = "(only for importing migrated VMs from Vmware to KVM) UUID of a linked existing vCenter")
127+
description = "(only for importing VMs from VMware to KVM) UUID of a linked existing vCenter")
127128
private Long existingVcenterId;
128129

129130
@Parameter(name = ApiConstants.HOST_IP,
130131
type = BaseCmd.CommandType.STRING,
131-
description = "(only for importing migrated VMs from Vmware to KVM) VMware ESXi host IP/Name.")
132+
description = "(only for importing VMs from VMware to KVM) VMware ESXi host IP/Name.")
132133
private String hostip;
133134

134135
@Parameter(name = ApiConstants.VCENTER,
135136
type = CommandType.STRING,
136-
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.")
137+
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.")
137138
private String vcenter;
138139

139140
@Parameter(name = ApiConstants.DATACENTER_NAME, type = CommandType.STRING,
140-
description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware datacenter.")
141+
description = "(only for importing VMs from VMware to KVM) Name of VMware datacenter.")
141142
private String datacenterName;
142143

143144
@Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING,
144-
description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware cluster.")
145+
description = "(only for importing VMs from VMware to KVM) Name of VMware cluster.")
145146
private String clusterName;
146147

147148
@Parameter(name = ApiConstants.CONVERT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class,
148-
description = "(only for importing migrated VMs from Vmware to KVM) optional - the host to perform the virt-v2v migration from VMware to KVM.")
149+
description = "(only for importing VMs from VMware to KVM) optional - the host to perform the virt-v2v migration from VMware to KVM.")
149150
private Long convertInstanceHostId;
150151

151152
@Parameter(name = ApiConstants.CONVERT_INSTANCE_STORAGE_POOL_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class,
152-
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.")
153+
description = "(only for importing VMs from VMware to KVM) optional - the temporary storage pool to perform the virt-v2v migration from VMware to KVM.")
153154
private Long convertStoragePoolId;
154155

156+
@Parameter(name = ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES, type = CommandType.BOOLEAN,
157+
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.")
158+
private Boolean forceMsToImportVmFiles;
159+
155160
/////////////////////////////////////////////////////
156161
/////////////////// Accessors ///////////////////////
157162
/////////////////////////////////////////////////////
@@ -200,6 +205,10 @@ public Long getConvertStoragePoolId() {
200205
return convertStoragePoolId;
201206
}
202207

208+
public Boolean getForceMsToImportVmFiles() {
209+
return BooleanUtils.toBooleanDefaultIfNull(forceMsToImportVmFiles, false);
210+
}
211+
203212
public String getHypervisor() {
204213
return hypervisor;
205214
}

api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import com.cloud.host.Host;
3131
import com.cloud.host.Status;
32+
import com.cloud.hypervisor.Hypervisor;
3233
import com.cloud.serializer.Param;
3334
import com.google.gson.annotations.SerializedName;
3435

@@ -277,6 +278,10 @@ public class HostResponse extends BaseResponseWithAnnotations {
277278
@Param(description = "true if the host supports encryption", since = "4.18")
278279
private Boolean encryptionSupported;
279280

281+
@SerializedName(ApiConstants.INSTANCE_CONVERSION_SUPPORTED)
282+
@Param(description = "true if the host supports instance conversion (using virt-v2v)", since = "4.19.1")
283+
private Boolean instanceConversionSupported;
284+
280285
@Override
281286
public String getObjectId() {
282287
return this.getId();
@@ -526,7 +531,7 @@ public void setUsername(String username) {
526531
this.username = username;
527532
}
528533

529-
public void setDetails(Map details) {
534+
public void setDetails(Map details, Hypervisor.HypervisorType hypervisorType) {
530535

531536
if (details == null) {
532537
return;
@@ -547,6 +552,15 @@ public void setDetails(Map details) {
547552
this.setEncryptionSupported(new Boolean(false)); // default
548553
}
549554

555+
if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) {
556+
if (detailsCopy.containsKey(Host.HOST_INSTANCE_CONVERSION)) {
557+
this.setInstanceConversionSupported(Boolean.parseBoolean((String) detailsCopy.get(Host.HOST_INSTANCE_CONVERSION)));
558+
detailsCopy.remove(Host.HOST_INSTANCE_CONVERSION);
559+
} else {
560+
this.setInstanceConversionSupported(new Boolean(false)); // default
561+
}
562+
}
563+
550564
this.details = detailsCopy;
551565
}
552566

@@ -737,6 +751,10 @@ public void setEncryptionSupported(Boolean encryptionSupported) {
737751
this.encryptionSupported = encryptionSupported;
738752
}
739753

754+
public void setInstanceConversionSupported(Boolean instanceConversionSupported) {
755+
this.instanceConversionSupported = instanceConversionSupported;
756+
}
757+
740758
public Boolean getIsTagARule() {
741759
return isTagARule;
742760
}

api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.apache.cloudstack.vm;
1919

20+
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
21+
2022
import java.util.List;
2123

2224
public class UnmanagedInstanceTO {
@@ -317,6 +319,16 @@ public void setDatastorePort(int datastorePort) {
317319
public int getDatastorePort() {
318320
return datastorePort;
319321
}
322+
323+
@Override
324+
public String toString() {
325+
return "Disk {" +
326+
"diskId='" + diskId + '\'' +
327+
", capacity=" + toHumanReadableSize(capacity) +
328+
", controller='" + controller + '\'' +
329+
", controllerUnit=" + controllerUnit +
330+
"}";
331+
}
320332
}
321333

322334
public static class Nic {
@@ -409,5 +421,14 @@ public String getPciSlot() {
409421
public void setPciSlot(String pciSlot) {
410422
this.pciSlot = pciSlot;
411423
}
424+
425+
@Override
426+
public String toString() {
427+
return "Nic{" +
428+
"nicId='" + nicId + '\'' +
429+
", adapterType='" + adapterType + '\'' +
430+
", macAddress='" + macAddress + '\'' +
431+
"}";
432+
}
412433
}
413434
}

api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,37 @@ public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService,
3939
ConfigKey.Scope.Global,
4040
null);
4141

42+
ConfigKey<Integer> ConvertVmwareInstanceToKvmTimeout = new ConfigKey<>(Integer.class,
43+
"convert.vmware.instance.to.kvm.timeout",
44+
"Advanced",
45+
"3",
46+
"Timeout (in hours) for the instance conversion process from VMware through the virt-v2v binary on a KVM host",
47+
true,
48+
ConfigKey.Scope.Global,
49+
null);
50+
51+
ConfigKey<Integer> ThreadsOnMSToImportVMwareVMFiles = new ConfigKey<>(Integer.class,
52+
"threads.on.ms.to.import.vmware.vm.files",
53+
"Advanced",
54+
"0",
55+
"Threads to use on the management server when importing VM files from VMWare." +
56+
" -1 or 1 disables threads, 0 uses a thread per VM disk (disabled for single disk) and >1 for manual thread configuration." +
57+
" Max number is 10, Default is 0.",
58+
true,
59+
ConfigKey.Scope.Global,
60+
null);
61+
62+
ConfigKey<Integer> ThreadsOnKVMHostToImportVMwareVMFiles = new ConfigKey<>(Integer.class,
63+
"threads.on.kvm.host.to.import.vmware.vm.files",
64+
"Advanced",
65+
"0",
66+
"Threads to use on the KVM host (by the ovftool, if the version is 4.4.0+) when importing VM files from VMWare." +
67+
" -1 or 1 disables threads, 0 uses a thread per VM disk (disabled for single disk) and >1 for manual thread configuration." +
68+
" Max number is 10, Default is 0.",
69+
true,
70+
ConfigKey.Scope.Global,
71+
null);
72+
4273
static boolean isSupported(Hypervisor.HypervisorType hypervisorType) {
4374
return hypervisorType == VMware || hypervisorType == KVM;
4475
}

0 commit comments

Comments
 (0)