Skip to content

Commit 371ad9f

Browse files
authored
New Feature: Import VMware VMs into KVM (apache#7881)
This PR adds the capability in CloudStack to convert VMware Instances disk(s) to KVM using virt-v2v and import them as CloudStack instances. It enables CloudStack operators to import VMware instances from vSphere into a KVM cluster managed by CloudStack. vSphere/VMware setup might be managed by CloudStack or be a standalone setup. CloudStack will let the administrator select a VM from an existing VMware vCenter in the CloudStack environment or external vCenter requesting vCenter IP, Datacenter name and credentials. The migrated VM will be imported as a KVM instance The migration is done through virt-v2v: https://access.redhat.com/articles/1351473, https://www.ovirt.org/develop/release-management/features/virt/virt-v2v-integration.html The migration process timeout can be set by the setting convert.instance.process.timeout Before attempting the virt-v2v migration, CloudStack will create a clone of the source VM on VMware. The clone VM will be removed after the registration process finishes. CloudStack will delegate the migration action to a KVM host and the host will attempt to migrate the VM invoking virt-v2v. In case the guest OS is not supported then CloudStack will handle the error operation as a failure The migration process using virt-v2v may not be a fast process CloudStack will not perform any check about the guest OS compatibility for the virt-v2v library as indicated on: https://access.redhat.com/articles/1351473.
1 parent fdfbb4f commit 371ad9f

File tree

67 files changed

+3906
-655
lines changed

Some content is hidden

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

67 files changed

+3906
-655
lines changed

agent/conf/agent.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,3 +419,6 @@ iscsi.session.cleanup.enabled=false
419419

420420
# Timeout (in milliseconds) of the KVM heartbeat checker.
421421
# kvm.heartbeat.checker.timeout=360000
422+
423+
# Instance Conversion from Vmware to KVM through virt-v2v. Enable verbose mode
424+
# virtv2v.verbose.enabled=false

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,13 @@ public Property<Integer> getWorkers() {
733733
*/
734734
public static final Property<Integer> IOTHREADS = new Property<>("iothreads", 1);
735735

736+
/**
737+
* Enable verbose mode for virt-v2v Instance Conversion from Vmware to KVM
738+
* Data type: Boolean.<br>
739+
* Default value: <code>false</code>
740+
*/
741+
public static final Property<Boolean> VIRTV2V_VERBOSE_ENABLED = new Property<>("virtv2v.verbose.enabled", false);
742+
736743
/**
737744
* BGP controll CIDR
738745
* Data type: String.<br>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package com.cloud.agent.api.to;
20+
21+
import com.cloud.agent.api.LogLevel;
22+
import com.cloud.hypervisor.Hypervisor;
23+
24+
import java.io.Serializable;
25+
26+
public class RemoteInstanceTO implements Serializable {
27+
28+
private Hypervisor.HypervisorType hypervisorType;
29+
private String hostName;
30+
private String instanceName;
31+
32+
// Vmware Remote Instances parameters
33+
// TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes
34+
private String vcenterUsername;
35+
@LogLevel(LogLevel.Log4jLevel.Off)
36+
private String vcenterPassword;
37+
private String vcenterHost;
38+
private String datacenterName;
39+
private String clusterName;
40+
41+
public RemoteInstanceTO() {
42+
}
43+
44+
public RemoteInstanceTO(String hostName, String instanceName, String vcenterHost,
45+
String datacenterName, String clusterName,
46+
String vcenterUsername, String vcenterPassword) {
47+
this.hypervisorType = Hypervisor.HypervisorType.VMware;
48+
this.hostName = hostName;
49+
this.instanceName = instanceName;
50+
this.vcenterHost = vcenterHost;
51+
this.datacenterName = datacenterName;
52+
this.clusterName = clusterName;
53+
this.vcenterUsername = vcenterUsername;
54+
this.vcenterPassword = vcenterPassword;
55+
}
56+
57+
public Hypervisor.HypervisorType getHypervisorType() {
58+
return this.hypervisorType;
59+
}
60+
61+
public String getInstanceName() {
62+
return this.instanceName;
63+
}
64+
65+
public String getHostName() {
66+
return this.hostName;
67+
}
68+
69+
public String getVcenterUsername() {
70+
return vcenterUsername;
71+
}
72+
73+
public String getVcenterPassword() {
74+
return vcenterPassword;
75+
}
76+
77+
public String getVcenterHost() {
78+
return vcenterHost;
79+
}
80+
81+
public String getDatacenterName() {
82+
return datacenterName;
83+
}
84+
85+
public String getClusterName() {
86+
return clusterName;
87+
}
88+
}

plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenter.java renamed to api/src/main/java/com/cloud/dc/VmwareDatacenter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
package com.cloud.hypervisor.vmware;
18+
package com.cloud.dc;
1919

2020
import org.apache.cloudstack.api.Identity;
2121
import org.apache.cloudstack.api.InternalIdentity;

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.cloud.vm.NicProfile;
3434
import com.cloud.vm.VirtualMachine;
3535
import com.cloud.vm.VirtualMachineProfile;
36+
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
3637

3738
public interface HypervisorGuru extends Adapter {
3839

@@ -104,4 +105,25 @@ boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backu
104105
* @return a list of commands to perform for a successful migration
105106
*/
106107
List<Command> finalizeMigrate(VirtualMachine vm, Map<Volume, StoragePool> volumeToPool);
108+
109+
110+
/**
111+
* Will perform a clone of a VM on an external host (if the guru can handle)
112+
* @param hostIp VM's source host IP
113+
* @param vmName name of the source VM to clone from
114+
* @param params hypervisor specific additional parameters
115+
* @return a reference to the cloned VM
116+
*/
117+
UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName,
118+
Map<String, String> params);
119+
120+
/**
121+
* Removes a VM created as a clone of a VM on an external host
122+
* @param hostIp VM's source host IP
123+
* @param vmName name of the VM to remove
124+
* @param params hypervisor specific additional parameters
125+
* @return true if the operation succeeds, false if not
126+
*/
127+
boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName,
128+
Map<String, String> params);
107129
}

api/src/main/java/com/cloud/vm/VmDetailConstants.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,16 @@ public interface VmDetailConstants {
8686
String DEPLOY_AS_IS_CONFIGURATION = "configurationId";
8787
String KEY_PAIR_NAMES = "keypairnames";
8888
String CKS_CONTROL_NODE_LOGIN_USER = "controlNodeLoginUser";
89+
90+
// VMware to KVM VM migrations specific
91+
String VMWARE_TO_KVM_PREFIX = "vmware-to-kvm";
92+
String VMWARE_VCENTER_HOST = String.format("%s-vcenter", VMWARE_TO_KVM_PREFIX);
93+
String VMWARE_DATACENTER_NAME = String.format("%s-datacenter", VMWARE_TO_KVM_PREFIX);
94+
String VMWARE_CLUSTER_NAME = String.format("%s-cluster", VMWARE_TO_KVM_PREFIX);
95+
String VMWARE_VCENTER_USERNAME = String.format("%s-username", VMWARE_TO_KVM_PREFIX);
96+
String VMWARE_VCENTER_PASSWORD = String.format("%s-password", VMWARE_TO_KVM_PREFIX);
97+
String VMWARE_VM_NAME = String.format("%s-vmname", VMWARE_TO_KVM_PREFIX);
98+
String VMWARE_HOST_NAME = String.format("%s-host", VMWARE_TO_KVM_PREFIX);
99+
String VMWARE_DISK = String.format("%s-disk", VMWARE_TO_KVM_PREFIX);
100+
String VMWARE_MAC_ADDRESSES = String.format("%s-mac-addresses", VMWARE_TO_KVM_PREFIX);
89101
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ public class ApiConstants {
6969
public static final String CERTIFICATE_SERIALNUM = "serialnum";
7070
public static final String CERTIFICATE_SUBJECT = "subject";
7171
public static final String CERTIFICATE_VALIDITY = "validity";
72+
public static final String CONVERT_INSTANCE_HOST_ID = "convertinstancehostid";
73+
public static final String CONVERT_INSTANCE_STORAGE_POOL_ID = "convertinstancepoolid";
7274
public static final String ENABLED_REVOCATION_CHECK = "enabledrevocationcheck";
7375
public static final String CONTROLLER = "controller";
7476
public static final String CONTROLLER_UNIT = "controllerunit";
@@ -120,6 +122,7 @@ public class ApiConstants {
120122
public static final String MIN_IOPS = "miniops";
121123
public static final String MAX_IOPS = "maxiops";
122124
public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve";
125+
public static final String DATACENTER_NAME = "datacentername";
123126
public static final String DATADISK_OFFERING_LIST = "datadiskofferinglist";
124127
public static final String DEFAULT_VALUE = "defaultvalue";
125128
public static final String DESCRIPTION = "description";
@@ -207,6 +210,7 @@ public class ApiConstants {
207210
public static final String HIDE_IP_ADDRESS_USAGE = "hideipaddressusage";
208211
public static final String HOST_ID = "hostid";
209212
public static final String HOST_IDS = "hostids";
213+
public static final String HOST_IP = "hostip";
210214
public static final String HOST_NAME = "hostname";
211215
public static final String HOST_CONTROL_STATE = "hostcontrolstate";
212216
public static final String HOSTS_MAP = "hostsmap";
@@ -779,6 +783,7 @@ public class ApiConstants {
779783
public static final String VSM_CONFIG_STATE = "vsmconfigstate";
780784
public static final String VSM_DEVICE_STATE = "vsmdevicestate";
781785
public static final String VCENTER = "vcenter";
786+
public static final String EXISTING_VCENTER_ID = "existingvcenterid";
782787
public static final String ADD_VSM_FLAG = "addvsmflag";
783788
public static final String END_POINT = "endpoint";
784789
public static final String REGION_ID = "regionid";
@@ -1059,6 +1064,7 @@ public class ApiConstants {
10591064
public static final String SOURCE_NAT_IP = "sourcenatipaddress";
10601065
public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid";
10611066
public static final String HAS_RULES = "hasrules";
1067+
public static final String IMPORT_SOURCE = "importsource";
10621068
public static final String OBJECT_STORAGE = "objectstore";
10631069

10641070
public static final String HEURISTIC_RULE = "heuristicrule";

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
import org.apache.cloudstack.api.response.TemplateResponse;
124124
import org.apache.cloudstack.api.response.TrafficMonitorResponse;
125125
import org.apache.cloudstack.api.response.TrafficTypeResponse;
126+
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
126127
import org.apache.cloudstack.api.response.UpgradeRouterTemplateResponse;
127128
import org.apache.cloudstack.api.response.UsageRecordResponse;
128129
import org.apache.cloudstack.api.response.UserDataResponse;
@@ -237,6 +238,7 @@
237238
import com.cloud.vm.NicSecondaryIp;
238239
import com.cloud.vm.VirtualMachine;
239240
import com.cloud.vm.snapshot.VMSnapshot;
241+
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
240242

241243
public interface ResponseGenerator {
242244
UserResponse createUserResponse(UserAccount user);
@@ -538,6 +540,8 @@ List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachine
538540

539541
FirewallResponse createIpv6FirewallRuleResponse(FirewallRule acl);
540542

543+
UnmanagedInstanceResponse createUnmanagedInstanceResponse(UnmanagedInstanceTO instance, Cluster cluster, Host host);
544+
541545
SecondaryStorageHeuristicsResponse createSecondaryStorageSelectorResponse(Heuristic heuristic);
542546

543547
IpQuarantineResponse createQuarantinedIpsResponse(PublicIpQuarantine publicIp);

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
124124
type = CommandType.UUID,
125125
entityType = ServiceOfferingResponse.class,
126126
required = true,
127-
description = "the ID of the service offering for the virtual machine")
127+
description = "the service offering for the virtual machine")
128128
private Long serviceOfferingId;
129129

130130
@Parameter(name = ApiConstants.NIC_NETWORK_LIST,
@@ -154,7 +154,7 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
154154

155155
@Parameter(name = ApiConstants.FORCED,
156156
type = CommandType.BOOLEAN,
157-
description = "VM is imported despite some of its NIC's MAC addresses are already present")
157+
description = "VM is imported despite some of its NIC's MAC addresses are already present, in case the MAC address exists then a new MAC address is generated")
158158
private Boolean forced;
159159

160160
/////////////////////////////////////////////////////
@@ -279,7 +279,8 @@ public String getEventType() {
279279

280280
@Override
281281
public String getEventDescription() {
282-
return "Importing unmanaged VM";
282+
String vmName = this.name;
283+
return String.format("Importing unmanaged VM: %s", vmName);
283284
}
284285

285286
public boolean isForced() {

0 commit comments

Comments
 (0)