mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
New Feature: Import VMware VMs into KVM (#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.
This commit is contained in:
parent
fdfbb4fad1
commit
371ad9f55b
@ -419,3 +419,6 @@ iscsi.session.cleanup.enabled=false
|
||||
|
||||
# Timeout (in milliseconds) of the KVM heartbeat checker.
|
||||
# kvm.heartbeat.checker.timeout=360000
|
||||
|
||||
# Instance Conversion from Vmware to KVM through virt-v2v. Enable verbose mode
|
||||
# virtv2v.verbose.enabled=false
|
||||
|
||||
@ -733,6 +733,13 @@ public class AgentProperties{
|
||||
*/
|
||||
public static final Property<Integer> IOTHREADS = new Property<>("iothreads", 1);
|
||||
|
||||
/**
|
||||
* Enable verbose mode for virt-v2v Instance Conversion from Vmware to KVM
|
||||
* Data type: Boolean.<br>
|
||||
* Default value: <code>false</code>
|
||||
*/
|
||||
public static final Property<Boolean> VIRTV2V_VERBOSE_ENABLED = new Property<>("virtv2v.verbose.enabled", false);
|
||||
|
||||
/**
|
||||
* BGP controll CIDR
|
||||
* Data type: String.<br>
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.to;
|
||||
|
||||
import com.cloud.agent.api.LogLevel;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class RemoteInstanceTO implements Serializable {
|
||||
|
||||
private Hypervisor.HypervisorType hypervisorType;
|
||||
private String hostName;
|
||||
private String instanceName;
|
||||
|
||||
// Vmware Remote Instances parameters
|
||||
// TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes
|
||||
private String vcenterUsername;
|
||||
@LogLevel(LogLevel.Log4jLevel.Off)
|
||||
private String vcenterPassword;
|
||||
private String vcenterHost;
|
||||
private String datacenterName;
|
||||
private String clusterName;
|
||||
|
||||
public RemoteInstanceTO() {
|
||||
}
|
||||
|
||||
public RemoteInstanceTO(String hostName, String instanceName, String vcenterHost,
|
||||
String datacenterName, String clusterName,
|
||||
String vcenterUsername, String vcenterPassword) {
|
||||
this.hypervisorType = Hypervisor.HypervisorType.VMware;
|
||||
this.hostName = hostName;
|
||||
this.instanceName = instanceName;
|
||||
this.vcenterHost = vcenterHost;
|
||||
this.datacenterName = datacenterName;
|
||||
this.clusterName = clusterName;
|
||||
this.vcenterUsername = vcenterUsername;
|
||||
this.vcenterPassword = vcenterPassword;
|
||||
}
|
||||
|
||||
public Hypervisor.HypervisorType getHypervisorType() {
|
||||
return this.hypervisorType;
|
||||
}
|
||||
|
||||
public String getInstanceName() {
|
||||
return this.instanceName;
|
||||
}
|
||||
|
||||
public String getHostName() {
|
||||
return this.hostName;
|
||||
}
|
||||
|
||||
public String getVcenterUsername() {
|
||||
return vcenterUsername;
|
||||
}
|
||||
|
||||
public String getVcenterPassword() {
|
||||
return vcenterPassword;
|
||||
}
|
||||
|
||||
public String getVcenterHost() {
|
||||
return vcenterHost;
|
||||
}
|
||||
|
||||
public String getDatacenterName() {
|
||||
return datacenterName;
|
||||
}
|
||||
|
||||
public String getClusterName() {
|
||||
return clusterName;
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,7 @@
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com.cloud.hypervisor.vmware;
|
||||
package com.cloud.dc;
|
||||
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
@ -33,6 +33,7 @@ import com.cloud.utils.component.Adapter;
|
||||
import com.cloud.vm.NicProfile;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachineProfile;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
|
||||
public interface HypervisorGuru extends Adapter {
|
||||
|
||||
@ -104,4 +105,25 @@ public interface HypervisorGuru extends Adapter {
|
||||
* @return a list of commands to perform for a successful migration
|
||||
*/
|
||||
List<Command> finalizeMigrate(VirtualMachine vm, Map<Volume, StoragePool> volumeToPool);
|
||||
|
||||
|
||||
/**
|
||||
* Will perform a clone of a VM on an external host (if the guru can handle)
|
||||
* @param hostIp VM's source host IP
|
||||
* @param vmName name of the source VM to clone from
|
||||
* @param params hypervisor specific additional parameters
|
||||
* @return a reference to the cloned VM
|
||||
*/
|
||||
UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName,
|
||||
Map<String, String> params);
|
||||
|
||||
/**
|
||||
* Removes a VM created as a clone of a VM on an external host
|
||||
* @param hostIp VM's source host IP
|
||||
* @param vmName name of the VM to remove
|
||||
* @param params hypervisor specific additional parameters
|
||||
* @return true if the operation succeeds, false if not
|
||||
*/
|
||||
boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName,
|
||||
Map<String, String> params);
|
||||
}
|
||||
|
||||
@ -86,4 +86,16 @@ public interface VmDetailConstants {
|
||||
String DEPLOY_AS_IS_CONFIGURATION = "configurationId";
|
||||
String KEY_PAIR_NAMES = "keypairnames";
|
||||
String CKS_CONTROL_NODE_LOGIN_USER = "controlNodeLoginUser";
|
||||
|
||||
// VMware to KVM VM migrations specific
|
||||
String VMWARE_TO_KVM_PREFIX = "vmware-to-kvm";
|
||||
String VMWARE_VCENTER_HOST = String.format("%s-vcenter", VMWARE_TO_KVM_PREFIX);
|
||||
String VMWARE_DATACENTER_NAME = String.format("%s-datacenter", VMWARE_TO_KVM_PREFIX);
|
||||
String VMWARE_CLUSTER_NAME = String.format("%s-cluster", VMWARE_TO_KVM_PREFIX);
|
||||
String VMWARE_VCENTER_USERNAME = String.format("%s-username", VMWARE_TO_KVM_PREFIX);
|
||||
String VMWARE_VCENTER_PASSWORD = String.format("%s-password", VMWARE_TO_KVM_PREFIX);
|
||||
String VMWARE_VM_NAME = String.format("%s-vmname", VMWARE_TO_KVM_PREFIX);
|
||||
String VMWARE_HOST_NAME = String.format("%s-host", VMWARE_TO_KVM_PREFIX);
|
||||
String VMWARE_DISK = String.format("%s-disk", VMWARE_TO_KVM_PREFIX);
|
||||
String VMWARE_MAC_ADDRESSES = String.format("%s-mac-addresses", VMWARE_TO_KVM_PREFIX);
|
||||
}
|
||||
|
||||
@ -69,6 +69,8 @@ public class ApiConstants {
|
||||
public static final String CERTIFICATE_SERIALNUM = "serialnum";
|
||||
public static final String CERTIFICATE_SUBJECT = "subject";
|
||||
public static final String CERTIFICATE_VALIDITY = "validity";
|
||||
public static final String CONVERT_INSTANCE_HOST_ID = "convertinstancehostid";
|
||||
public static final String CONVERT_INSTANCE_STORAGE_POOL_ID = "convertinstancepoolid";
|
||||
public static final String ENABLED_REVOCATION_CHECK = "enabledrevocationcheck";
|
||||
public static final String CONTROLLER = "controller";
|
||||
public static final String CONTROLLER_UNIT = "controllerunit";
|
||||
@ -120,6 +122,7 @@ public class ApiConstants {
|
||||
public static final String MIN_IOPS = "miniops";
|
||||
public static final String MAX_IOPS = "maxiops";
|
||||
public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve";
|
||||
public static final String DATACENTER_NAME = "datacentername";
|
||||
public static final String DATADISK_OFFERING_LIST = "datadiskofferinglist";
|
||||
public static final String DEFAULT_VALUE = "defaultvalue";
|
||||
public static final String DESCRIPTION = "description";
|
||||
@ -207,6 +210,7 @@ public class ApiConstants {
|
||||
public static final String HIDE_IP_ADDRESS_USAGE = "hideipaddressusage";
|
||||
public static final String HOST_ID = "hostid";
|
||||
public static final String HOST_IDS = "hostids";
|
||||
public static final String HOST_IP = "hostip";
|
||||
public static final String HOST_NAME = "hostname";
|
||||
public static final String HOST_CONTROL_STATE = "hostcontrolstate";
|
||||
public static final String HOSTS_MAP = "hostsmap";
|
||||
@ -779,6 +783,7 @@ public class ApiConstants {
|
||||
public static final String VSM_CONFIG_STATE = "vsmconfigstate";
|
||||
public static final String VSM_DEVICE_STATE = "vsmdevicestate";
|
||||
public static final String VCENTER = "vcenter";
|
||||
public static final String EXISTING_VCENTER_ID = "existingvcenterid";
|
||||
public static final String ADD_VSM_FLAG = "addvsmflag";
|
||||
public static final String END_POINT = "endpoint";
|
||||
public static final String REGION_ID = "regionid";
|
||||
@ -1059,6 +1064,7 @@ public class ApiConstants {
|
||||
public static final String SOURCE_NAT_IP = "sourcenatipaddress";
|
||||
public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid";
|
||||
public static final String HAS_RULES = "hasrules";
|
||||
public static final String IMPORT_SOURCE = "importsource";
|
||||
public static final String OBJECT_STORAGE = "objectstore";
|
||||
|
||||
public static final String HEURISTIC_RULE = "heuristicrule";
|
||||
|
||||
@ -123,6 +123,7 @@ import org.apache.cloudstack.api.response.TemplatePermissionsResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.TrafficMonitorResponse;
|
||||
import org.apache.cloudstack.api.response.TrafficTypeResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.UpgradeRouterTemplateResponse;
|
||||
import org.apache.cloudstack.api.response.UsageRecordResponse;
|
||||
import org.apache.cloudstack.api.response.UserDataResponse;
|
||||
@ -237,6 +238,7 @@ import com.cloud.vm.Nic;
|
||||
import com.cloud.vm.NicSecondaryIp;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.snapshot.VMSnapshot;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
|
||||
public interface ResponseGenerator {
|
||||
UserResponse createUserResponse(UserAccount user);
|
||||
@ -538,6 +540,8 @@ public interface ResponseGenerator {
|
||||
|
||||
FirewallResponse createIpv6FirewallRuleResponse(FirewallRule acl);
|
||||
|
||||
UnmanagedInstanceResponse createUnmanagedInstanceResponse(UnmanagedInstanceTO instance, Cluster cluster, Host host);
|
||||
|
||||
SecondaryStorageHeuristicsResponse createSecondaryStorageSelectorResponse(Heuristic heuristic);
|
||||
|
||||
IpQuarantineResponse createQuarantinedIpsResponse(PublicIpQuarantine publicIp);
|
||||
|
||||
@ -124,7 +124,7 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
|
||||
type = CommandType.UUID,
|
||||
entityType = ServiceOfferingResponse.class,
|
||||
required = true,
|
||||
description = "the ID of the service offering for the virtual machine")
|
||||
description = "the service offering for the virtual machine")
|
||||
private Long serviceOfferingId;
|
||||
|
||||
@Parameter(name = ApiConstants.NIC_NETWORK_LIST,
|
||||
@ -154,7 +154,7 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
|
||||
|
||||
@Parameter(name = ApiConstants.FORCED,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "VM is imported despite some of its NIC's MAC addresses are already present")
|
||||
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")
|
||||
private Boolean forced;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
@ -279,7 +279,8 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd {
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Importing unmanaged VM";
|
||||
String vmName = this.name;
|
||||
return String.format("Importing unmanaged VM: %s", vmName);
|
||||
}
|
||||
|
||||
public boolean isForced() {
|
||||
|
||||
@ -0,0 +1,180 @@
|
||||
// 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 org.apache.cloudstack.api.command.admin.vm;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.NetworkRuleConflictException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.HostResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VmwareDatacenterResponse;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@APICommand(name = "importVm",
|
||||
description = "Import virtual machine from a unmanaged host into CloudStack",
|
||||
responseObject = UserVmResponse.class,
|
||||
responseView = ResponseObject.ResponseView.Full,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = true,
|
||||
authorized = {RoleType.Admin},
|
||||
since = "4.19.0")
|
||||
public class ImportVmCmd extends ImportUnmanagedInstanceCmd {
|
||||
|
||||
public static final Logger LOGGER = Logger.getLogger(ImportVmCmd.class);
|
||||
|
||||
@Parameter(name = ApiConstants.HYPERVISOR,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "hypervisor type of the host")
|
||||
private String hypervisor;
|
||||
|
||||
@Parameter(name = ApiConstants.IMPORT_SOURCE,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "Source location for Import" )
|
||||
private String importSource;
|
||||
|
||||
// Import from Vmware to KVM migration parameters
|
||||
|
||||
@Parameter(name = ApiConstants.EXISTING_VCENTER_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = VmwareDatacenterResponse.class,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) UUID of a linked existing vCenter")
|
||||
private Long existingVcenterId;
|
||||
|
||||
@Parameter(name = ApiConstants.HOST_IP,
|
||||
type = BaseCmd.CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) VMware ESXi host IP/Name.")
|
||||
private String host;
|
||||
|
||||
@Parameter(name = ApiConstants.VCENTER,
|
||||
type = CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.")
|
||||
private String vcenter;
|
||||
|
||||
@Parameter(name = ApiConstants.DATACENTER_NAME, type = CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware datacenter.")
|
||||
private String datacenterName;
|
||||
|
||||
@Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware cluster.")
|
||||
private String clusterName;
|
||||
|
||||
@Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) The Username required to connect to resource.")
|
||||
private String username;
|
||||
|
||||
@Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) The password for the specified username.")
|
||||
private String password;
|
||||
|
||||
@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.")
|
||||
private Long convertInstanceHostId;
|
||||
|
||||
@Parameter(name = ApiConstants.CONVERT_INSTANCE_STORAGE_POOL_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class,
|
||||
description = "(only for importing migrated VMs from Vmware to KVM) optional - the temporary storage pool to perform the virt-v2v migration from VMware to KVM.")
|
||||
private Long convertStoragePoolId;
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_VM_IMPORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
String vmName = getName();
|
||||
if (ObjectUtils.anyNotNull(vcenter, existingVcenterId)) {
|
||||
String msg = StringUtils.isNotBlank(vcenter) ?
|
||||
String.format("external vCenter: %s - datacenter: %s", vcenter, datacenterName) :
|
||||
String.format("existing vCenter Datacenter with ID: %s", existingVcenterId);
|
||||
return String.format("Importing unmanaged VM: %s from %s - VM: %s", getDisplayName(), msg, vmName);
|
||||
}
|
||||
return String.format("Importing unmanaged VM: %s", vmName);
|
||||
}
|
||||
|
||||
|
||||
public Long getExistingVcenterId() {
|
||||
return existingVcenterId;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public String getVcenter() {
|
||||
return vcenter;
|
||||
}
|
||||
|
||||
public String getDatacenterName() {
|
||||
return datacenterName;
|
||||
}
|
||||
|
||||
public String getClusterName() {
|
||||
return clusterName;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public Long getConvertInstanceHostId() {
|
||||
return convertInstanceHostId;
|
||||
}
|
||||
|
||||
public Long getConvertStoragePoolId() {
|
||||
return convertStoragePoolId;
|
||||
}
|
||||
|
||||
public String getHypervisor() {
|
||||
return hypervisor;
|
||||
}
|
||||
|
||||
public String getImportSource() {
|
||||
return importSource;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
UserVmResponse response = vmImportService.importVm(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
}
|
||||
@ -39,6 +39,10 @@ public class UnmanagedInstanceResponse extends BaseResponse {
|
||||
@Param(description = "the ID of the cluster to which virtual machine belongs")
|
||||
private String clusterId;
|
||||
|
||||
@SerializedName(ApiConstants.CLUSTER_NAME)
|
||||
@Param(description = "the name of the cluster to which virtual machine belongs")
|
||||
private String clusterName;
|
||||
|
||||
@SerializedName(ApiConstants.HOST_ID)
|
||||
@Param(description = "the ID of the host to which virtual machine belongs")
|
||||
private String hostId;
|
||||
@ -104,6 +108,14 @@ public class UnmanagedInstanceResponse extends BaseResponse {
|
||||
this.clusterId = clusterId;
|
||||
}
|
||||
|
||||
public String getClusterName() {
|
||||
return clusterName;
|
||||
}
|
||||
|
||||
public void setClusterName(String clusterName) {
|
||||
this.clusterName = clusterName;
|
||||
}
|
||||
|
||||
public String getHostId() {
|
||||
return hostId;
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.EntityReference;
|
||||
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenter;
|
||||
import com.cloud.dc.VmwareDatacenter;
|
||||
import com.cloud.serializer.Param;
|
||||
|
||||
@EntityReference(value = VmwareDatacenter.class)
|
||||
@ -33,6 +33,8 @@ public class UnmanagedInstanceTO {
|
||||
|
||||
private PowerState powerState;
|
||||
|
||||
private PowerState cloneSourcePowerState;
|
||||
|
||||
private Integer cpuCores;
|
||||
|
||||
private Integer cpuCoresPerSocket;
|
||||
@ -45,6 +47,10 @@ public class UnmanagedInstanceTO {
|
||||
|
||||
private String operatingSystem;
|
||||
|
||||
private String clusterName;
|
||||
|
||||
private String hostName;
|
||||
|
||||
private List<Disk> disks;
|
||||
|
||||
private List<Nic> nics;
|
||||
@ -73,6 +79,14 @@ public class UnmanagedInstanceTO {
|
||||
this.powerState = powerState;
|
||||
}
|
||||
|
||||
public PowerState getCloneSourcePowerState() {
|
||||
return cloneSourcePowerState;
|
||||
}
|
||||
|
||||
public void setCloneSourcePowerState(PowerState cloneSourcePowerState) {
|
||||
this.cloneSourcePowerState = cloneSourcePowerState;
|
||||
}
|
||||
|
||||
public Integer getCpuCores() {
|
||||
return cpuCores;
|
||||
}
|
||||
@ -121,6 +135,22 @@ public class UnmanagedInstanceTO {
|
||||
this.operatingSystem = operatingSystem;
|
||||
}
|
||||
|
||||
public String getClusterName() {
|
||||
return clusterName;
|
||||
}
|
||||
|
||||
public void setClusterName(String clusterName) {
|
||||
this.clusterName = clusterName;
|
||||
}
|
||||
|
||||
public String getHostName() {
|
||||
return hostName;
|
||||
}
|
||||
|
||||
public void setHostName(String hostName) {
|
||||
this.hostName = hostName;
|
||||
}
|
||||
|
||||
public List<Disk> getDisks() {
|
||||
return disks;
|
||||
}
|
||||
|
||||
@ -18,12 +18,24 @@
|
||||
package org.apache.cloudstack.vm;
|
||||
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportVmCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
|
||||
public interface VmImportService {
|
||||
|
||||
enum ImportSource {
|
||||
UNMANAGED, VMWARE, EXTERNAL, SHARED, LOCAL;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
ListResponse<UnmanagedInstanceResponse> listUnmanagedInstances(ListUnmanagedInstancesCmd cmd);
|
||||
UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd);
|
||||
UserVmResponse importVm(ImportVmCmd cmd);
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
// 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;
|
||||
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
|
||||
public class ConvertInstanceAnswer extends Answer {
|
||||
|
||||
public ConvertInstanceAnswer() {
|
||||
super();
|
||||
}
|
||||
private UnmanagedInstanceTO convertedInstance;
|
||||
|
||||
public ConvertInstanceAnswer(Command command, boolean success, String details) {
|
||||
super(command, success, details);
|
||||
}
|
||||
|
||||
public ConvertInstanceAnswer(Command command, UnmanagedInstanceTO convertedInstance) {
|
||||
super(command, true, "");
|
||||
this.convertedInstance = convertedInstance;
|
||||
}
|
||||
|
||||
public UnmanagedInstanceTO getConvertedInstance() {
|
||||
return convertedInstance;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
// 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;
|
||||
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.agent.api.to.RemoteInstanceTO;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ConvertInstanceCommand extends Command {
|
||||
|
||||
private RemoteInstanceTO sourceInstance;
|
||||
private Hypervisor.HypervisorType destinationHypervisorType;
|
||||
private List<String> destinationStoragePools;
|
||||
private DataStoreTO conversionTemporaryLocation;
|
||||
|
||||
public ConvertInstanceCommand() {
|
||||
}
|
||||
|
||||
public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType,
|
||||
List<String> destinationStoragePools, DataStoreTO conversionTemporaryLocation) {
|
||||
this.sourceInstance = sourceInstance;
|
||||
this.destinationHypervisorType = destinationHypervisorType;
|
||||
this.destinationStoragePools = destinationStoragePools;
|
||||
this.conversionTemporaryLocation = conversionTemporaryLocation;
|
||||
}
|
||||
|
||||
public RemoteInstanceTO getSourceInstance() {
|
||||
return sourceInstance;
|
||||
}
|
||||
|
||||
public Hypervisor.HypervisorType getDestinationHypervisorType() {
|
||||
return destinationHypervisorType;
|
||||
}
|
||||
|
||||
public List<String> getDestinationStoragePools() {
|
||||
return destinationStoragePools;
|
||||
}
|
||||
|
||||
public DataStoreTO getConversionTemporaryLocation() {
|
||||
return conversionTemporaryLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
2
debian/control
vendored
2
debian/control
vendored
@ -24,7 +24,7 @@ Description: CloudStack server library
|
||||
|
||||
Package: cloudstack-agent
|
||||
Architecture: all
|
||||
Depends: ${python:Depends}, ${python3:Depends}, openjdk-11-jre-headless | java11-runtime-headless | java11-runtime | openjdk-11-jre-headless | zulu-11, cloudstack-common (= ${source:Version}), lsb-base (>= 9), openssh-client, qemu-kvm (>= 2.5) | qemu-system-x86 (>= 5.2), libvirt-bin (>= 1.3) | libvirt-daemon-system (>= 3.0), iproute2, ebtables, vlan, ipset, python3-libvirt, ethtool, iptables, cryptsetup, rng-tools, lsb-release, aria2, ufw, apparmor
|
||||
Depends: ${python:Depends}, ${python3:Depends}, openjdk-11-jre-headless | java11-runtime-headless | java11-runtime | openjdk-11-jre-headless | zulu-11, cloudstack-common (= ${source:Version}), lsb-base (>= 9), openssh-client, qemu-kvm (>= 2.5) | qemu-system-x86 (>= 5.2), libvirt-bin (>= 1.3) | libvirt-daemon-system (>= 3.0), iproute2, ebtables, vlan, ipset, python3-libvirt, ethtool, iptables, cryptsetup, rng-tools, lsb-release, ufw, apparmor
|
||||
Recommends: init-system-helpers
|
||||
Conflicts: cloud-agent, cloud-agent-libs, cloud-agent-deps, cloud-agent-scripts
|
||||
Description: CloudStack agent
|
||||
|
||||
@ -96,6 +96,14 @@ public interface StorageManager extends StorageService {
|
||||
true,
|
||||
ConfigKey.Scope.Global,
|
||||
null);
|
||||
ConfigKey<Integer> ConvertVmwareInstanceToKvmTimeout = new ConfigKey<>(Integer.class,
|
||||
"convert.vmware.instance.to.kvm.timeout",
|
||||
"Storage",
|
||||
"8",
|
||||
"Timeout (in hours) for the instance conversion process from VMware through the virt-v2v binary on a KVM host",
|
||||
true,
|
||||
ConfigKey.Scope.Global,
|
||||
null);
|
||||
ConfigKey<Boolean> KvmAutoConvergence = new ConfigKey<>(Boolean.class,
|
||||
"kvm.auto.convergence",
|
||||
"Storage",
|
||||
|
||||
@ -21,7 +21,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -4588,18 +4587,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
@Override
|
||||
public NicVO doInTransaction(TransactionStatus status) {
|
||||
NicVO existingNic = _nicDao.findByNetworkIdAndMacAddress(network.getId(), macAddress);
|
||||
String macAddressToPersist = macAddress;
|
||||
if (existingNic != null) {
|
||||
if (!forced) {
|
||||
throw new CloudRuntimeException("NIC with MAC address = " + macAddress + " exists on network with ID = " + network.getId() +
|
||||
" and forced flag is disabled");
|
||||
}
|
||||
s_logger.debug("Removing existing NIC with MAC address = " + macAddress + " on network with ID = " + network.getId());
|
||||
existingNic.setState(Nic.State.Deallocating);
|
||||
existingNic.setRemoved(new Date());
|
||||
_nicDao.update(existingNic.getId(), existingNic);
|
||||
macAddressToPersist = generateNewMacAddressIfForced(network, macAddress, forced);
|
||||
}
|
||||
NicVO vo = new NicVO(network.getGuruName(), vm.getId(), network.getId(), vm.getType());
|
||||
vo.setMacAddress(macAddress);
|
||||
vo.setMacAddress(macAddressToPersist);
|
||||
vo.setAddressFormat(Networks.AddressFormat.Ip4);
|
||||
if (NetUtils.isValidIp4(finalGuestIp) && StringUtils.isNotEmpty(network.getGateway())) {
|
||||
vo.setIPv4Address(finalGuestIp);
|
||||
@ -4643,6 +4636,23 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
return new Pair<NicProfile, Integer>(vmNic, Integer.valueOf(deviceId));
|
||||
}
|
||||
|
||||
private String generateNewMacAddressIfForced(Network network, String macAddress, boolean forced) {
|
||||
if (!forced) {
|
||||
throw new CloudRuntimeException("NIC with MAC address = " + macAddress + " exists on network with ID = " + network.getId() +
|
||||
" and forced flag is disabled");
|
||||
}
|
||||
try {
|
||||
s_logger.debug(String.format("Generating a new mac address on network %s as the mac address %s already exists", network.getName(), macAddress));
|
||||
String newMacAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId());
|
||||
s_logger.debug(String.format("Successfully generated the mac address %s, using it instead of the conflicting address %s", newMacAddress, macAddress));
|
||||
return newMacAddress;
|
||||
} catch (InsufficientAddressCapacityException e) {
|
||||
String msg = String.format("Could not generate a new mac address on network %s", network.getName());
|
||||
s_logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unmanageNics(VirtualMachineProfile vm) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com.cloud.hypervisor.vmware;
|
||||
package com.cloud.dc;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@ -15,11 +15,11 @@
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package com.cloud.hypervisor.vmware.dao;
|
||||
package com.cloud.dc.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface VmwareDatacenterDao extends GenericDao<VmwareDatacenterVO, Long> {
|
||||
@ -20,10 +20,11 @@ package com.cloud.hypervisor.vmware.dao;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
@ -281,6 +281,7 @@
|
||||
<bean id="publicIpQuarantineDaoImpl" class="com.cloud.network.dao.PublicIpQuarantineDaoImpl" />
|
||||
<bean id="VMScheduleDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduleDaoImpl" />
|
||||
<bean id="VMScheduledJobDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDaoImpl" />
|
||||
<bean id="VmwareDatacenterDaoImpl" class="com.cloud.hypervisor.vmware.dao.VmwareDatacenterDaoImpl" />
|
||||
<bean id="vnfTemplateDetailsDaoImpl" class="com.cloud.storage.dao.VnfTemplateDetailsDaoImpl" />
|
||||
<bean id="vnfTemplateNicDaoImpl" class="com.cloud.storage.dao.VnfTemplateNicDaoImpl" />
|
||||
<bean id="ClusterDrsPlanDaoImpl" class="org.apache.cloudstack.cluster.dao.ClusterDrsPlanDaoImpl" />
|
||||
|
||||
@ -35,9 +35,9 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
|
||||
import com.cloud.storage.Storage.StoragePoolType;
|
||||
|
||||
@ -41,9 +41,9 @@ import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenter;
|
||||
import com.cloud.dc.VmwareDatacenter;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
|
||||
@ -743,6 +743,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
protected StorageSubsystemCommandHandler storageHandler;
|
||||
|
||||
private boolean convertInstanceVerboseMode = false;
|
||||
protected boolean dpdkSupport = false;
|
||||
protected String dpdkOvsPath;
|
||||
protected String directDownloadTemporaryDownloadPath;
|
||||
@ -803,6 +804,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
return networkDirectDevice;
|
||||
}
|
||||
|
||||
public boolean isConvertInstanceVerboseModeEnabled() {
|
||||
return convertInstanceVerboseMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines resource's public and private network interface according to what is configured in agent.properties.
|
||||
*/
|
||||
@ -991,6 +996,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
params.putAll(getDeveloperProperties());
|
||||
}
|
||||
|
||||
convertInstanceVerboseMode = BooleanUtils.isTrue(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VIRTV2V_VERBOSE_ENABLED));
|
||||
|
||||
pool = (String)params.get("pool");
|
||||
if (pool == null) {
|
||||
pool = "/root";
|
||||
|
||||
@ -609,7 +609,7 @@ public class LibvirtVMDef {
|
||||
}
|
||||
}
|
||||
|
||||
enum DiskType {
|
||||
public enum DiskType {
|
||||
FILE("file"), BLOCK("block"), DIRECTROY("dir"), NETWORK("network");
|
||||
String _diskType;
|
||||
|
||||
@ -2113,14 +2113,14 @@ public class LibvirtVMDef {
|
||||
private String path = "/dev/random";
|
||||
private RngModel rngModel = RngModel.VIRTIO;
|
||||
private RngBackendModel rngBackendModel = RngBackendModel.RANDOM;
|
||||
private int rngRateBytes = 2048;
|
||||
private int rngRatePeriod = 1000;
|
||||
private Integer rngRateBytes = 2048;
|
||||
private Integer rngRatePeriod = 1000;
|
||||
|
||||
public RngDef(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public RngDef(String path, int rngRateBytes, int rngRatePeriod) {
|
||||
public RngDef(String path, Integer rngRateBytes, Integer rngRatePeriod) {
|
||||
this.path = path;
|
||||
this.rngRateBytes = rngRateBytes;
|
||||
this.rngRatePeriod = rngRatePeriod;
|
||||
@ -2139,7 +2139,7 @@ public class LibvirtVMDef {
|
||||
this.rngBackendModel = rngBackendModel;
|
||||
}
|
||||
|
||||
public RngDef(String path, RngBackendModel rngBackendModel, int rngRateBytes, int rngRatePeriod) {
|
||||
public RngDef(String path, RngBackendModel rngBackendModel, Integer rngRateBytes, Integer rngRatePeriod) {
|
||||
this.path = path;
|
||||
this.rngBackendModel = rngBackendModel;
|
||||
this.rngRateBytes = rngRateBytes;
|
||||
@ -2164,11 +2164,11 @@ public class LibvirtVMDef {
|
||||
}
|
||||
|
||||
public int getRngRateBytes() {
|
||||
return rngRateBytes;
|
||||
return rngRateBytes != null ? rngRateBytes : 0;
|
||||
}
|
||||
|
||||
public int getRngRatePeriod() {
|
||||
return rngRatePeriod;
|
||||
return rngRatePeriod != null ? rngRatePeriod : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,400 @@
|
||||
//
|
||||
// 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 com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.ConvertInstanceAnswer;
|
||||
import com.cloud.agent.api.ConvertInstanceCommand;
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.agent.api.to.NfsTO;
|
||||
import com.cloud.agent.api.to.RemoteInstanceTO;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.script.OutputInterpreter;
|
||||
import com.cloud.utils.script.Script;
|
||||
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ResourceWrapper(handles = ConvertInstanceCommand.class)
|
||||
public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<ConvertInstanceCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(LibvirtConvertInstanceCommandWrapper.class);
|
||||
|
||||
private static final List<Hypervisor.HypervisorType> supportedInstanceConvertSourceHypervisors =
|
||||
List.of(Hypervisor.HypervisorType.VMware);
|
||||
|
||||
protected static final String checkIfConversionIsSupportedCommand = "which virt-v2v";
|
||||
|
||||
@Override
|
||||
public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serverResource) {
|
||||
RemoteInstanceTO sourceInstance = cmd.getSourceInstance();
|
||||
Hypervisor.HypervisorType sourceHypervisorType = sourceInstance.getHypervisorType();
|
||||
String sourceInstanceName = sourceInstance.getInstanceName();
|
||||
Hypervisor.HypervisorType destinationHypervisorType = cmd.getDestinationHypervisorType();
|
||||
List<String> destinationStoragePools = cmd.getDestinationStoragePools();
|
||||
DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation();
|
||||
long timeout = (long) cmd.getWait() * 1000;
|
||||
|
||||
if (!isInstanceConversionSupportedOnHost()) {
|
||||
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);
|
||||
s_logger.info(msg);
|
||||
return new ConvertInstanceAnswer(cmd, false, msg);
|
||||
}
|
||||
|
||||
if (!areSourceAndDestinationHypervisorsSupported(sourceHypervisorType, destinationHypervisorType)) {
|
||||
String err = destinationHypervisorType != Hypervisor.HypervisorType.KVM ?
|
||||
String.format("The destination hypervisor type is %s, KVM was expected, cannot handle it", destinationHypervisorType) :
|
||||
String.format("The source hypervisor type %s is not supported for KVM conversion", sourceHypervisorType);
|
||||
s_logger.error(err);
|
||||
return new ConvertInstanceAnswer(cmd, false, err);
|
||||
}
|
||||
|
||||
final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr();
|
||||
KVMStoragePool temporaryStoragePool = getTemporaryStoragePool(conversionTemporaryLocation, storagePoolMgr);
|
||||
|
||||
s_logger.info(String.format("Attempting to convert the instance %s from %s to KVM",
|
||||
sourceInstanceName, sourceHypervisorType));
|
||||
final String convertInstanceUrl = getConvertInstanceUrl(sourceInstance);
|
||||
final String temporaryConvertUuid = UUID.randomUUID().toString();
|
||||
final String temporaryPasswordFilePath = createTemporaryPasswordFileAndRetrievePath(sourceInstance);
|
||||
final String temporaryConvertPath = temporaryStoragePool.getLocalPath();
|
||||
boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled();
|
||||
|
||||
try {
|
||||
boolean result = performInstanceConversion(convertInstanceUrl, sourceInstanceName, temporaryPasswordFilePath,
|
||||
temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled);
|
||||
if (!result) {
|
||||
String err = String.format("The virt-v2v conversion of the instance %s failed. " +
|
||||
"Please check the agent logs for the virt-v2v output", sourceInstanceName);
|
||||
s_logger.error(err);
|
||||
return new ConvertInstanceAnswer(cmd, false, err);
|
||||
}
|
||||
String convertedBasePath = String.format("%s/%s", temporaryConvertPath, temporaryConvertUuid);
|
||||
LibvirtDomainXMLParser xmlParser = parseMigratedVMXmlDomain(convertedBasePath);
|
||||
|
||||
List<KVMPhysicalDisk> temporaryDisks = xmlParser == null ?
|
||||
getTemporaryDisksWithPrefixFromTemporaryPool(temporaryStoragePool, temporaryConvertPath, temporaryConvertUuid) :
|
||||
getTemporaryDisksFromParsedXml(temporaryStoragePool, xmlParser, convertedBasePath);
|
||||
|
||||
List<KVMPhysicalDisk> destinationDisks = moveTemporaryDisksToDestination(temporaryDisks,
|
||||
destinationStoragePools, storagePoolMgr);
|
||||
|
||||
cleanupDisksAndDomainFromTemporaryLocation(temporaryDisks, temporaryStoragePool, temporaryConvertUuid);
|
||||
|
||||
UnmanagedInstanceTO convertedInstanceTO = getConvertedUnmanagedInstance(temporaryConvertUuid,
|
||||
destinationDisks, xmlParser);
|
||||
return new ConvertInstanceAnswer(cmd, convertedInstanceTO);
|
||||
} catch (Exception e) {
|
||||
String error = String.format("Error converting instance %s from %s, due to: %s",
|
||||
sourceInstanceName, sourceHypervisorType, e.getMessage());
|
||||
s_logger.error(error, e);
|
||||
return new ConvertInstanceAnswer(cmd, false, error);
|
||||
} finally {
|
||||
s_logger.debug("Cleaning up instance conversion temporary password file");
|
||||
Script.runSimpleBashScript(String.format("rm -rf %s", temporaryPasswordFilePath));
|
||||
if (conversionTemporaryLocation instanceof NfsTO) {
|
||||
s_logger.debug("Cleaning up secondary storage temporary location");
|
||||
storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected KVMStoragePool getTemporaryStoragePool(DataStoreTO conversionTemporaryLocation, KVMStoragePoolManager storagePoolMgr) {
|
||||
if (conversionTemporaryLocation instanceof NfsTO) {
|
||||
NfsTO nfsTO = (NfsTO) conversionTemporaryLocation;
|
||||
return storagePoolMgr.getStoragePoolByURI(nfsTO.getUrl());
|
||||
} else {
|
||||
PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) conversionTemporaryLocation;
|
||||
return storagePoolMgr.getStoragePool(primaryDataStoreTO.getPoolType(), primaryDataStoreTO.getUuid());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean areSourceAndDestinationHypervisorsSupported(Hypervisor.HypervisorType sourceHypervisorType,
|
||||
Hypervisor.HypervisorType destinationHypervisorType) {
|
||||
return destinationHypervisorType == Hypervisor.HypervisorType.KVM &&
|
||||
supportedInstanceConvertSourceHypervisors.contains(sourceHypervisorType);
|
||||
}
|
||||
|
||||
protected List<KVMPhysicalDisk> getTemporaryDisksFromParsedXml(KVMStoragePool pool, LibvirtDomainXMLParser xmlParser, String convertedBasePath) {
|
||||
List<LibvirtVMDef.DiskDef> disksDefs = xmlParser.getDisks();
|
||||
disksDefs = disksDefs.stream().filter(x -> x.getDiskType() == LibvirtVMDef.DiskDef.DiskType.FILE &&
|
||||
x.getDeviceType() == LibvirtVMDef.DiskDef.DeviceType.DISK).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(disksDefs)) {
|
||||
String err = String.format("Cannot find any disk defined on the converted XML domain %s.xml", convertedBasePath);
|
||||
s_logger.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
sanitizeDisksPath(disksDefs);
|
||||
return getPhysicalDisksFromDefPaths(disksDefs, pool);
|
||||
}
|
||||
|
||||
private List<KVMPhysicalDisk> getPhysicalDisksFromDefPaths(List<LibvirtVMDef.DiskDef> disksDefs, KVMStoragePool pool) {
|
||||
List<KVMPhysicalDisk> disks = new ArrayList<>();
|
||||
for (LibvirtVMDef.DiskDef diskDef : disksDefs) {
|
||||
KVMPhysicalDisk physicalDisk = pool.getPhysicalDisk(diskDef.getDiskPath());
|
||||
disks.add(physicalDisk);
|
||||
}
|
||||
return disks;
|
||||
}
|
||||
|
||||
protected List<KVMPhysicalDisk> getTemporaryDisksWithPrefixFromTemporaryPool(KVMStoragePool pool, String path, String prefix) {
|
||||
String msg = String.format("Could not parse correctly the converted XML domain, checking for disks on %s with prefix %s", path, prefix);
|
||||
s_logger.info(msg);
|
||||
pool.refresh();
|
||||
List<KVMPhysicalDisk> disksWithPrefix = pool.listPhysicalDisks()
|
||||
.stream()
|
||||
.filter(x -> x.getName().startsWith(prefix) && !x.getName().endsWith(".xml"))
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(disksWithPrefix)) {
|
||||
msg = String.format("Could not find any converted disk with prefix %s on temporary location %s", prefix, path);
|
||||
s_logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
return disksWithPrefix;
|
||||
}
|
||||
|
||||
private void cleanupDisksAndDomainFromTemporaryLocation(List<KVMPhysicalDisk> disks,
|
||||
KVMStoragePool temporaryStoragePool,
|
||||
String temporaryConvertUuid) {
|
||||
for (KVMPhysicalDisk disk : disks) {
|
||||
s_logger.info(String.format("Cleaning up temporary disk %s after conversion from temporary location", disk.getName()));
|
||||
temporaryStoragePool.deletePhysicalDisk(disk.getName(), Storage.ImageFormat.QCOW2);
|
||||
}
|
||||
s_logger.info(String.format("Cleaning up temporary domain %s after conversion from temporary location", 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) {
|
||||
for (LibvirtVMDef.DiskDef disk : disks) {
|
||||
String[] diskPathParts = disk.getDiskPath().split("/");
|
||||
String relativePath = diskPathParts[diskPathParts.length - 1];
|
||||
disk.setDiskPath(relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<KVMPhysicalDisk> moveTemporaryDisksToDestination(List<KVMPhysicalDisk> temporaryDisks,
|
||||
List<String> destinationStoragePools,
|
||||
KVMStoragePoolManager storagePoolMgr) {
|
||||
List<KVMPhysicalDisk> targetDisks = new ArrayList<>();
|
||||
if (temporaryDisks.size() != destinationStoragePools.size()) {
|
||||
String warn = String.format("Discrepancy between the converted instance disks (%s) " +
|
||||
"and the expected number of disks (%s)", temporaryDisks.size(), destinationStoragePools.size());
|
||||
s_logger.warn(warn);
|
||||
}
|
||||
for (int i = 0; i < temporaryDisks.size(); i++) {
|
||||
String poolPath = destinationStoragePools.get(i);
|
||||
KVMStoragePool destinationPool = storagePoolMgr.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, poolPath);
|
||||
if (destinationPool == null) {
|
||||
String err = String.format("Could not find a storage pool by URI: %s", poolPath);
|
||||
s_logger.error(err);
|
||||
continue;
|
||||
}
|
||||
KVMPhysicalDisk sourceDisk = temporaryDisks.get(i);
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
String msg = String.format("Trying to copy converted instance disk number %s from the temporary location %s" +
|
||||
" to destination storage pool %s", i, sourceDisk.getPool().getLocalPath(), destinationPool.getUuid());
|
||||
s_logger.debug(msg);
|
||||
}
|
||||
|
||||
String destinationName = UUID.randomUUID().toString();
|
||||
|
||||
KVMPhysicalDisk destinationDisk = storagePoolMgr.copyPhysicalDisk(sourceDisk, destinationName, destinationPool, 7200 * 1000);
|
||||
targetDisks.add(destinationDisk);
|
||||
}
|
||||
return targetDisks;
|
||||
}
|
||||
|
||||
private UnmanagedInstanceTO getConvertedUnmanagedInstance(String baseName,
|
||||
List<KVMPhysicalDisk> vmDisks,
|
||||
LibvirtDomainXMLParser xmlParser) {
|
||||
UnmanagedInstanceTO instanceTO = new UnmanagedInstanceTO();
|
||||
instanceTO.setName(baseName);
|
||||
instanceTO.setDisks(getUnmanagedInstanceDisks(vmDisks, xmlParser));
|
||||
instanceTO.setNics(getUnmanagedInstanceNics(xmlParser));
|
||||
return instanceTO;
|
||||
}
|
||||
|
||||
private List<UnmanagedInstanceTO.Nic> getUnmanagedInstanceNics(LibvirtDomainXMLParser xmlParser) {
|
||||
List<UnmanagedInstanceTO.Nic> nics = new ArrayList<>();
|
||||
if (xmlParser != null) {
|
||||
List<LibvirtVMDef.InterfaceDef> interfaces = xmlParser.getInterfaces();
|
||||
for (LibvirtVMDef.InterfaceDef interfaceDef : interfaces) {
|
||||
UnmanagedInstanceTO.Nic nic = new UnmanagedInstanceTO.Nic();
|
||||
nic.setMacAddress(interfaceDef.getMacAddress());
|
||||
nic.setNicId(interfaceDef.getBrName());
|
||||
nic.setAdapterType(interfaceDef.getModel().toString());
|
||||
nics.add(nic);
|
||||
}
|
||||
}
|
||||
return nics;
|
||||
}
|
||||
|
||||
protected List<UnmanagedInstanceTO.Disk> getUnmanagedInstanceDisks(List<KVMPhysicalDisk> vmDisks, LibvirtDomainXMLParser xmlParser) {
|
||||
List<UnmanagedInstanceTO.Disk> instanceDisks = new ArrayList<>();
|
||||
List<LibvirtVMDef.DiskDef> diskDefs = xmlParser != null ? xmlParser.getDisks() : null;
|
||||
for (int i = 0; i< vmDisks.size(); i++) {
|
||||
KVMPhysicalDisk physicalDisk = vmDisks.get(i);
|
||||
KVMStoragePool storagePool = physicalDisk.getPool();
|
||||
UnmanagedInstanceTO.Disk disk = new UnmanagedInstanceTO.Disk();
|
||||
disk.setPosition(i);
|
||||
Pair<String, String> storagePoolHostAndPath = getNfsStoragePoolHostAndPath(storagePool);
|
||||
disk.setDatastoreHost(storagePoolHostAndPath.first());
|
||||
disk.setDatastorePath(storagePoolHostAndPath.second());
|
||||
disk.setDatastoreName(storagePool.getUuid());
|
||||
disk.setDatastoreType(storagePool.getType().name());
|
||||
disk.setCapacity(physicalDisk.getVirtualSize());
|
||||
disk.setFileBaseName(physicalDisk.getName());
|
||||
if (CollectionUtils.isNotEmpty(diskDefs)) {
|
||||
LibvirtVMDef.DiskDef diskDef = diskDefs.get(i);
|
||||
disk.setController(diskDef.getBusType() != null ? diskDef.getBusType().toString() : LibvirtVMDef.DiskDef.DiskBus.VIRTIO.toString());
|
||||
} else {
|
||||
// If the job is finished but we cannot parse the XML, the guest VM can use the virtio driver
|
||||
disk.setController(LibvirtVMDef.DiskDef.DiskBus.VIRTIO.toString());
|
||||
}
|
||||
instanceDisks.add(disk);
|
||||
}
|
||||
return instanceDisks;
|
||||
}
|
||||
|
||||
protected Pair<String, String> getNfsStoragePoolHostAndPath(KVMStoragePool storagePool) {
|
||||
String sourceHostIp = null;
|
||||
String sourcePath = null;
|
||||
String storagePoolMountPoint = Script.runSimpleBashScript(String.format("mount | grep %s", storagePool.getLocalPath()));
|
||||
if (StringUtils.isNotEmpty(storagePoolMountPoint)) {
|
||||
String[] res = storagePoolMountPoint.strip().split(" ");
|
||||
res = res[0].split(":");
|
||||
sourceHostIp = res[0].strip();
|
||||
sourcePath = res[1].strip();
|
||||
}
|
||||
return new Pair<>(sourceHostIp, sourcePath);
|
||||
}
|
||||
|
||||
protected boolean performInstanceConversion(String convertInstanceUrl, String sourceInstanceName,
|
||||
String temporaryPasswordFilePath,
|
||||
String temporaryConvertFolder,
|
||||
String temporaryConvertUuid,
|
||||
long timeout, boolean verboseModeEnabled) {
|
||||
Script script = new Script("virt-v2v", timeout, s_logger);
|
||||
script.add("--root", "first");
|
||||
script.add("-ic", convertInstanceUrl);
|
||||
script.add(sourceInstanceName);
|
||||
script.add("--password-file", temporaryPasswordFilePath);
|
||||
script.add("-o", "local");
|
||||
script.add("-os", temporaryConvertFolder);
|
||||
script.add("-of", "qcow2");
|
||||
script.add("-on", temporaryConvertUuid);
|
||||
if (verboseModeEnabled) {
|
||||
script.add("-v");
|
||||
}
|
||||
|
||||
String logPrefix = String.format("virt-v2v source: %s %s progress", convertInstanceUrl, sourceInstanceName);
|
||||
OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix);
|
||||
script.execute(outputLogger);
|
||||
int exitValue = script.getExitValue();
|
||||
return exitValue == 0;
|
||||
}
|
||||
|
||||
private String createTemporaryPasswordFileAndRetrievePath(RemoteInstanceTO sourceInstance) {
|
||||
String password = null;
|
||||
if (sourceInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
|
||||
password = sourceInstance.getVcenterPassword();
|
||||
}
|
||||
String passwordFile = String.format("/tmp/vmw-%s", UUID.randomUUID());
|
||||
String msg = String.format("Creating a temporary password file for VMware instance %s conversion on: %s", sourceInstance.getInstanceName(), passwordFile);
|
||||
s_logger.debug(msg);
|
||||
Script.runSimpleBashScriptForExitValueAvoidLogging(String.format("echo \"%s\" > %s", password, passwordFile));
|
||||
return passwordFile;
|
||||
}
|
||||
|
||||
private String getConvertInstanceUrl(RemoteInstanceTO sourceInstance) {
|
||||
String url = null;
|
||||
if (sourceInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
|
||||
url = getConvertInstanceUrlFromVmware(sourceInstance);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private String getConvertInstanceUrlFromVmware(RemoteInstanceTO vmwareInstance) {
|
||||
String vcenter = vmwareInstance.getVcenterHost();
|
||||
String datacenter = vmwareInstance.getDatacenterName();
|
||||
String username = vmwareInstance.getVcenterUsername();
|
||||
String host = vmwareInstance.getHostName();
|
||||
String cluster = vmwareInstance.getClusterName();
|
||||
|
||||
String encodedUsername = encodeUsername(username);
|
||||
return String.format("vpx://%s@%s/%s/%s/%s?no_verify=1",
|
||||
encodedUsername, vcenter, datacenter, cluster, host);
|
||||
}
|
||||
protected LibvirtDomainXMLParser parseMigratedVMXmlDomain(String installPath) throws IOException {
|
||||
String xmlPath = String.format("%s.xml", installPath);
|
||||
if (!new File(xmlPath).exists()) {
|
||||
String err = String.format("Conversion failed. Unable to find the converted XML domain, expected %s", xmlPath);
|
||||
s_logger.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
InputStream is = new BufferedInputStream(new FileInputStream(xmlPath));
|
||||
String xml = IOUtils.toString(is, Charset.defaultCharset());
|
||||
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
|
||||
try {
|
||||
parser.parseDomainXML(xml);
|
||||
return parser;
|
||||
} catch (RuntimeException e) {
|
||||
String err = String.format("Error parsing the converted instance XML domain at %s: %s", xmlPath, e.getMessage());
|
||||
s_logger.error(err, e);
|
||||
s_logger.debug(xml);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected String encodeUsername(String username) {
|
||||
return URLEncoder.encode(username, Charset.defaultCharset());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,310 @@
|
||||
//
|
||||
// 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 com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.ConvertInstanceCommand;
|
||||
import com.cloud.agent.api.to.NfsTO;
|
||||
import com.cloud.agent.api.to.RemoteInstanceTO;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
|
||||
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.script.Script;
|
||||
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedConstruction;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LibvirtConvertInstanceCommandWrapperTest {
|
||||
|
||||
@Spy
|
||||
private LibvirtConvertInstanceCommandWrapper convertInstanceCommandWrapper = Mockito.spy(LibvirtConvertInstanceCommandWrapper.class);
|
||||
|
||||
@Mock
|
||||
private LibvirtComputingResource libvirtComputingResourceMock;
|
||||
|
||||
@Mock
|
||||
private KVMStoragePool temporaryPool;
|
||||
@Mock
|
||||
private KVMStoragePool destinationPool;
|
||||
@Mock
|
||||
private PrimaryDataStoreTO primaryDataStore;
|
||||
@Mock
|
||||
private NfsTO secondaryDataStore;
|
||||
@Mock
|
||||
private KVMStoragePoolManager storagePoolManager;
|
||||
|
||||
private static final String secondaryPoolUrl = "nfs://192.168.1.1/secondary";
|
||||
private static final String vmName = "VmToImport";
|
||||
private static final String hostName = "VmwareHost1";
|
||||
private static final String vmwareVcenter = "192.168.1.2";
|
||||
private static final String vmwareDatacenter = "Datacenter";
|
||||
private static final String vmwareCluster = "Cluster";
|
||||
private static final String vmwareUsername = "administrator@vsphere.local";
|
||||
private static final String vmwarePassword = "password";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Mockito.when(secondaryDataStore.getUrl()).thenReturn(secondaryPoolUrl);
|
||||
Mockito.when(libvirtComputingResourceMock.getStoragePoolMgr()).thenReturn(storagePoolManager);
|
||||
Mockito.when(storagePoolManager.getStoragePoolByURI(secondaryPoolUrl)).thenReturn(temporaryPool);
|
||||
KVMPhysicalDisk physicalDisk1 = Mockito.mock(KVMPhysicalDisk.class);
|
||||
KVMPhysicalDisk physicalDisk2 = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstanceConversionSupportedOnHost() {
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
|
||||
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(0);
|
||||
boolean supported = convertInstanceCommandWrapper.isInstanceConversionSupportedOnHost();
|
||||
Assert.assertTrue(supported);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAreSourceAndDestinationHypervisorsSupported() {
|
||||
boolean supported = convertInstanceCommandWrapper.areSourceAndDestinationHypervisorsSupported(Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.KVM);
|
||||
Assert.assertTrue(supported);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAreSourceAndDestinationHypervisorsSupportedUnsupportedSource() {
|
||||
boolean supported = convertInstanceCommandWrapper.areSourceAndDestinationHypervisorsSupported(Hypervisor.HypervisorType.XenServer, Hypervisor.HypervisorType.KVM);
|
||||
Assert.assertFalse(supported);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAreSourceAndDestinationHypervisorsSupportedUnsupportedDestination() {
|
||||
boolean supported = convertInstanceCommandWrapper.areSourceAndDestinationHypervisorsSupported(Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.VMware);
|
||||
Assert.assertFalse(supported);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemporaryStoragePool() {
|
||||
KVMStoragePool temporaryStoragePool = convertInstanceCommandWrapper.getTemporaryStoragePool(secondaryDataStore, libvirtComputingResourceMock.getStoragePoolMgr());
|
||||
Assert.assertNotNull(temporaryStoragePool);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemporaryDisksWithPrefixFromTemporaryPool() {
|
||||
String convertPath = "/xyz";
|
||||
String convertPrefix = UUID.randomUUID().toString();
|
||||
KVMPhysicalDisk physicalDisk1 = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(physicalDisk1.getName()).thenReturn("disk1");
|
||||
KVMPhysicalDisk physicalDisk2 = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(physicalDisk2.getName()).thenReturn("disk2");
|
||||
|
||||
KVMPhysicalDisk convertedDisk1 = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(convertedDisk1.getName()).thenReturn(String.format("%s-sda", convertPrefix));
|
||||
KVMPhysicalDisk convertedDisk2 = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(convertedDisk2.getName()).thenReturn(String.format("%s-sdb", convertPrefix));
|
||||
KVMPhysicalDisk convertedXml = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(convertedXml.getName()).thenReturn(String.format("%s.xml", convertPrefix));
|
||||
Mockito.when(temporaryPool.listPhysicalDisks()).thenReturn(Arrays.asList(physicalDisk1, physicalDisk2,
|
||||
convertedDisk1, convertedDisk2, convertedXml));
|
||||
|
||||
List<KVMPhysicalDisk> convertedDisks = convertInstanceCommandWrapper.getTemporaryDisksWithPrefixFromTemporaryPool(temporaryPool, convertPath, convertPrefix);
|
||||
Assert.assertEquals(2, convertedDisks.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemporaryDisksFromParsedXml() {
|
||||
String relativePath = UUID.randomUUID().toString();
|
||||
String fullPath = String.format("/mnt/xyz/%s", relativePath);
|
||||
|
||||
LibvirtVMDef.DiskDef diskDef = new LibvirtVMDef.DiskDef();
|
||||
LibvirtVMDef.DiskDef.DiskBus bus = LibvirtVMDef.DiskDef.DiskBus.VIRTIO;
|
||||
LibvirtVMDef.DiskDef.DiskFmtType type = LibvirtVMDef.DiskDef.DiskFmtType.QCOW2;
|
||||
diskDef.defFileBasedDisk(fullPath, "test", bus, type);
|
||||
|
||||
LibvirtDomainXMLParser parser = Mockito.mock(LibvirtDomainXMLParser.class);
|
||||
Mockito.when(parser.getDisks()).thenReturn(List.of(diskDef));
|
||||
|
||||
KVMPhysicalDisk convertedDisk1 = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(convertedDisk1.getName()).thenReturn("disk1");
|
||||
Mockito.when(temporaryPool.getPhysicalDisk(relativePath)).thenReturn(convertedDisk1);
|
||||
|
||||
List<KVMPhysicalDisk> disks = convertInstanceCommandWrapper.getTemporaryDisksFromParsedXml(temporaryPool, parser, "");
|
||||
Mockito.verify(convertInstanceCommandWrapper).sanitizeDisksPath(List.of(diskDef));
|
||||
Assert.assertEquals(1, disks.size());
|
||||
Assert.assertEquals("disk1", disks.get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSanitizeDisksPath() {
|
||||
String relativePath = UUID.randomUUID().toString();
|
||||
String fullPath = String.format("/mnt/xyz/%s", relativePath);
|
||||
LibvirtVMDef.DiskDef diskDef = new LibvirtVMDef.DiskDef();
|
||||
LibvirtVMDef.DiskDef.DiskBus bus = LibvirtVMDef.DiskDef.DiskBus.VIRTIO;
|
||||
LibvirtVMDef.DiskDef.DiskFmtType type = LibvirtVMDef.DiskDef.DiskFmtType.QCOW2;
|
||||
diskDef.defFileBasedDisk(fullPath, "test", bus, type);
|
||||
|
||||
convertInstanceCommandWrapper.sanitizeDisksPath(List.of(diskDef));
|
||||
Assert.assertEquals(relativePath, diskDef.getDiskPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveTemporaryDisksToDestination() {
|
||||
KVMPhysicalDisk sourceDisk = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(sourceDisk.getPool()).thenReturn(temporaryPool);
|
||||
List<KVMPhysicalDisk> disks = List.of(sourceDisk);
|
||||
String destinationPoolUuid = UUID.randomUUID().toString();
|
||||
List<String> destinationPools = List.of(destinationPoolUuid);
|
||||
|
||||
KVMPhysicalDisk destDisk = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(destDisk.getPath()).thenReturn("xyz");
|
||||
Mockito.when(storagePoolManager.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, destinationPoolUuid))
|
||||
.thenReturn(destinationPool);
|
||||
Mockito.when(storagePoolManager.copyPhysicalDisk(Mockito.eq(sourceDisk), Mockito.anyString(), Mockito.eq(destinationPool), Mockito.anyInt()))
|
||||
.thenReturn(destDisk);
|
||||
|
||||
List<KVMPhysicalDisk> movedDisks = convertInstanceCommandWrapper.moveTemporaryDisksToDestination(disks, destinationPools, storagePoolManager);
|
||||
Assert.assertEquals(1, movedDisks.size());
|
||||
Assert.assertEquals("xyz", movedDisks.get(0).getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnmanagedInstanceDisks() {
|
||||
String relativePath = UUID.randomUUID().toString();
|
||||
LibvirtVMDef.DiskDef diskDef = new LibvirtVMDef.DiskDef();
|
||||
LibvirtVMDef.DiskDef.DiskBus bus = LibvirtVMDef.DiskDef.DiskBus.IDE;
|
||||
LibvirtVMDef.DiskDef.DiskFmtType type = LibvirtVMDef.DiskDef.DiskFmtType.QCOW2;
|
||||
diskDef.defFileBasedDisk(relativePath, relativePath, bus, type);
|
||||
|
||||
KVMPhysicalDisk sourceDisk = Mockito.mock(KVMPhysicalDisk.class);
|
||||
Mockito.when(sourceDisk.getName()).thenReturn(UUID.randomUUID().toString());
|
||||
Mockito.when(sourceDisk.getPool()).thenReturn(destinationPool);
|
||||
Mockito.when(destinationPool.getType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
List<KVMPhysicalDisk> disks = List.of(sourceDisk);
|
||||
|
||||
LibvirtDomainXMLParser parser = Mockito.mock(LibvirtDomainXMLParser.class);
|
||||
Mockito.when(parser.getDisks()).thenReturn(List.of(diskDef));
|
||||
|
||||
List<UnmanagedInstanceTO.Disk> unmanagedInstanceDisks = convertInstanceCommandWrapper.getUnmanagedInstanceDisks(disks, parser);
|
||||
Assert.assertEquals(1, unmanagedInstanceDisks.size());
|
||||
UnmanagedInstanceTO.Disk disk = unmanagedInstanceDisks.get(0);
|
||||
Assert.assertEquals(LibvirtVMDef.DiskDef.DiskBus.IDE.toString(), disk.getController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNfsStoragePoolHostAndPath() {
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
|
||||
String localMountPoint = "/mnt/xyz";
|
||||
String host = "192.168.1.2";
|
||||
String path = "/secondary";
|
||||
String mountOutput = String.format("%s:%s on %s type nfs (...)", host, path, localMountPoint);
|
||||
Mockito.when(temporaryPool.getLocalPath()).thenReturn(localMountPoint);
|
||||
Mockito.when(Script.runSimpleBashScript(Mockito.anyString()))
|
||||
.thenReturn(mountOutput);
|
||||
|
||||
Pair<String, String> pair = convertInstanceCommandWrapper.getNfsStoragePoolHostAndPath(temporaryPool);
|
||||
Assert.assertEquals(host, pair.first());
|
||||
Assert.assertEquals(path, pair.second());
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteInstanceTO getRemoteInstanceTO(Hypervisor.HypervisorType hypervisorType) {
|
||||
RemoteInstanceTO remoteInstanceTO = Mockito.mock(RemoteInstanceTO.class);
|
||||
Mockito.when(remoteInstanceTO.getHypervisorType()).thenReturn(hypervisorType);
|
||||
Mockito.when(remoteInstanceTO.getInstanceName()).thenReturn(vmName);
|
||||
Mockito.when(remoteInstanceTO.getHostName()).thenReturn(hostName);
|
||||
Mockito.when(remoteInstanceTO.getVcenterHost()).thenReturn(vmwareVcenter);
|
||||
Mockito.when(remoteInstanceTO.getDatacenterName()).thenReturn(vmwareDatacenter);
|
||||
Mockito.when(remoteInstanceTO.getClusterName()).thenReturn(vmwareCluster);
|
||||
Mockito.when(remoteInstanceTO.getVcenterUsername()).thenReturn(vmwareUsername);
|
||||
Mockito.when(remoteInstanceTO.getVcenterPassword()).thenReturn(vmwarePassword);
|
||||
return remoteInstanceTO;
|
||||
}
|
||||
|
||||
private ConvertInstanceCommand getConvertInstanceCommand(RemoteInstanceTO remoteInstanceTO, Hypervisor.HypervisorType hypervisorType) {
|
||||
ConvertInstanceCommand cmd = Mockito.mock(ConvertInstanceCommand.class);
|
||||
Mockito.when(cmd.getSourceInstance()).thenReturn(remoteInstanceTO);
|
||||
Mockito.when(cmd.getDestinationHypervisorType()).thenReturn(hypervisorType);
|
||||
Mockito.when(cmd.getWait()).thenReturn(14400);
|
||||
Mockito.when(cmd.getConversionTemporaryLocation()).thenReturn(secondaryDataStore);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteConvertUnsupportedOnTheHost() {
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
|
||||
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.VMware);
|
||||
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM);
|
||||
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(1);
|
||||
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
|
||||
Assert.assertFalse(answer.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteConvertUnsupportedHypervisors() {
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
|
||||
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.XenServer);
|
||||
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM);
|
||||
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(0);
|
||||
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
|
||||
Assert.assertFalse(answer.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteConvertFailure() {
|
||||
RemoteInstanceTO remoteInstanceTO = getRemoteInstanceTO(Hypervisor.HypervisorType.VMware);
|
||||
ConvertInstanceCommand cmd = getConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM);
|
||||
String localMountPoint = "/mnt/xyz";
|
||||
Mockito.when(temporaryPool.getLocalPath()).thenReturn(localMountPoint);
|
||||
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class);
|
||||
MockedConstruction<Script> ignored2 = Mockito.mockConstruction(Script.class, (mock, context) -> {
|
||||
Mockito.when(mock.execute()).thenReturn("");
|
||||
Mockito.when(mock.getExitValue()).thenReturn(1);
|
||||
})
|
||||
) {
|
||||
Mockito.when(Script.runSimpleBashScriptForExitValue(LibvirtConvertInstanceCommandWrapper.checkIfConversionIsSupportedCommand)).thenReturn(0);
|
||||
Mockito.when(Script.runSimpleBashScriptForExitValueAvoidLogging(Mockito.anyString())).thenReturn(0);
|
||||
Mockito.when(Script.runSimpleBashScript(Mockito.anyString())).thenReturn("");
|
||||
|
||||
Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock);
|
||||
Assert.assertFalse(answer.getResult());
|
||||
Mockito.verify(convertInstanceCommandWrapper).performInstanceConversion(Mockito.anyString(),
|
||||
Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -27,6 +27,12 @@ import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HostMO;
|
||||
import com.cloud.hypervisor.vmware.util.VmwareClient;
|
||||
import com.cloud.hypervisor.vmware.util.VmwareHelper;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import com.vmware.vim25.VirtualMachinePowerState;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
@ -44,6 +50,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -80,9 +87,9 @@ import com.cloud.host.dao.HostDetailsDao;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.HypervisorGuru;
|
||||
import com.cloud.hypervisor.HypervisorGuruBase;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
|
||||
import com.cloud.hypervisor.vmware.manager.VmwareManager;
|
||||
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
|
||||
@ -1217,4 +1224,118 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
|
||||
protected VirtualMachineTO toVirtualMachineTO(VirtualMachineProfile vmProfile) {
|
||||
return super.toVirtualMachineTO(vmProfile);
|
||||
}
|
||||
|
||||
private VmwareContext connectToVcenter(String vcenter, String username, String password) throws Exception {
|
||||
VmwareClient vimClient = new VmwareClient(vcenter);
|
||||
String serviceUrl = "https://" + vcenter + "/sdk/vimService";
|
||||
vimClient.connect(serviceUrl, username, password);
|
||||
return new VmwareContext(vimClient, vcenter);
|
||||
}
|
||||
|
||||
private void relocateClonedVMToSourceHost(VirtualMachineMO clonedVM, HostMO sourceHost) throws Exception {
|
||||
if (!clonedVM.getRunningHost().getMor().equals(sourceHost.getMor())) {
|
||||
s_logger.debug(String.format("Relocating VM to the same host as the source VM: %s", sourceHost.getHostName()));
|
||||
if (!clonedVM.relocate(sourceHost.getMor())) {
|
||||
String err = String.format("Cannot relocate cloned VM %s to the source host %s", clonedVM.getVmName(), sourceHost.getHostName());
|
||||
s_logger.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VirtualMachineMO createCloneFromSourceVM(String vmName, VirtualMachineMO vmMo,
|
||||
DatacenterMO dataCenterMO) throws Exception {
|
||||
HostMO sourceHost = vmMo.getRunningHost();
|
||||
String cloneName = UUID.randomUUID().toString();
|
||||
DatastoreMO datastoreMO = vmMo.getAllDatastores().get(0); //pick the first datastore
|
||||
ManagedObjectReference morPool = vmMo.getRunningHost().getHyperHostOwnerResourcePool();
|
||||
boolean result = vmMo.createFullClone(cloneName, dataCenterMO.getVmFolder(), morPool, datastoreMO.getMor(), Storage.ProvisioningType.THIN);
|
||||
VirtualMachineMO clonedVM = dataCenterMO.findVm(cloneName);
|
||||
if (!result || clonedVM == null) {
|
||||
String err = String.format("Could not clone VM %s before migration from VMware", vmName);
|
||||
s_logger.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
relocateClonedVMToSourceHost(clonedVM, sourceHost);
|
||||
return clonedVM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName,
|
||||
Map<String, String> params) {
|
||||
s_logger.debug(String.format("Cloning VM %s on external vCenter %s", vmName, hostIp));
|
||||
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);
|
||||
|
||||
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 %s/%s", vmName, vcenter, datacenter);
|
||||
s_logger.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
VirtualMachinePowerState sourceVmPowerState = vmMo.getPowerState();
|
||||
if (sourceVmPowerState == VirtualMachinePowerState.POWERED_ON && isWindowsVm(vmMo)) {
|
||||
s_logger.debug(String.format("VM %s is a Windows VM and its Running, cannot be imported." +
|
||||
"Please gracefully shut it down before attempting the import",
|
||||
vmName));
|
||||
}
|
||||
|
||||
VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO);
|
||||
s_logger.debug(String.format("VM %s cloned successfully", vmName));
|
||||
UnmanagedInstanceTO clonedInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), clonedVM);
|
||||
setNicsFromSourceVM(clonedInstance, vmMo);
|
||||
clonedInstance.setCloneSourcePowerState(sourceVmPowerState == VirtualMachinePowerState.POWERED_ON ? UnmanagedInstanceTO.PowerState.PowerOn : UnmanagedInstanceTO.PowerState.PowerOff);
|
||||
return clonedInstance;
|
||||
} catch (Exception e) {
|
||||
String err = String.format("Error cloning VM: %s from external vCenter %s: %s", vmName, vcenter, e.getMessage());
|
||||
s_logger.error(err, e);
|
||||
throw new CloudRuntimeException(err, e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWindowsVm(VirtualMachineMO vmMo) throws Exception {
|
||||
UnmanagedInstanceTO sourceInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
|
||||
return sourceInstance.getOperatingSystem().toLowerCase().contains("windows");
|
||||
}
|
||||
|
||||
private void setNicsFromSourceVM(UnmanagedInstanceTO clonedInstance, VirtualMachineMO vmMo) throws Exception {
|
||||
UnmanagedInstanceTO sourceInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
|
||||
List<UnmanagedInstanceTO.Disk> sourceDisks = sourceInstance.getDisks();
|
||||
List<UnmanagedInstanceTO.Disk> clonedDisks = clonedInstance.getDisks();
|
||||
for (int i = 0; i < sourceDisks.size(); i++) {
|
||||
UnmanagedInstanceTO.Disk sourceDisk = sourceDisks.get(i);
|
||||
UnmanagedInstanceTO.Disk clonedDisk = clonedDisks.get(i);
|
||||
clonedDisk.setDiskId(sourceDisk.getDiskId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
|
||||
s_logger.debug(String.format("Removing VM %s on external vCenter %s", vmName, hostIp));
|
||||
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);
|
||||
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 %s on datacenter %s, not possible to remove VM out of band",
|
||||
vmName, datacenter);
|
||||
s_logger.error(err);
|
||||
return false;
|
||||
}
|
||||
return vmMo.destroy();
|
||||
} catch (Exception e) {
|
||||
String err = String.format("Error destroying external VM %s: %s", vmName, e.getMessage());
|
||||
s_logger.error(err, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
|
||||
package com.cloud.hypervisor.vmware;
|
||||
|
||||
import com.cloud.dc.VmwareDatacenter;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.dc.VsphereStoragePolicy;
|
||||
import com.cloud.exception.DiscoveryException;
|
||||
import com.cloud.exception.ResourceInUseException;
|
||||
@ -25,11 +27,13 @@ import com.cloud.utils.component.PluggableService;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ImportVsphereStoragePoliciesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcVmsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePoliciesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePolicyCompatiblePoolsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.UpdateVmwareDcCmd;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -48,4 +52,6 @@ public interface VmwareDatacenterService extends PluggableService {
|
||||
List<? extends VsphereStoragePolicy> listVsphereStoragePolicies(ListVsphereStoragePoliciesCmd cmd);
|
||||
|
||||
List<StoragePool> listVsphereStoragePolicyCompatibleStoragePools(ListVsphereStoragePolicyCompatiblePoolsCmd cmd);
|
||||
|
||||
List<UnmanagedInstanceTO> listVMsInDatacenter(ListVmwareDcVmsCmd cmd);
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ import java.util.UUID;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -46,7 +47,7 @@ import com.cloud.host.HostVO;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
|
||||
import com.cloud.hypervisor.vmware.manager.VmwareManager;
|
||||
import com.cloud.hypervisor.vmware.mo.ClusterMO;
|
||||
|
||||
@ -43,8 +43,10 @@ import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
import javax.persistence.EntityExistsException;
|
||||
|
||||
import com.cloud.hypervisor.vmware.util.VmwareClient;
|
||||
import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ImportVsphereStoragePoliciesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcVmsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePoliciesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePolicyCompatiblePoolsCmd;
|
||||
@ -61,6 +63,7 @@ import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplain
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -109,13 +112,13 @@ import com.cloud.hypervisor.HypervisorGuruManager;
|
||||
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
||||
import com.cloud.hypervisor.vmware.LegacyZoneVO;
|
||||
import com.cloud.hypervisor.vmware.VmwareCleanupMaid;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenter;
|
||||
import com.cloud.dc.VmwareDatacenter;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterService;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO;
|
||||
import com.cloud.hypervisor.vmware.dao.LegacyZoneDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
|
||||
import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
|
||||
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
|
||||
@ -1113,6 +1116,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
|
||||
cmdList.add(ImportVsphereStoragePoliciesCmd.class);
|
||||
cmdList.add(ListVsphereStoragePoliciesCmd.class);
|
||||
cmdList.add(ListVsphereStoragePolicyCompatiblePoolsCmd.class);
|
||||
cmdList.add(ListVmwareDcVmsCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
@ -1586,6 +1590,62 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
|
||||
return compatiblePools;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UnmanagedInstanceTO> listVMsInDatacenter(ListVmwareDcVmsCmd cmd) {
|
||||
String vcenter = cmd.getVcenter();
|
||||
String datacenterName = cmd.getDatacenterName();
|
||||
String username = cmd.getUsername();
|
||||
String password = cmd.getPassword();
|
||||
Long existingVcenterId = cmd.getExistingVcenterId();
|
||||
String keyword = cmd.getKeyword();
|
||||
|
||||
if ((existingVcenterId == null && StringUtils.isBlank(vcenter)) ||
|
||||
(existingVcenterId != null && StringUtils.isNotBlank(vcenter))) {
|
||||
throw new InvalidParameterValueException("Please provide an existing vCenter ID or a vCenter IP/Name, parameters are mutually exclusive");
|
||||
}
|
||||
|
||||
if (existingVcenterId == null && StringUtils.isAnyBlank(vcenter, datacenterName, username, password)) {
|
||||
throw new InvalidParameterValueException("Please set all the information for a vCenter IP/Name, datacenter, username and password");
|
||||
}
|
||||
|
||||
if (existingVcenterId != null) {
|
||||
VmwareDatacenterVO vmwareDc = vmwareDcDao.findById(existingVcenterId);
|
||||
if (vmwareDc == null) {
|
||||
throw new InvalidParameterValueException(String.format("Cannot find a VMware datacenter with ID %s", existingVcenterId));
|
||||
}
|
||||
vcenter = vmwareDc.getVcenterHost();
|
||||
datacenterName = vmwareDc.getVmwareDatacenterName();
|
||||
username = vmwareDc.getUser();
|
||||
password = vmwareDc.getPassword();
|
||||
}
|
||||
|
||||
try {
|
||||
s_logger.debug(String.format("Connecting to the VMware datacenter %s at vCenter %s to retrieve VMs",
|
||||
datacenterName, vcenter));
|
||||
String serviceUrl = String.format("https://%s/sdk/vimService", vcenter);
|
||||
VmwareClient vimClient = new VmwareClient(vcenter);
|
||||
vimClient.connect(serviceUrl, username, password);
|
||||
VmwareContext context = new VmwareContext(vimClient, vcenter);
|
||||
|
||||
DatacenterMO dcMo = new DatacenterMO(context, datacenterName);
|
||||
ManagedObjectReference dcMor = dcMo.getMor();
|
||||
if (dcMor == null) {
|
||||
String msg = String.format("Unable to find VMware datacenter %s in vCenter %s",
|
||||
datacenterName, vcenter);
|
||||
s_logger.error(msg);
|
||||
throw new InvalidParameterValueException(msg);
|
||||
}
|
||||
List<UnmanagedInstanceTO> instances = dcMo.getAllVmsOnDatacenter();
|
||||
return StringUtils.isBlank(keyword) ? instances :
|
||||
instances.stream().filter(x -> x.getName().toLowerCase().contains(keyword.toLowerCase())).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
String errorMsg = String.format("Error retrieving stopped VMs from the VMware VC %s datacenter %s: %s",
|
||||
vcenter, datacenterName, e.getMessage());
|
||||
s_logger.error(errorMsg, e);
|
||||
throw new CloudRuntimeException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNexusVSM(Long clusterId) {
|
||||
ClusterVSMMapVO vsmMapVo = null;
|
||||
|
||||
@ -242,7 +242,6 @@ import com.cloud.hypervisor.vmware.mo.DatacenterMO;
|
||||
import com.cloud.hypervisor.vmware.mo.DatastoreFile;
|
||||
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
|
||||
import com.cloud.hypervisor.vmware.mo.DiskControllerType;
|
||||
import com.cloud.hypervisor.vmware.mo.DistributedVirtualSwitchMO;
|
||||
import com.cloud.hypervisor.vmware.mo.FeatureKeyConstants;
|
||||
import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HostMO;
|
||||
@ -312,23 +311,19 @@ import com.vmware.vim25.CustomFieldStringValue;
|
||||
import com.vmware.vim25.DVPortConfigInfo;
|
||||
import com.vmware.vim25.DVPortConfigSpec;
|
||||
import com.vmware.vim25.DasVmPriority;
|
||||
import com.vmware.vim25.DatastoreInfo;
|
||||
import com.vmware.vim25.DatastoreSummary;
|
||||
import com.vmware.vim25.DistributedVirtualPort;
|
||||
import com.vmware.vim25.DistributedVirtualSwitchPortConnection;
|
||||
import com.vmware.vim25.DistributedVirtualSwitchPortCriteria;
|
||||
import com.vmware.vim25.DynamicProperty;
|
||||
import com.vmware.vim25.GuestInfo;
|
||||
import com.vmware.vim25.GuestNicInfo;
|
||||
import com.vmware.vim25.GuestOsDescriptor;
|
||||
import com.vmware.vim25.HostCapability;
|
||||
import com.vmware.vim25.HostConfigInfo;
|
||||
import com.vmware.vim25.HostFileSystemMountInfo;
|
||||
import com.vmware.vim25.HostHostBusAdapter;
|
||||
import com.vmware.vim25.HostInternetScsiHba;
|
||||
import com.vmware.vim25.HostPortGroupSpec;
|
||||
import com.vmware.vim25.ManagedObjectReference;
|
||||
import com.vmware.vim25.NasDatastoreInfo;
|
||||
import com.vmware.vim25.ObjectContent;
|
||||
import com.vmware.vim25.OptionValue;
|
||||
import com.vmware.vim25.PerfCounterInfo;
|
||||
@ -353,14 +348,12 @@ import com.vmware.vim25.VirtualDevice;
|
||||
import com.vmware.vim25.VirtualDeviceBackingInfo;
|
||||
import com.vmware.vim25.VirtualDeviceConfigSpec;
|
||||
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
|
||||
import com.vmware.vim25.VirtualDeviceFileBackingInfo;
|
||||
import com.vmware.vim25.VirtualDisk;
|
||||
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
|
||||
import com.vmware.vim25.VirtualEthernetCard;
|
||||
import com.vmware.vim25.VirtualEthernetCardDistributedVirtualPortBackingInfo;
|
||||
import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo;
|
||||
import com.vmware.vim25.VirtualEthernetCardOpaqueNetworkBackingInfo;
|
||||
import com.vmware.vim25.VirtualIDEController;
|
||||
import com.vmware.vim25.VirtualMachineBootOptions;
|
||||
import com.vmware.vim25.VirtualMachineConfigSpec;
|
||||
import com.vmware.vim25.VirtualMachineDefinedProfileSpec;
|
||||
@ -374,14 +367,10 @@ import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator;
|
||||
import com.vmware.vim25.VirtualMachineRuntimeInfo;
|
||||
import com.vmware.vim25.VirtualMachineToolsStatus;
|
||||
import com.vmware.vim25.VirtualMachineVideoCard;
|
||||
import com.vmware.vim25.VirtualPCNet32;
|
||||
import com.vmware.vim25.VirtualSCSIController;
|
||||
import com.vmware.vim25.VirtualUSBController;
|
||||
import com.vmware.vim25.VirtualVmxnet2;
|
||||
import com.vmware.vim25.VirtualVmxnet3;
|
||||
import com.vmware.vim25.VmConfigInfo;
|
||||
import com.vmware.vim25.VmConfigSpec;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec;
|
||||
|
||||
public class VmwareResource extends ServerResourceBase implements StoragePoolResource, ServerResource, VmwareHostService, VirtualRouterDeployer {
|
||||
@ -453,10 +442,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
protected static final String s_relativePathSystemVmKeyFileInstallDir = "scripts/vm/systemvm/id_rsa.cloud";
|
||||
protected static final String s_defaultPathSystemVmKeyFile = "/usr/share/cloudstack-common/scripts/vm/systemvm/id_rsa.cloud";
|
||||
|
||||
public Gson getGson() {
|
||||
return _gson;
|
||||
}
|
||||
|
||||
public VmwareResource() {
|
||||
_gson = GsonHelper.getGsonLogger();
|
||||
}
|
||||
@ -987,7 +972,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
|
||||
// OfflineVmwareMigration: 5. ignore/replace the rest of the try-block; It is the functional bit
|
||||
VirtualDisk disk = getDiskAfterResizeDiskValidations(vmMo, path);
|
||||
String vmdkAbsFile = getAbsoluteVmdkFile(disk);
|
||||
String vmdkAbsFile = VmwareHelper.getAbsoluteVmdkFile(disk);
|
||||
|
||||
if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) {
|
||||
vmMo.updateAdapterTypeIfRequired(vmdkAbsFile);
|
||||
@ -4729,7 +4714,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
s_logger.debug(String.format("locating disk for volume (%d) using path %s", volumeId, volumePath));
|
||||
}
|
||||
Pair<VirtualDisk, String> diskInfo = getVirtualDiskInfo(vmMo, volumePath + VMDK_EXTENSION);
|
||||
String vmdkAbsFile = getAbsoluteVmdkFile(diskInfo.first());
|
||||
String vmdkAbsFile = VmwareHelper.getAbsoluteVmdkFile(diskInfo.first());
|
||||
if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) {
|
||||
vmMo.updateAdapterTypeIfRequired(vmdkAbsFile);
|
||||
}
|
||||
@ -4928,7 +4913,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
}
|
||||
|
||||
VirtualDisk disk = vdisk.first();
|
||||
String vmdkAbsFile = getAbsoluteVmdkFile(disk);
|
||||
String vmdkAbsFile = VmwareHelper.getAbsoluteVmdkFile(disk);
|
||||
if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) {
|
||||
vmMo.updateAdapterTypeIfRequired(vmdkAbsFile);
|
||||
}
|
||||
@ -5048,7 +5033,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
|
||||
String fullVolumePath = VmwareStorageLayoutHelper.getVmwareDatastorePathFromVmdkFileName(targetDsMo, vmName, volumePath + VMDK_EXTENSION);
|
||||
Pair<VirtualDisk, String> diskInfo = getVirtualDiskInfo(vmMo, appendFileType(volumePath, VMDK_EXTENSION));
|
||||
String vmdkAbsFile = getAbsoluteVmdkFile(diskInfo.first());
|
||||
String vmdkAbsFile = VmwareHelper.getAbsoluteVmdkFile(diskInfo.first());
|
||||
if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) {
|
||||
vmMo.updateAdapterTypeIfRequired(vmdkAbsFile);
|
||||
}
|
||||
@ -7105,16 +7090,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
return dcMo.findVm(vol.getPath());
|
||||
}
|
||||
|
||||
public String getAbsoluteVmdkFile(VirtualDisk disk) {
|
||||
String vmdkAbsFile = null;
|
||||
VirtualDeviceBackingInfo backingInfo = disk.getBacking();
|
||||
if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
|
||||
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo) backingInfo;
|
||||
vmdkAbsFile = diskBackingInfo.getFileName();
|
||||
}
|
||||
return vmdkAbsFile;
|
||||
}
|
||||
|
||||
protected File getSystemVmKeyFile() {
|
||||
if (s_systemVmKeyFile == null) {
|
||||
syncFetchSystemVmKeyFile();
|
||||
@ -7149,255 +7124,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
return keyFile;
|
||||
}
|
||||
|
||||
private List<UnmanagedInstanceTO.Disk> getUnmanageInstanceDisks(VirtualMachineMO vmMo) {
|
||||
List<UnmanagedInstanceTO.Disk> instanceDisks = new ArrayList<>();
|
||||
VirtualDisk[] disks = null;
|
||||
try {
|
||||
disks = vmMo.getAllDiskDevice();
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance disks. " + e.getMessage());
|
||||
}
|
||||
if (disks != null) {
|
||||
for (VirtualDevice diskDevice : disks) {
|
||||
try {
|
||||
if (diskDevice instanceof VirtualDisk) {
|
||||
UnmanagedInstanceTO.Disk instanceDisk = new UnmanagedInstanceTO.Disk();
|
||||
VirtualDisk disk = (VirtualDisk) diskDevice;
|
||||
instanceDisk.setDiskId(disk.getDiskObjectId());
|
||||
instanceDisk.setLabel(disk.getDeviceInfo() != null ? disk.getDeviceInfo().getLabel() : "");
|
||||
instanceDisk.setFileBaseName(vmMo.getVmdkFileBaseName(disk));
|
||||
instanceDisk.setImagePath(getAbsoluteVmdkFile(disk));
|
||||
instanceDisk.setCapacity(disk.getCapacityInBytes());
|
||||
instanceDisk.setPosition(diskDevice.getUnitNumber());
|
||||
DatastoreFile file = new DatastoreFile(getAbsoluteVmdkFile(disk));
|
||||
if (StringUtils.isNoneEmpty(file.getFileBaseName(), file.getDatastoreName())) {
|
||||
VirtualMachineDiskInfo diskInfo = vmMo.getDiskInfoBuilder().getDiskInfoByBackingFileBaseName(file.getFileBaseName(), file.getDatastoreName());
|
||||
instanceDisk.setChainInfo(getGson().toJson(diskInfo));
|
||||
}
|
||||
for (VirtualDevice device : vmMo.getAllDeviceList()) {
|
||||
if (diskDevice.getControllerKey() == device.getKey()) {
|
||||
if (device instanceof VirtualIDEController) {
|
||||
instanceDisk.setController(DiskControllerType.getType(device.getClass().getSimpleName()).toString());
|
||||
instanceDisk.setControllerUnit(((VirtualIDEController) device).getBusNumber());
|
||||
} else if (device instanceof VirtualSCSIController) {
|
||||
instanceDisk.setController(DiskControllerType.getType(device.getClass().getSimpleName()).toString());
|
||||
instanceDisk.setControllerUnit(((VirtualSCSIController) device).getBusNumber());
|
||||
} else {
|
||||
instanceDisk.setController(DiskControllerType.none.toString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (disk.getBacking() instanceof VirtualDeviceFileBackingInfo) {
|
||||
VirtualDeviceFileBackingInfo diskBacking = (VirtualDeviceFileBackingInfo) disk.getBacking();
|
||||
ManagedObjectReference morDs = diskBacking.getDatastore();
|
||||
DatastoreInfo info = (DatastoreInfo)vmMo.getContext().getVimClient().getDynamicProperty(diskBacking.getDatastore(), "info");
|
||||
if (info instanceof NasDatastoreInfo) {
|
||||
NasDatastoreInfo dsInfo = (NasDatastoreInfo) info;
|
||||
instanceDisk.setDatastoreName(dsInfo.getName());
|
||||
if (dsInfo.getNas() != null) {
|
||||
instanceDisk.setDatastoreHost(dsInfo.getNas().getRemoteHost());
|
||||
instanceDisk.setDatastorePath(dsInfo.getNas().getRemotePath());
|
||||
instanceDisk.setDatastoreType(dsInfo.getNas().getType());
|
||||
}
|
||||
} else {
|
||||
instanceDisk.setDatastoreName(info.getName());
|
||||
}
|
||||
}
|
||||
s_logger.info(vmMo.getName() + " " + disk.getDeviceInfo().getLabel() + " " + disk.getDeviceInfo().getSummary() + " " + disk.getDiskObjectId() + " " + disk.getCapacityInKB() + " " + instanceDisk.getController());
|
||||
instanceDisks.add(instanceDisk);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance disk info. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
Collections.sort(instanceDisks, new Comparator<UnmanagedInstanceTO.Disk>() {
|
||||
@Override
|
||||
public int compare(final UnmanagedInstanceTO.Disk disk1, final UnmanagedInstanceTO.Disk disk2) {
|
||||
return extractInt(disk1) - extractInt(disk2);
|
||||
}
|
||||
|
||||
int extractInt(UnmanagedInstanceTO.Disk disk) {
|
||||
String num = disk.getLabel().replaceAll("\\D", "");
|
||||
// return 0 if no digits found
|
||||
return num.isEmpty() ? 0 : Integer.parseInt(num);
|
||||
}
|
||||
});
|
||||
}
|
||||
return instanceDisks;
|
||||
}
|
||||
|
||||
private List<UnmanagedInstanceTO.Nic> getUnmanageInstanceNics(VmwareHypervisorHost hyperHost, VirtualMachineMO vmMo) {
|
||||
List<UnmanagedInstanceTO.Nic> instanceNics = new ArrayList<>();
|
||||
|
||||
HashMap<String, List<String>> guestNicMacIPAddressMap = new HashMap<>();
|
||||
try {
|
||||
GuestInfo guestInfo = vmMo.getGuestInfo();
|
||||
if (guestInfo.getToolsStatus() == VirtualMachineToolsStatus.TOOLS_OK) {
|
||||
for (GuestNicInfo nicInfo: guestInfo.getNet()) {
|
||||
if (CollectionUtils.isNotEmpty(nicInfo.getIpAddress())) {
|
||||
List<String> ipAddresses = new ArrayList<>();
|
||||
for (String ipAddress : nicInfo.getIpAddress()) {
|
||||
if (NetUtils.isValidIp4(ipAddress)) {
|
||||
ipAddresses.add(ipAddress);
|
||||
}
|
||||
}
|
||||
guestNicMacIPAddressMap.put(nicInfo.getMacAddress(), ipAddresses);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s_logger.info(String.format("Unable to retrieve guest nics for instance: %s from VMware tools as tools status: %s", vmMo.getName(), guestInfo.getToolsStatus().toString()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve guest nics for instance from VMware tools. " + e.getMessage());
|
||||
}
|
||||
VirtualDevice[] nics = null;
|
||||
try {
|
||||
nics = vmMo.getNicDevices();
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance nics. " + e.getMessage());
|
||||
}
|
||||
if (nics != null) {
|
||||
for (VirtualDevice nic : nics) {
|
||||
try {
|
||||
VirtualEthernetCard ethCardDevice = (VirtualEthernetCard) nic;
|
||||
s_logger.error(nic.getClass().getCanonicalName() + " " + nic.getBacking().getClass().getCanonicalName() + " " + ethCardDevice.getMacAddress());
|
||||
UnmanagedInstanceTO.Nic instanceNic = new UnmanagedInstanceTO.Nic();
|
||||
instanceNic.setNicId(ethCardDevice.getDeviceInfo().getLabel());
|
||||
if (ethCardDevice instanceof VirtualPCNet32) {
|
||||
instanceNic.setAdapterType(VirtualEthernetCardType.PCNet32.toString());
|
||||
} else if (ethCardDevice instanceof VirtualVmxnet2) {
|
||||
instanceNic.setAdapterType(VirtualEthernetCardType.Vmxnet2.toString());
|
||||
} else if (ethCardDevice instanceof VirtualVmxnet3) {
|
||||
instanceNic.setAdapterType(VirtualEthernetCardType.Vmxnet3.toString());
|
||||
} else {
|
||||
instanceNic.setAdapterType(VirtualEthernetCardType.E1000.toString());
|
||||
}
|
||||
instanceNic.setMacAddress(ethCardDevice.getMacAddress());
|
||||
if (guestNicMacIPAddressMap.containsKey(instanceNic.getMacAddress())) {
|
||||
instanceNic.setIpAddress(guestNicMacIPAddressMap.get(instanceNic.getMacAddress()));
|
||||
}
|
||||
if (ethCardDevice.getSlotInfo() != null) {
|
||||
instanceNic.setPciSlot(ethCardDevice.getSlotInfo().toString());
|
||||
}
|
||||
VirtualDeviceBackingInfo backing = ethCardDevice.getBacking();
|
||||
if (backing instanceof VirtualEthernetCardDistributedVirtualPortBackingInfo) {
|
||||
VirtualEthernetCardDistributedVirtualPortBackingInfo backingInfo = (VirtualEthernetCardDistributedVirtualPortBackingInfo) backing;
|
||||
DistributedVirtualSwitchPortConnection port = backingInfo.getPort();
|
||||
String portKey = port.getPortKey();
|
||||
String portGroupKey = port.getPortgroupKey();
|
||||
String dvSwitchUuid = port.getSwitchUuid();
|
||||
|
||||
s_logger.debug("NIC " + nic.toString() + " is connected to dvSwitch " + dvSwitchUuid + " pg " + portGroupKey + " port " + portKey);
|
||||
|
||||
ManagedObjectReference dvSwitchManager = vmMo.getContext().getVimClient().getServiceContent().getDvSwitchManager();
|
||||
ManagedObjectReference dvSwitch = vmMo.getContext().getVimClient().getService().queryDvsByUuid(dvSwitchManager, dvSwitchUuid);
|
||||
|
||||
// Get all ports
|
||||
DistributedVirtualSwitchPortCriteria criteria = new DistributedVirtualSwitchPortCriteria();
|
||||
criteria.setInside(true);
|
||||
criteria.getPortgroupKey().add(portGroupKey);
|
||||
List<DistributedVirtualPort> dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria);
|
||||
|
||||
for (DistributedVirtualPort dvPort : dvPorts) {
|
||||
// Find the port for this NIC by portkey
|
||||
if (portKey.equals(dvPort.getKey())) {
|
||||
VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPort.getConfig().getSetting();
|
||||
if (settings.getVlan() instanceof VmwareDistributedVirtualSwitchVlanIdSpec) {
|
||||
VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings.getVlan();
|
||||
s_logger.trace("Found port " + dvPort.getKey() + " with vlan " + vlanId.getVlanId());
|
||||
if (vlanId.getVlanId() > 0 && vlanId.getVlanId() < 4095) {
|
||||
instanceNic.setVlan(vlanId.getVlanId());
|
||||
}
|
||||
} else if (settings.getVlan() instanceof VmwareDistributedVirtualSwitchPvlanSpec) {
|
||||
VmwareDistributedVirtualSwitchPvlanSpec pvlanSpec = (VmwareDistributedVirtualSwitchPvlanSpec) settings.getVlan();
|
||||
s_logger.trace("Found port " + dvPort.getKey() + " with pvlan " + pvlanSpec.getPvlanId());
|
||||
if (pvlanSpec.getPvlanId() > 0 && pvlanSpec.getPvlanId() < 4095) {
|
||||
DistributedVirtualSwitchMO dvSwitchMo = new DistributedVirtualSwitchMO(vmMo.getContext(), dvSwitch);
|
||||
Pair<Integer, HypervisorHostHelper.PvlanType> vlanDetails = dvSwitchMo.retrieveVlanFromPvlan(pvlanSpec.getPvlanId(), dvSwitch);
|
||||
if (vlanDetails != null && vlanDetails.first() != null && vlanDetails.second() != null) {
|
||||
instanceNic.setVlan(vlanDetails.first());
|
||||
instanceNic.setPvlan(pvlanSpec.getPvlanId());
|
||||
instanceNic.setPvlanType(vlanDetails.second().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (backing instanceof VirtualEthernetCardNetworkBackingInfo) {
|
||||
VirtualEthernetCardNetworkBackingInfo backingInfo = (VirtualEthernetCardNetworkBackingInfo) backing;
|
||||
instanceNic.setNetwork(backingInfo.getDeviceName());
|
||||
if (hyperHost instanceof HostMO) {
|
||||
HostMO hostMo = (HostMO) hyperHost;
|
||||
HostPortGroupSpec portGroupSpec = hostMo.getHostPortGroupSpec(backingInfo.getDeviceName());
|
||||
instanceNic.setVlan(portGroupSpec.getVlanId());
|
||||
}
|
||||
}
|
||||
instanceNics.add(instanceNic);
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance nic info. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
Collections.sort(instanceNics, new Comparator<UnmanagedInstanceTO.Nic>() {
|
||||
@Override
|
||||
public int compare(final UnmanagedInstanceTO.Nic nic1, final UnmanagedInstanceTO.Nic nic2) {
|
||||
return extractInt(nic1) - extractInt(nic2);
|
||||
}
|
||||
|
||||
int extractInt(UnmanagedInstanceTO.Nic nic) {
|
||||
String num = nic.getNicId().replaceAll("\\D", "");
|
||||
// return 0 if no digits found
|
||||
return num.isEmpty() ? 0 : Integer.parseInt(num);
|
||||
}
|
||||
});
|
||||
}
|
||||
return instanceNics;
|
||||
}
|
||||
|
||||
private UnmanagedInstanceTO getUnmanagedInstance(VmwareHypervisorHost hyperHost, VirtualMachineMO vmMo) {
|
||||
UnmanagedInstanceTO instance = null;
|
||||
try {
|
||||
instance = new UnmanagedInstanceTO();
|
||||
instance.setName(vmMo.getVmName());
|
||||
instance.setInternalCSName(vmMo.getInternalCSName());
|
||||
instance.setCpuCores(vmMo.getConfigSummary().getNumCpu());
|
||||
instance.setCpuCoresPerSocket(vmMo.getCoresPerSocket());
|
||||
instance.setCpuSpeed(vmMo.getConfigSummary().getCpuReservation());
|
||||
instance.setMemory(vmMo.getConfigSummary().getMemorySizeMB());
|
||||
instance.setOperatingSystemId(vmMo.getVmGuestInfo().getGuestId());
|
||||
if (StringUtils.isEmpty(instance.getOperatingSystemId())) {
|
||||
instance.setOperatingSystemId(vmMo.getConfigSummary().getGuestId());
|
||||
}
|
||||
VirtualMachineGuestOsIdentifier osIdentifier = VirtualMachineGuestOsIdentifier.OTHER_GUEST;
|
||||
try {
|
||||
osIdentifier = VirtualMachineGuestOsIdentifier.fromValue(instance.getOperatingSystemId());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
if (StringUtils.isNotEmpty(instance.getOperatingSystemId()) && instance.getOperatingSystemId().contains("64")) {
|
||||
osIdentifier = VirtualMachineGuestOsIdentifier.OTHER_GUEST_64;
|
||||
}
|
||||
}
|
||||
instance.setOperatingSystem(vmMo.getGuestInfo().getGuestFullName());
|
||||
if (StringUtils.isEmpty(instance.getOperatingSystem())) {
|
||||
instance.setOperatingSystem(vmMo.getConfigSummary().getGuestFullName());
|
||||
}
|
||||
UnmanagedInstanceTO.PowerState powerState = UnmanagedInstanceTO.PowerState.PowerUnknown;
|
||||
if (vmMo.getPowerState().toString().equalsIgnoreCase("POWERED_ON")) {
|
||||
powerState = UnmanagedInstanceTO.PowerState.PowerOn;
|
||||
}
|
||||
if (vmMo.getPowerState().toString().equalsIgnoreCase("POWERED_OFF")) {
|
||||
powerState = UnmanagedInstanceTO.PowerState.PowerOff;
|
||||
}
|
||||
instance.setPowerState(powerState);
|
||||
instance.setDisks(getUnmanageInstanceDisks(vmMo));
|
||||
instance.setNics(getUnmanageInstanceNics(hyperHost, vmMo));
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance info. " + e.getMessage());
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Answer execute(GetUnmanagedInstancesCommand cmd) {
|
||||
VmwareContext context = getServiceContext();
|
||||
HashMap<String, UnmanagedInstanceTO> unmanagedInstances = new HashMap<>();
|
||||
@ -7426,7 +7152,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
!cmd.getInstanceName().equals(vmMo.getVmName())) {
|
||||
continue;
|
||||
}
|
||||
UnmanagedInstanceTO instance = getUnmanagedInstance(hyperHost, vmMo);
|
||||
UnmanagedInstanceTO instance = VmwareHelper.getUnmanagedInstance(hyperHost, vmMo);
|
||||
if (instance != null) {
|
||||
unmanagedInstances.put(instance.getName(), instance);
|
||||
}
|
||||
@ -7555,7 +7281,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
|
||||
VirtualMachineRelocateSpecDiskLocator diskLocator = new VirtualMachineRelocateSpecDiskLocator();
|
||||
diskLocator.setDatastore(morVolumeDatastore);
|
||||
Pair<VirtualDisk, String> diskInfo = getVirtualDiskInfo(vmMo, volume.getPath() + VMDK_EXTENSION);
|
||||
String vmdkAbsFile = getAbsoluteVmdkFile(diskInfo.first());
|
||||
String vmdkAbsFile = VmwareHelper.getAbsoluteVmdkFile(diskInfo.first());
|
||||
if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) {
|
||||
vmMo.updateAdapterTypeIfRequired(vmdkAbsFile);
|
||||
}
|
||||
|
||||
@ -2271,7 +2271,7 @@ public class VmwareStorageProcessor implements StorageProcessor {
|
||||
"Please re-try when virtual disk is attached to a VM using a SCSI controller.");
|
||||
}
|
||||
|
||||
String vmdkAbsFile = resource.getAbsoluteVmdkFile(vDisk);
|
||||
String vmdkAbsFile = VmwareHelper.getAbsoluteVmdkFile(vDisk);
|
||||
|
||||
if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) {
|
||||
vmMo.updateAdapterTypeIfRequired(vmdkAbsFile);
|
||||
|
||||
@ -33,7 +33,7 @@ import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import com.cloud.exception.DiscoveryException;
|
||||
import com.cloud.exception.ResourceInUseException;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterService;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
|
||||
@ -0,0 +1,139 @@
|
||||
// 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 org.apache.cloudstack.api.command.admin.zone;
|
||||
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.NetworkRuleConflictException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterService;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseListCmd;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.VmwareDatacenterResponse;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@APICommand(name = "listVmwareDcVms", responseObject = UnmanagedInstanceResponse.class,
|
||||
description = "Lists the VMs in a VMware Datacenter",
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class ListVmwareDcVmsCmd extends BaseListCmd {
|
||||
|
||||
@Inject
|
||||
public VmwareDatacenterService _vmwareDatacenterService;
|
||||
|
||||
@Parameter(name = ApiConstants.EXISTING_VCENTER_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = VmwareDatacenterResponse.class,
|
||||
description = "UUID of a linked existing vCenter")
|
||||
private Long existingVcenterId;
|
||||
|
||||
@Parameter(name = ApiConstants.VCENTER,
|
||||
type = CommandType.STRING,
|
||||
description = "The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.")
|
||||
private String vcenter;
|
||||
|
||||
@Parameter(name = ApiConstants.DATACENTER_NAME, type = CommandType.STRING, description = "Name of VMware datacenter.")
|
||||
private String datacenterName;
|
||||
|
||||
@Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "The Username required to connect to resource.")
|
||||
private String username;
|
||||
|
||||
@Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "The password for specified username.")
|
||||
private String password;
|
||||
|
||||
public String getVcenter() {
|
||||
return vcenter;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getDatacenterName() {
|
||||
return datacenterName;
|
||||
}
|
||||
|
||||
public Long getExistingVcenterId() {
|
||||
return existingVcenterId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
checkParameters();
|
||||
try {
|
||||
List<UnmanagedInstanceTO> vms = _vmwareDatacenterService.listVMsInDatacenter(this);
|
||||
List<BaseResponse> baseResponseList = new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(vms)) {
|
||||
for (UnmanagedInstanceTO vmwareVm : vms) {
|
||||
UnmanagedInstanceResponse resp = _responseGenerator.createUnmanagedInstanceResponse(vmwareVm, null, null);
|
||||
baseResponseList.add(resp);
|
||||
}
|
||||
}
|
||||
List<BaseResponse> pagingList = com.cloud.utils.StringUtils.applyPagination(baseResponseList, this.getStartIndex(), this.getPageSizeVal());
|
||||
if (CollectionUtils.isEmpty(pagingList)) {
|
||||
pagingList = baseResponseList;
|
||||
}
|
||||
ListResponse<BaseResponse> response = new ListResponse<>();
|
||||
response.setResponses(pagingList, baseResponseList.size());
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (CloudRuntimeException e) {
|
||||
String errorMsg = String.format("Error retrieving VMs from VMware VC: %s", e.getMessage());
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkParameters() {
|
||||
if ((existingVcenterId == null && vcenter == null) || (existingVcenterId != null && vcenter != null)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
|
||||
"Please provide an existing vCenter ID or a vCenter IP/Name, parameters are mutually exclusive");
|
||||
}
|
||||
if (existingVcenterId == null && StringUtils.isAnyBlank(vcenter, datacenterName, username, password)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
|
||||
"Please set all the information for a vCenter IP/Name, datacenter, username and password");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "listvmwaredcvmsresponse";
|
||||
}
|
||||
}
|
||||
@ -39,7 +39,7 @@ import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenter;
|
||||
import com.cloud.dc.VmwareDatacenter;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterService;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ import org.apache.cloudstack.api.response.VmwareDatacenterResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenter;
|
||||
import com.cloud.dc.VmwareDatacenter;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterService;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
|
||||
@ -31,8 +31,6 @@
|
||||
class="com.cloud.hypervisor.vmware.manager.VmwareManagerImpl" />
|
||||
<bean id="vmwareContextFactory"
|
||||
class="com.cloud.hypervisor.vmware.resource.VmwareContextFactory" />
|
||||
<bean id="VmwareDatacenterDaoImpl"
|
||||
class="com.cloud.hypervisor.vmware.dao.VmwareDatacenterDaoImpl" />
|
||||
<bean id="VmwareDatacenterZoneMapDaoImpl"
|
||||
class="com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDaoImpl" />
|
||||
<bean id="LegacyZoneDaoImpl" class="com.cloud.hypervisor.vmware.dao.LegacyZoneDaoImpl" />
|
||||
|
||||
@ -27,6 +27,7 @@ import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.DataCenter.NetworkType;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.HostPodVO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.ClusterVSMMapDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
@ -42,7 +43,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.HypervisorGuruManager;
|
||||
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
||||
import com.cloud.hypervisor.vmware.dao.LegacyZoneDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
|
||||
import com.cloud.hypervisor.vmware.manager.VmwareManagerImpl;
|
||||
import com.cloud.network.NetworkModel;
|
||||
|
||||
@ -38,10 +38,10 @@ import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.host.dao.HostDetailsDao;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenter;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
|
||||
import com.cloud.dc.VmwareDatacenter;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
|
||||
@ -164,6 +164,8 @@ import org.apache.cloudstack.api.response.TemplatePermissionsResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.TrafficMonitorResponse;
|
||||
import org.apache.cloudstack.api.response.TrafficTypeResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceDiskResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.UpgradeRouterTemplateResponse;
|
||||
import org.apache.cloudstack.api.response.UsageRecordResponse;
|
||||
import org.apache.cloudstack.api.response.UserDataResponse;
|
||||
@ -213,6 +215,7 @@ import org.apache.cloudstack.storage.object.ObjectStore;
|
||||
import org.apache.cloudstack.usage.Usage;
|
||||
import org.apache.cloudstack.usage.UsageService;
|
||||
import org.apache.cloudstack.usage.UsageTypes;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -5107,6 +5110,72 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnmanagedInstanceResponse createUnmanagedInstanceResponse(UnmanagedInstanceTO instance, Cluster cluster, Host host) {
|
||||
UnmanagedInstanceResponse response = new UnmanagedInstanceResponse();
|
||||
response.setName(instance.getName());
|
||||
if (cluster != null) {
|
||||
response.setClusterId(cluster.getUuid());
|
||||
response.setClusterName(cluster.getName());
|
||||
} else if (instance.getClusterName() != null) {
|
||||
response.setClusterName(instance.getClusterName());
|
||||
}
|
||||
if (host != null) {
|
||||
response.setHostId(host.getUuid());
|
||||
response.setHostName(host.getName());
|
||||
} else if (instance.getHostName() != null) {
|
||||
response.setHostName(instance.getHostName());
|
||||
}
|
||||
response.setPowerState(instance.getPowerState().toString());
|
||||
response.setCpuCores(instance.getCpuCores());
|
||||
response.setCpuSpeed(instance.getCpuSpeed());
|
||||
response.setCpuCoresPerSocket(instance.getCpuCoresPerSocket());
|
||||
response.setMemory(instance.getMemory());
|
||||
response.setOperatingSystemId(instance.getOperatingSystemId());
|
||||
response.setOperatingSystem(instance.getOperatingSystem());
|
||||
response.setObjectName("unmanagedinstance");
|
||||
|
||||
if (instance.getDisks() != null) {
|
||||
for (UnmanagedInstanceTO.Disk disk : instance.getDisks()) {
|
||||
UnmanagedInstanceDiskResponse diskResponse = new UnmanagedInstanceDiskResponse();
|
||||
diskResponse.setDiskId(disk.getDiskId());
|
||||
if (StringUtils.isNotEmpty(disk.getLabel())) {
|
||||
diskResponse.setLabel(disk.getLabel());
|
||||
}
|
||||
diskResponse.setCapacity(disk.getCapacity());
|
||||
diskResponse.setController(disk.getController());
|
||||
diskResponse.setControllerUnit(disk.getControllerUnit());
|
||||
diskResponse.setPosition(disk.getPosition());
|
||||
diskResponse.setImagePath(disk.getImagePath());
|
||||
diskResponse.setDatastoreName(disk.getDatastoreName());
|
||||
diskResponse.setDatastoreHost(disk.getDatastoreHost());
|
||||
diskResponse.setDatastorePath(disk.getDatastorePath());
|
||||
diskResponse.setDatastoreType(disk.getDatastoreType());
|
||||
response.addDisk(diskResponse);
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.getNics() != null) {
|
||||
for (UnmanagedInstanceTO.Nic nic : instance.getNics()) {
|
||||
NicResponse nicResponse = new NicResponse();
|
||||
nicResponse.setId(nic.getNicId());
|
||||
nicResponse.setNetworkName(nic.getNetwork());
|
||||
nicResponse.setMacAddress(nic.getMacAddress());
|
||||
if (StringUtils.isNotEmpty(nic.getAdapterType())) {
|
||||
nicResponse.setAdapterType(nic.getAdapterType());
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(nic.getIpAddress())) {
|
||||
nicResponse.setIpAddresses(nic.getIpAddress());
|
||||
}
|
||||
nicResponse.setVlanId(nic.getVlan());
|
||||
nicResponse.setIsolatedPvlanId(nic.getPvlan());
|
||||
nicResponse.setIsolatedPvlanType(nic.getPvlanType());
|
||||
response.addNic(nicResponse);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecondaryStorageHeuristicsResponse createSecondaryStorageSelectorResponse(Heuristic heuristic) {
|
||||
String zoneUuid = ApiDBUtils.findZoneById(heuristic.getZoneId()).getUuid();
|
||||
|
||||
@ -28,6 +28,7 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationSe
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -366,4 +367,15 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
|
||||
s_logger.error("Unsupported operation: cannot clone external VM");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
|
||||
s_logger.error("Unsupported operation: cannot remove external VM");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3673,7 +3673,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
|
||||
SecStorageVMAutoScaleDown,
|
||||
MountDisabledStoragePool,
|
||||
VmwareCreateCloneFull,
|
||||
VmwareAllowParallelExecution
|
||||
VmwareAllowParallelExecution,
|
||||
ConvertVmwareInstanceToKvmTimeout
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -4487,17 +4487,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
vm.setDisplayVm(true);
|
||||
}
|
||||
|
||||
if (isImport) {
|
||||
vm.setDataCenterId(zone.getId());
|
||||
vm.setHostId(host.getId());
|
||||
if (lastHost != null) {
|
||||
vm.setLastHostId(lastHost.getId());
|
||||
}
|
||||
vm.setPowerState(powerState);
|
||||
if (powerState == VirtualMachine.PowerState.PowerOn) {
|
||||
vm.setState(State.Running);
|
||||
}
|
||||
}
|
||||
setVmRequiredFieldsForImport(isImport, vm, zone, hypervisorType, host, lastHost, powerState);
|
||||
|
||||
vm.setUserVmType(vmType);
|
||||
_vmDao.persist(vm);
|
||||
@ -4585,6 +4575,23 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
return vm;
|
||||
}
|
||||
|
||||
protected void setVmRequiredFieldsForImport(boolean isImport, UserVmVO vm, DataCenter zone, HypervisorType hypervisorType,
|
||||
Host host, Host lastHost, VirtualMachine.PowerState powerState) {
|
||||
if (isImport) {
|
||||
vm.setDataCenterId(zone.getId());
|
||||
if (hypervisorType == HypervisorType.VMware) {
|
||||
vm.setHostId(host.getId());
|
||||
}
|
||||
if (lastHost != null) {
|
||||
vm.setLastHostId(lastHost.getId());
|
||||
}
|
||||
vm.setPowerState(powerState);
|
||||
if (powerState == VirtualMachine.PowerState.PowerOn) {
|
||||
vm.setState(State.Running);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVMDiskController(UserVmVO vm, Map<String, String> customParameters, GuestOSVO guestOS) {
|
||||
// If hypervisor is vSphere and OS is OS X, set special settings.
|
||||
if (guestOS.getDisplayName().toLowerCase().contains("apple mac os")) {
|
||||
@ -8204,7 +8211,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
if (zone == null) {
|
||||
throw new InvalidParameterValueException("Unable to import virtual machine with invalid zone");
|
||||
}
|
||||
if (host == null) {
|
||||
if (host == null && hypervisorType == HypervisorType.VMware) {
|
||||
throw new InvalidParameterValueException("Unable to import virtual machine with invalid host");
|
||||
}
|
||||
|
||||
|
||||
@ -18,32 +18,58 @@
|
||||
package org.apache.cloudstack.vm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.agent.api.ConvertInstanceAnswer;
|
||||
import com.cloud.agent.api.ConvertInstanceCommand;
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.agent.api.to.RemoteInstanceTO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.event.EventVO;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.hypervisor.HypervisorGuru;
|
||||
import com.cloud.hypervisor.HypervisorGuruManager;
|
||||
import com.cloud.resource.ResourceState;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.ScopeType;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.StorageManager;
|
||||
import com.cloud.storage.dao.StoragePoolHostDao;
|
||||
import com.cloud.vm.VirtualMachineName;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.ResponseGenerator;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportVmCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.UnmanageVMInstanceCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.NicResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceDiskResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
|
||||
@ -77,7 +103,6 @@ import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.UnsupportedServiceException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.Status;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
@ -143,6 +168,8 @@ import com.google.gson.Gson;
|
||||
public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
public static final String VM_IMPORT_DEFAULT_TEMPLATE_NAME = "system-default-vm-import-dummy-template.iso";
|
||||
private static final Logger LOGGER = Logger.getLogger(UnmanagedVMsManagerImpl.class);
|
||||
private static final List<Hypervisor.HypervisorType> importUnmanagedInstancesSupportedHypervisors =
|
||||
Arrays.asList(Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.KVM);
|
||||
|
||||
@Inject
|
||||
private AgentManager agentManager;
|
||||
@ -206,6 +233,16 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
private SnapshotDao snapshotDao;
|
||||
@Inject
|
||||
private UserVmDao userVmDao;
|
||||
@Inject
|
||||
private HypervisorGuruManager hypervisorGuruManager;
|
||||
@Inject
|
||||
private VmwareDatacenterDao vmwareDatacenterDao;
|
||||
@Inject
|
||||
private ImageStoreDao imageStoreDao;
|
||||
@Inject
|
||||
private StoragePoolHostDao storagePoolHostDao;
|
||||
@Inject
|
||||
private DataStoreManager dataStoreManager;
|
||||
|
||||
protected Gson gson;
|
||||
|
||||
@ -232,66 +269,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
return template;
|
||||
}
|
||||
|
||||
private UnmanagedInstanceResponse createUnmanagedInstanceResponse(UnmanagedInstanceTO instance, Cluster cluster, Host host) {
|
||||
UnmanagedInstanceResponse response = new UnmanagedInstanceResponse();
|
||||
response.setName(instance.getName());
|
||||
if (cluster != null) {
|
||||
response.setClusterId(cluster.getUuid());
|
||||
}
|
||||
if (host != null) {
|
||||
response.setHostId(host.getUuid());
|
||||
response.setHostName(host.getName());
|
||||
}
|
||||
response.setPowerState(instance.getPowerState().toString());
|
||||
response.setCpuCores(instance.getCpuCores());
|
||||
response.setCpuSpeed(instance.getCpuSpeed());
|
||||
response.setCpuCoresPerSocket(instance.getCpuCoresPerSocket());
|
||||
response.setMemory(instance.getMemory());
|
||||
response.setOperatingSystemId(instance.getOperatingSystemId());
|
||||
response.setOperatingSystem(instance.getOperatingSystem());
|
||||
response.setObjectName("unmanagedinstance");
|
||||
|
||||
if (instance.getDisks() != null) {
|
||||
for (UnmanagedInstanceTO.Disk disk : instance.getDisks()) {
|
||||
UnmanagedInstanceDiskResponse diskResponse = new UnmanagedInstanceDiskResponse();
|
||||
diskResponse.setDiskId(disk.getDiskId());
|
||||
if (StringUtils.isNotEmpty(disk.getLabel())) {
|
||||
diskResponse.setLabel(disk.getLabel());
|
||||
}
|
||||
diskResponse.setCapacity(disk.getCapacity());
|
||||
diskResponse.setController(disk.getController());
|
||||
diskResponse.setControllerUnit(disk.getControllerUnit());
|
||||
diskResponse.setPosition(disk.getPosition());
|
||||
diskResponse.setImagePath(disk.getImagePath());
|
||||
diskResponse.setDatastoreName(disk.getDatastoreName());
|
||||
diskResponse.setDatastoreHost(disk.getDatastoreHost());
|
||||
diskResponse.setDatastorePath(disk.getDatastorePath());
|
||||
diskResponse.setDatastoreType(disk.getDatastoreType());
|
||||
response.addDisk(diskResponse);
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.getNics() != null) {
|
||||
for (UnmanagedInstanceTO.Nic nic : instance.getNics()) {
|
||||
NicResponse nicResponse = new NicResponse();
|
||||
nicResponse.setId(nic.getNicId());
|
||||
nicResponse.setNetworkName(nic.getNetwork());
|
||||
nicResponse.setMacAddress(nic.getMacAddress());
|
||||
if (StringUtils.isNotEmpty(nic.getAdapterType())) {
|
||||
nicResponse.setAdapterType(nic.getAdapterType());
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(nic.getIpAddress())) {
|
||||
nicResponse.setIpAddresses(nic.getIpAddress());
|
||||
}
|
||||
nicResponse.setVlanId(nic.getVlan());
|
||||
nicResponse.setIsolatedPvlanId(nic.getPvlan());
|
||||
nicResponse.setIsolatedPvlanType(nic.getPvlanType());
|
||||
response.addNic(nicResponse);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private List<String> getAdditionalNameFilters(Cluster cluster) {
|
||||
List<String> additionalNameFilter = new ArrayList<>();
|
||||
if (cluster == null) {
|
||||
@ -565,7 +542,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkUnmanagedNicAndNetworkForImport(String instanceName, UnmanagedInstanceTO.Nic nic, Network network, final DataCenter zone, final Account owner, final boolean autoAssign) throws ServerApiException {
|
||||
private void checkUnmanagedNicAndNetworkForImport(String instanceName, UnmanagedInstanceTO.Nic nic, Network network, final DataCenter zone, final Account owner, final boolean autoAssign, Hypervisor.HypervisorType hypervisorType) throws ServerApiException {
|
||||
basicNetworkChecks(instanceName, nic, network);
|
||||
if (network.getDataCenterId() != zone.getId()) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Network(ID: %s) for nic(ID: %s) belongs to a different zone than VM to be imported", network.getUuid(), nic.getNicId()));
|
||||
@ -575,16 +552,18 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
return;
|
||||
}
|
||||
|
||||
String networkBroadcastUri = network.getBroadcastUri() == null ? null : network.getBroadcastUri().toString();
|
||||
if (nic.getVlan() != null && nic.getVlan() != 0 && nic.getPvlan() == null &&
|
||||
(StringUtils.isEmpty(networkBroadcastUri) ||
|
||||
!networkBroadcastUri.equals(String.format("vlan://%d", nic.getVlan())))) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VLAN of network(ID: %s) %s is found different from the VLAN of nic(ID: %s) vlan://%d during VM import", network.getUuid(), networkBroadcastUri, nic.getNicId(), nic.getVlan()));
|
||||
}
|
||||
String pvLanType = nic.getPvlanType() == null ? "" : nic.getPvlanType().toLowerCase().substring(0, 1);
|
||||
if (nic.getVlan() != null && nic.getVlan() != 0 && nic.getPvlan() != null && nic.getPvlan() != 0 &&
|
||||
(StringUtils.isEmpty(networkBroadcastUri) || !String.format("pvlan://%d-%s%d", nic.getVlan(), pvLanType, nic.getPvlan()).equals(networkBroadcastUri))) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("PVLAN of network(ID: %s) %s is found different from the VLAN of nic(ID: %s) pvlan://%d-%s%d during VM import", network.getUuid(), networkBroadcastUri, nic.getNicId(), nic.getVlan(), pvLanType, nic.getPvlan()));
|
||||
if (hypervisorType == Hypervisor.HypervisorType.VMware) {
|
||||
String networkBroadcastUri = network.getBroadcastUri() == null ? null : network.getBroadcastUri().toString();
|
||||
if (nic.getVlan() != null && nic.getVlan() != 0 && nic.getPvlan() == null &&
|
||||
(StringUtils.isEmpty(networkBroadcastUri) ||
|
||||
!networkBroadcastUri.equals(String.format("vlan://%d", nic.getVlan())))) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VLAN of network(ID: %s) %s is found different from the VLAN of nic(ID: %s) vlan://%d during VM import", network.getUuid(), networkBroadcastUri, nic.getNicId(), nic.getVlan()));
|
||||
}
|
||||
String pvLanType = nic.getPvlanType() == null ? "" : nic.getPvlanType().toLowerCase().substring(0, 1);
|
||||
if (nic.getVlan() != null && nic.getVlan() != 0 && nic.getPvlan() != null && nic.getPvlan() != 0 &&
|
||||
(StringUtils.isEmpty(networkBroadcastUri) || !String.format("pvlan://%d-%s%d", nic.getVlan(), pvLanType, nic.getPvlan()).equals(networkBroadcastUri))) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("PVLAN of network(ID: %s) %s is found different from the VLAN of nic(ID: %s) pvlan://%d-%s%d during VM import", network.getUuid(), networkBroadcastUri, nic.getNicId(), nic.getVlan(), pvLanType, nic.getPvlan()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -621,10 +600,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Long> getUnmanagedNicNetworkMap(String instanceName, List<UnmanagedInstanceTO.Nic> nics, final Map<String, Long> callerNicNetworkMap, final Map<String, Network.IpAddresses> callerNicIpAddressMap, final DataCenter zone, final String hostName, final Account owner) throws ServerApiException {
|
||||
private Map<String, Long> getUnmanagedNicNetworkMap(String instanceName, List<UnmanagedInstanceTO.Nic> nics, final Map<String, Long> callerNicNetworkMap, final Map<String, Network.IpAddresses> callerNicIpAddressMap, final DataCenter zone, final String hostName, final Account owner, Hypervisor.HypervisorType hypervisorType) throws ServerApiException {
|
||||
Map<String, Long> nicNetworkMap = new HashMap<>();
|
||||
String nicAdapter = null;
|
||||
for (UnmanagedInstanceTO.Nic nic : nics) {
|
||||
for (int i = 0; i < nics.size(); i++) {
|
||||
UnmanagedInstanceTO.Nic nic = nics.get(i);
|
||||
if (StringUtils.isEmpty(nicAdapter)) {
|
||||
nicAdapter = nic.getAdapterType();
|
||||
} else {
|
||||
@ -646,10 +626,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
checkUnmanagedNicAndNetworkForImport(instanceName, nic, networkVO, zone, owner, true);
|
||||
checkUnmanagedNicAndNetworkForImport(instanceName, nic, networkVO, zone, owner, true, hypervisorType);
|
||||
network = networkVO;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(String.format("Error when checking NIC [%s] of unmanaged instance to import due to [%s]." , nic.getNicId(), e.getMessage()), e);
|
||||
LOGGER.error(String.format("Error when checking NIC [%s] of unmanaged instance to import due to [%s].", nic.getNicId(), e.getMessage()), e);
|
||||
}
|
||||
if (network != null) {
|
||||
checkUnmanagedNicAndNetworkHostnameForImport(instanceName, nic, network, hostName);
|
||||
@ -660,7 +640,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
}
|
||||
} else {
|
||||
network = networkDao.findById(callerNicNetworkMap.get(nic.getNicId()));
|
||||
checkUnmanagedNicAndNetworkForImport(instanceName, nic, network, zone, owner, false);
|
||||
boolean autoImport = false;
|
||||
if (hypervisorType == Hypervisor.HypervisorType.KVM) {
|
||||
autoImport = ipAddresses != null && ipAddresses.getIp4Address() != null && ipAddresses.getIp4Address().equalsIgnoreCase("auto");
|
||||
}
|
||||
checkUnmanagedNicAndNetworkForImport(instanceName, nic, network, zone, owner, autoImport, hypervisorType);
|
||||
checkUnmanagedNicAndNetworkHostnameForImport(instanceName, nic, network, hostName);
|
||||
checkUnmanagedNicIpAndNetworkForImport(instanceName, nic, network, ipAddresses);
|
||||
}
|
||||
@ -678,7 +662,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
final DataCenter zone = dataCenterDao.findById(vm.getDataCenterId());
|
||||
final String path = StringUtils.isEmpty(disk.getFileBaseName()) ? disk.getImagePath() : disk.getFileBaseName();
|
||||
String chainInfo = disk.getChainInfo();
|
||||
if (StringUtils.isEmpty(chainInfo)) {
|
||||
if (vm.getHypervisorType() == Hypervisor.HypervisorType.VMware && StringUtils.isEmpty(chainInfo)) {
|
||||
VirtualMachineDiskInfo diskInfo = new VirtualMachineDiskInfo();
|
||||
diskInfo.setDiskDeviceBusName(String.format("%s%d:%d", disk.getController(), disk.getControllerUnit(), disk.getPosition()));
|
||||
diskInfo.setDiskChain(new String[]{disk.getImagePath()});
|
||||
@ -927,7 +911,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (!migrateAllowed && !hostSupportsServiceOffering(host, validatedServiceOffering)) {
|
||||
if (!migrateAllowed && host != null && !hostSupportsServiceOffering(host, validatedServiceOffering)) {
|
||||
throw new InvalidParameterValueException(String.format("Service offering: %s is not compatible with host: %s of unmanaged VM: %s", serviceOffering.getUuid(), host.getUuid(), instanceName));
|
||||
}
|
||||
// Check disks and supplied disk offerings
|
||||
@ -955,7 +939,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
}
|
||||
// Check NICs and supplied networks
|
||||
Map<String, Network.IpAddresses> nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap);
|
||||
Map<String, Long> allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner);
|
||||
Map<String, Long> allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, cluster.getHypervisorType());
|
||||
if (!CollectionUtils.isEmpty(unmanagedInstance.getNics())) {
|
||||
allDetails.put(VmDetailConstants.NIC_ADAPTER, unmanagedInstance.getNics().get(0).getAdapterType());
|
||||
}
|
||||
@ -1047,7 +1031,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
return unmanagedInstances;
|
||||
}
|
||||
|
||||
private Cluster basicAccessChecks(Long clusterId) {
|
||||
protected Cluster basicAccessChecks(Long clusterId) {
|
||||
final Account caller = CallContext.current().getCallingAccount();
|
||||
if (caller.getType() != Account.Type.ADMIN) {
|
||||
throw new PermissionDeniedException(String.format("Cannot perform this operation, caller account [%s] is not ROOT Admin.", caller.getUuid()));
|
||||
@ -1059,7 +1043,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
if (cluster == null) {
|
||||
throw new InvalidParameterValueException(String.format("Cluster with ID [%d] cannot be found.", clusterId));
|
||||
}
|
||||
if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
|
||||
if (!importUnmanagedInstancesSupportedHypervisors.contains(cluster.getHypervisorType())) {
|
||||
throw new InvalidParameterValueException(String.format("VM import is currently not supported for hypervisor [%s].", cluster.getHypervisorType().toString()));
|
||||
}
|
||||
return cluster;
|
||||
@ -1069,7 +1053,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
public ListResponse<UnmanagedInstanceResponse> listUnmanagedInstances(ListUnmanagedInstancesCmd cmd) {
|
||||
Long clusterId = cmd.getClusterId();
|
||||
Cluster cluster = basicAccessChecks(clusterId);
|
||||
|
||||
String keyword = cmd.getKeyword();
|
||||
if (StringUtils.isNotEmpty(keyword)) {
|
||||
keyword = keyword.toLowerCase();
|
||||
@ -1088,7 +1071,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
!instance.getName().toLowerCase().contains(keyword)) {
|
||||
continue;
|
||||
}
|
||||
responses.add(createUnmanagedInstanceResponse(instance, cluster, host));
|
||||
responses.add(responseGenerator.createUnmanagedInstanceResponse(instance, cluster, host));
|
||||
}
|
||||
}
|
||||
ListResponse<UnmanagedInstanceResponse> listResponses = new ListResponse<>();
|
||||
@ -1098,78 +1081,36 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
|
||||
@Override
|
||||
public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) {
|
||||
return baseImportInstance(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base logic for import virtual machines (unmanaged, external) into CloudStack
|
||||
* @param cmd importVM or importUnmanagedInstance command
|
||||
* @return imported user vm
|
||||
*/
|
||||
private UserVmResponse baseImportInstance(ImportUnmanagedInstanceCmd cmd) {
|
||||
basicParametersCheckForImportInstance(cmd.getName(), cmd.getDomainId(), cmd.getAccountName());
|
||||
|
||||
final String instanceName = cmd.getName();
|
||||
Long clusterId = cmd.getClusterId();
|
||||
Cluster cluster = basicAccessChecks(clusterId);
|
||||
|
||||
final Account caller = CallContext.current().getCallingAccount();
|
||||
final DataCenter zone = dataCenterDao.findById(cluster.getDataCenterId());
|
||||
final String instanceName = cmd.getName();
|
||||
if (StringUtils.isEmpty(instanceName)) {
|
||||
throw new InvalidParameterValueException("Instance name cannot be empty");
|
||||
}
|
||||
if (cmd.getDomainId() != null && StringUtils.isEmpty(cmd.getAccountName())) {
|
||||
throw new InvalidParameterValueException(String.format("%s parameter must be specified with %s parameter", ApiConstants.DOMAIN_ID, ApiConstants.ACCOUNT));
|
||||
}
|
||||
final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId());
|
||||
long userId = CallContext.current().getCallingUserId();
|
||||
List<UserVO> userVOs = userDao.listByAccount(owner.getAccountId());
|
||||
if (CollectionUtils.isNotEmpty(userVOs)) {
|
||||
userId = userVOs.get(0).getId();
|
||||
}
|
||||
VMTemplateVO template;
|
||||
final Long templateId = cmd.getTemplateId();
|
||||
if (templateId == null) {
|
||||
template = templateDao.findByName(VM_IMPORT_DEFAULT_TEMPLATE_NAME);
|
||||
if (template == null) {
|
||||
template = createDefaultDummyVmImportTemplate();
|
||||
if (template == null) {
|
||||
throw new InvalidParameterValueException(String.format("Default VM import template with unique name: %s for hypervisor: %s cannot be created. Please use templateid parameter for import", VM_IMPORT_DEFAULT_TEMPLATE_NAME, cluster.getHypervisorType().toString()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
template = templateDao.findById(templateId);
|
||||
}
|
||||
if (template == null) {
|
||||
throw new InvalidParameterValueException(String.format("Template ID: %d cannot be found", templateId));
|
||||
}
|
||||
final Long serviceOfferingId = cmd.getServiceOfferingId();
|
||||
if (serviceOfferingId == null) {
|
||||
throw new InvalidParameterValueException("Service offering ID cannot be null");
|
||||
}
|
||||
final ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(serviceOfferingId);
|
||||
if (serviceOffering == null) {
|
||||
throw new InvalidParameterValueException(String.format("Service offering ID: %d cannot be found", serviceOfferingId));
|
||||
}
|
||||
accountService.checkAccess(owner, serviceOffering, zone);
|
||||
try {
|
||||
resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.user_vm, 1);
|
||||
} catch (ResourceAllocationException e) {
|
||||
LOGGER.error(String.format("VM resource allocation error for account: %s", owner.getUuid()), e);
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage())));
|
||||
}
|
||||
String displayName = cmd.getDisplayName();
|
||||
if (StringUtils.isEmpty(displayName)) {
|
||||
displayName = instanceName;
|
||||
}
|
||||
String hostName = cmd.getHostName();
|
||||
if (StringUtils.isEmpty(hostName)) {
|
||||
if (!NetUtils.verifyDomainNameLabel(instanceName, true)) {
|
||||
throw new InvalidParameterValueException("Please provide a valid hostname for the VM. VM name contains unsupported characters that cannot be used as hostname.");
|
||||
}
|
||||
hostName = instanceName;
|
||||
}
|
||||
if (!NetUtils.verifyDomainNameLabel(hostName, true)) {
|
||||
throw new InvalidParameterValueException("Invalid VM hostname. VM hostname can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
|
||||
+ "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit");
|
||||
}
|
||||
if (cluster.getHypervisorType().equals(Hypervisor.HypervisorType.VMware) &&
|
||||
Boolean.parseBoolean(configurationDao.getValue(Config.SetVmInternalNameUsingDisplayName.key()))) {
|
||||
// If global config vm.instancename.flag is set to true, then CS will set guest VM's name as it appears on the hypervisor, to its hostname.
|
||||
// In case of VMware since VM name must be unique within a DC, check if VM with the same hostname already exists in the zone.
|
||||
VMInstanceVO vmByHostName = vmDao.findVMByHostNameInZone(hostName, zone.getId());
|
||||
if (vmByHostName != null && vmByHostName.getState() != VirtualMachine.State.Expunging) {
|
||||
throw new InvalidParameterValueException(String.format("Failed to import VM: %s. There already exists a VM by the hostname: %s in zone: %s", instanceName, hostName, zone.getUuid()));
|
||||
}
|
||||
}
|
||||
long userId = getUserIdForImportInstance(owner);
|
||||
|
||||
VMTemplateVO template = getTemplateForImportInstance(cmd.getTemplateId(), cluster.getHypervisorType());
|
||||
ServiceOfferingVO serviceOffering = getServiceOfferingForImportInstance(cmd.getServiceOfferingId(), owner, zone);
|
||||
|
||||
checkResourceLimitForImportInstance(owner);
|
||||
|
||||
String displayName = getDisplayNameForImportInstance(cmd.getDisplayName(), instanceName);
|
||||
String hostName = getHostNameForImportInstance(cmd.getHostName(), cluster.getHypervisorType(), instanceName, displayName);
|
||||
|
||||
checkVmwareInstanceNameForImportInstance(cluster.getHypervisorType(), instanceName, hostName, zone);
|
||||
|
||||
final Map<String, Long> nicNetworkMap = cmd.getNicNetworkList();
|
||||
final Map<String, Network.IpAddresses> nicIpAddressMap = cmd.getNicIpAddressList();
|
||||
final Map<String, Long> dataDiskOfferingMap = cmd.getDataDiskToDiskOfferingList();
|
||||
@ -1180,8 +1121,150 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
List<String> additionalNameFilters = getAdditionalNameFilters(cluster);
|
||||
List<String> managedVms = new ArrayList<>(additionalNameFilters);
|
||||
managedVms.addAll(getHostsManagedVms(hosts));
|
||||
|
||||
ActionEventUtils.onStartedActionEvent(userId, owner.getId(), EventTypes.EVENT_VM_IMPORT,
|
||||
cmd.getEventDescription(), null, null, true, 0);
|
||||
|
||||
//TODO: Placeholder for integration with KVM ingestion and KVM extend unmanage/manage VMs
|
||||
if (cmd instanceof ImportVmCmd) {
|
||||
ImportVmCmd importVmCmd = (ImportVmCmd) cmd;
|
||||
if (StringUtils.isBlank(importVmCmd.getImportSource())) {
|
||||
throw new CloudRuntimeException("Please provide an import source for importing the VM");
|
||||
}
|
||||
String source = importVmCmd.getImportSource().toUpperCase();
|
||||
ImportSource importSource = Enum.valueOf(ImportSource.class, source);
|
||||
if (ImportSource.VMWARE == importSource) {
|
||||
userVm = importUnmanagedInstanceFromVmwareToKvm(zone, cluster,
|
||||
template, instanceName, displayName, hostName, caller, owner, userId,
|
||||
serviceOffering, dataDiskOfferingMap,
|
||||
nicNetworkMap, nicIpAddressMap,
|
||||
details, importVmCmd, forced);
|
||||
}
|
||||
} else {
|
||||
if (cluster.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
|
||||
userVm = importUnmanagedInstanceFromVmwareToVmware(zone, cluster, hosts, additionalNameFilters,
|
||||
template, instanceName, displayName, hostName, caller, owner, userId,
|
||||
serviceOffering, dataDiskOfferingMap,
|
||||
nicNetworkMap, nicIpAddressMap,
|
||||
details, cmd.getMigrateAllowed(), managedVms, forced);
|
||||
}
|
||||
}
|
||||
|
||||
if (userVm == null) {
|
||||
ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT,
|
||||
cmd.getEventDescription(), null, null, 0);
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to find unmanaged vm with name: %s in cluster: %s", instanceName, cluster.getUuid()));
|
||||
}
|
||||
ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_INFO, EventTypes.EVENT_VM_IMPORT,
|
||||
cmd.getEventDescription(), userVm.getId(), ApiCommandResourceType.VirtualMachine.toString(), 0);
|
||||
return responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVm).get(0);
|
||||
}
|
||||
|
||||
private long getUserIdForImportInstance(Account owner) {
|
||||
long userId = CallContext.current().getCallingUserId();
|
||||
List<UserVO> userVOs = userDao.listByAccount(owner.getAccountId());
|
||||
if (CollectionUtils.isNotEmpty(userVOs)) {
|
||||
userId = userVOs.get(0).getId();
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
protected void basicParametersCheckForImportInstance(String name, Long domainId, String accountName) {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
throw new InvalidParameterValueException("Instance name cannot be empty");
|
||||
}
|
||||
if (domainId != null && StringUtils.isEmpty(accountName)) {
|
||||
throw new InvalidParameterValueException(String.format("%s parameter must be specified with %s parameter", ApiConstants.DOMAIN_ID, ApiConstants.ACCOUNT));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkVmwareInstanceNameForImportInstance(Hypervisor.HypervisorType hypervisorType, String instanceName, String hostName, DataCenter zone) {
|
||||
if (hypervisorType.equals(Hypervisor.HypervisorType.VMware) &&
|
||||
Boolean.parseBoolean(configurationDao.getValue(Config.SetVmInternalNameUsingDisplayName.key()))) {
|
||||
// If global config vm.instancename.flag is set to true, then CS will set guest VM's name as it appears on the hypervisor, to its hostname.
|
||||
// In case of VMware since VM name must be unique within a DC, check if VM with the same hostname already exists in the zone.
|
||||
VMInstanceVO vmByHostName = vmDao.findVMByHostNameInZone(hostName, zone.getId());
|
||||
if (vmByHostName != null && vmByHostName.getState() != VirtualMachine.State.Expunging) {
|
||||
throw new InvalidParameterValueException(String.format("Failed to import VM: %s. There already exists a VM by the hostname: %s in zone: %s", instanceName, hostName, zone.getUuid()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getHostNameForImportInstance(String hostName, Hypervisor.HypervisorType hypervisorType,
|
||||
String instanceName, String displayName) {
|
||||
if (StringUtils.isEmpty(hostName)) {
|
||||
hostName = hypervisorType == Hypervisor.HypervisorType.VMware ? instanceName : displayName;
|
||||
if (!NetUtils.verifyDomainNameLabel(hostName, true)) {
|
||||
throw new InvalidParameterValueException("Please provide a valid hostname for the VM. VM name contains unsupported characters that cannot be used as hostname.");
|
||||
}
|
||||
}
|
||||
if (!NetUtils.verifyDomainNameLabel(hostName, true)) {
|
||||
throw new InvalidParameterValueException("Invalid VM hostname. VM hostname can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
|
||||
+ "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit");
|
||||
}
|
||||
return hostName;
|
||||
}
|
||||
|
||||
private String getDisplayNameForImportInstance(String displayName, String instanceName) {
|
||||
return StringUtils.isEmpty(displayName) ? instanceName : displayName;
|
||||
}
|
||||
|
||||
private void checkResourceLimitForImportInstance(Account owner) {
|
||||
try {
|
||||
resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.user_vm, 1);
|
||||
} catch (ResourceAllocationException e) {
|
||||
LOGGER.error(String.format("VM resource allocation error for account: %s", owner.getUuid()), e);
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage())));
|
||||
}
|
||||
}
|
||||
|
||||
private ServiceOfferingVO getServiceOfferingForImportInstance(Long serviceOfferingId, Account owner, DataCenter zone) {
|
||||
if (serviceOfferingId == null) {
|
||||
throw new InvalidParameterValueException("Service offering ID cannot be null");
|
||||
}
|
||||
final ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(serviceOfferingId);
|
||||
if (serviceOffering == null) {
|
||||
throw new InvalidParameterValueException(String.format("Service offering ID: %d cannot be found", serviceOfferingId));
|
||||
}
|
||||
accountService.checkAccess(owner, serviceOffering, zone);
|
||||
return serviceOffering;
|
||||
}
|
||||
|
||||
protected VMTemplateVO getTemplateForImportInstance(Long templateId, Hypervisor.HypervisorType hypervisorType) {
|
||||
VMTemplateVO template;
|
||||
if (templateId == null) {
|
||||
template = templateDao.findByName(VM_IMPORT_DEFAULT_TEMPLATE_NAME);
|
||||
if (template == null) {
|
||||
template = createDefaultDummyVmImportTemplate();
|
||||
if (template == null) {
|
||||
throw new InvalidParameterValueException(String.format("Default VM import template with unique name: %s for hypervisor: %s cannot be created. Please use templateid parameter for import", VM_IMPORT_DEFAULT_TEMPLATE_NAME, hypervisorType.toString()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
template = templateDao.findById(templateId);
|
||||
}
|
||||
if (template == null) {
|
||||
throw new InvalidParameterValueException(String.format("Template ID: %d cannot be found", templateId));
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VM_IMPORT, eventDescription = "importing VM", async = true)
|
||||
public UserVmResponse importVm(ImportVmCmd cmd) {
|
||||
return baseImportInstance(cmd);
|
||||
}
|
||||
|
||||
private UserVm importUnmanagedInstanceFromVmwareToVmware(DataCenter zone, Cluster cluster,
|
||||
List<HostVO> hosts, List<String> additionalNameFilters,
|
||||
VMTemplateVO template, String instanceName, String displayName,
|
||||
String hostName, Account caller, Account owner, long userId,
|
||||
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap,
|
||||
Map<String, Long> nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap,
|
||||
Map<String, String> details, Boolean migrateAllowed, List<String> managedVms, boolean forced) {
|
||||
UserVm userVm = null;
|
||||
for (HostVO host : hosts) {
|
||||
HashMap<String, UnmanagedInstanceTO> unmanagedInstances = getUnmanagedInstancesForHost(host, cmd.getName(), managedVms);
|
||||
HashMap<String, UnmanagedInstanceTO> unmanagedInstances = getUnmanagedInstancesForHost(host, instanceName, managedVms);
|
||||
if (MapUtils.isEmpty(unmanagedInstances)) {
|
||||
continue;
|
||||
}
|
||||
@ -1219,17 +1302,329 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
template, displayName, hostName, CallContext.current().getCallingAccount(), owner, userId,
|
||||
serviceOffering, dataDiskOfferingMap,
|
||||
nicNetworkMap, nicIpAddressMap,
|
||||
details, cmd.getMigrateAllowed(), forced);
|
||||
details, migrateAllowed, forced);
|
||||
break;
|
||||
}
|
||||
if (userVm != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (userVm == null) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to find unmanaged vm with name: %s in cluster: %s", instanceName, cluster.getUuid()));
|
||||
return userVm;
|
||||
}
|
||||
|
||||
private UnmanagedInstanceTO cloneSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM) {
|
||||
HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
|
||||
|
||||
Map<String, String> params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName,
|
||||
username, password, clusterName, sourceHostName, sourceVM);
|
||||
|
||||
return vmwareGuru.cloneHypervisorVMOutOfBand(sourceHostName, sourceVM, params);
|
||||
}
|
||||
|
||||
protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster destinationCluster, VMTemplateVO template,
|
||||
String sourceVM, String displayName, String hostName,
|
||||
Account caller, Account owner, long userId,
|
||||
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap,
|
||||
Map<String, Long> nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap,
|
||||
Map<String, String> details, ImportVmCmd cmd, boolean forced) {
|
||||
Long existingVcenterId = cmd.getExistingVcenterId();
|
||||
String vcenter = cmd.getVcenter();
|
||||
String datacenterName = cmd.getDatacenterName();
|
||||
String username = cmd.getUsername();
|
||||
String password = cmd.getPassword();
|
||||
String clusterName = cmd.getClusterName();
|
||||
String sourceHostName = cmd.getHost();
|
||||
Long convertInstanceHostId = cmd.getConvertInstanceHostId();
|
||||
Long convertStoragePoolId = cmd.getConvertStoragePoolId();
|
||||
|
||||
if ((existingVcenterId == null && vcenter == null) || (existingVcenterId != null && vcenter != null)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
|
||||
"Please provide an existing vCenter ID or a vCenter IP/Name, parameters are mutually exclusive");
|
||||
}
|
||||
return responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVm).get(0);
|
||||
if (existingVcenterId == null && StringUtils.isAnyBlank(vcenter, datacenterName, username, password)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
|
||||
"Please set all the information for a vCenter IP/Name, datacenter, username and password");
|
||||
}
|
||||
|
||||
if (existingVcenterId != null) {
|
||||
VmwareDatacenterVO existingDC = vmwareDatacenterDao.findById(existingVcenterId);
|
||||
if (existingDC == null) {
|
||||
String err = String.format("Cannot find any existing Vmware DC with ID %s", existingVcenterId);
|
||||
LOGGER.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
vcenter = existingDC.getVcenterHost();
|
||||
datacenterName = existingDC.getVmwareDatacenterName();
|
||||
username = existingDC.getUser();
|
||||
password = existingDC.getPassword();
|
||||
}
|
||||
|
||||
UnmanagedInstanceTO clonedInstance = null;
|
||||
try {
|
||||
String instanceName = getGeneratedInstanceName(owner);
|
||||
clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
|
||||
clusterName, sourceHostName, sourceVM);
|
||||
checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced);
|
||||
UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password,
|
||||
sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, convertStoragePoolId);
|
||||
sanitizeConvertedInstance(convertedInstance, clonedInstance);
|
||||
UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null,
|
||||
template, displayName, hostName, caller, owner, userId,
|
||||
serviceOffering, dataDiskOfferingMap,
|
||||
nicNetworkMap, nicIpAddressMap,
|
||||
details, false, forced);
|
||||
LOGGER.debug(String.format("VM %s imported successfully", sourceVM));
|
||||
return userVm;
|
||||
} catch (CloudRuntimeException e) {
|
||||
LOGGER.error(String.format("Error importing VM: %s", e.getMessage()), e);
|
||||
ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT,
|
||||
cmd.getEventDescription(), null, null, 0);
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
|
||||
} finally {
|
||||
removeClonedInstance(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNetworkingBeforeConvertingVmwareInstance(DataCenter zone, Account owner, String instanceName,
|
||||
String hostName, UnmanagedInstanceTO clonedInstance,
|
||||
Map<String, Long> nicNetworkMap,
|
||||
Map<String, Network.IpAddresses> nicIpAddressMap,
|
||||
boolean forced) {
|
||||
List<UnmanagedInstanceTO.Nic> nics = clonedInstance.getNics();
|
||||
List<Long> networkIds = new ArrayList<>(nicNetworkMap.values());
|
||||
if (nics.size() != networkIds.size()) {
|
||||
String msg = String.format("Different number of nics found on instance %s: %s vs %s nics provided",
|
||||
clonedInstance.getName(), nics.size(), networkIds.size());
|
||||
LOGGER.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
|
||||
for (UnmanagedInstanceTO.Nic nic : nics) {
|
||||
Long networkId = nicNetworkMap.get(nic.getNicId());
|
||||
NetworkVO network = networkDao.findById(networkId);
|
||||
if (network == null) {
|
||||
String err = String.format("Cannot find a network with id = %s", networkId);
|
||||
LOGGER.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
Network.IpAddresses ipAddresses = null;
|
||||
if (MapUtils.isNotEmpty(nicIpAddressMap) && nicIpAddressMap.containsKey(nic.getNicId())) {
|
||||
ipAddresses = nicIpAddressMap.get(nic.getNicId());
|
||||
}
|
||||
boolean autoImport = ipAddresses != null && ipAddresses.getIp4Address() != null && ipAddresses.getIp4Address().equalsIgnoreCase("auto");
|
||||
checkUnmanagedNicAndNetworkMacAddressForImport(network, nic, forced);
|
||||
checkUnmanagedNicAndNetworkForImport(instanceName, nic, network, zone, owner, autoImport, Hypervisor.HypervisorType.KVM);
|
||||
checkUnmanagedNicAndNetworkHostnameForImport(instanceName, nic, network, hostName);
|
||||
checkUnmanagedNicIpAndNetworkForImport(instanceName, nic, network, ipAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkUnmanagedNicAndNetworkMacAddressForImport(NetworkVO network, UnmanagedInstanceTO.Nic nic, boolean forced) {
|
||||
NicVO existingNic = nicDao.findByNetworkIdAndMacAddress(network.getId(), nic.getMacAddress());
|
||||
if (existingNic != null && !forced) {
|
||||
String err = String.format("NIC with MAC address = %s exists on network with ID = %s and forced flag is disabled",
|
||||
nic.getMacAddress(), network.getId());
|
||||
LOGGER.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
}
|
||||
|
||||
private String getGeneratedInstanceName(Account owner) {
|
||||
long id = vmDao.getNextInSequence(Long.class, "id");
|
||||
String instanceSuffix = configurationDao.getValue(Config.InstanceName.key());
|
||||
if (instanceSuffix == null) {
|
||||
instanceSuffix = "DEFAULT";
|
||||
}
|
||||
return VirtualMachineName.getVmName(id, owner.getId(), instanceSuffix);
|
||||
}
|
||||
|
||||
private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, UnmanagedInstanceTO clonedInstance) {
|
||||
convertedInstance.setCpuCores(clonedInstance.getCpuCores());
|
||||
convertedInstance.setCpuSpeed(clonedInstance.getCpuSpeed());
|
||||
convertedInstance.setCpuCoresPerSocket(clonedInstance.getCpuCoresPerSocket());
|
||||
convertedInstance.setMemory(clonedInstance.getMemory());
|
||||
convertedInstance.setPowerState(UnmanagedInstanceTO.PowerState.PowerOff);
|
||||
List<UnmanagedInstanceTO.Disk> convertedInstanceDisks = convertedInstance.getDisks();
|
||||
List<UnmanagedInstanceTO.Disk> clonedInstanceDisks = clonedInstance.getDisks();
|
||||
for (int i = 0; i < convertedInstanceDisks.size(); i++) {
|
||||
UnmanagedInstanceTO.Disk disk = convertedInstanceDisks.get(i);
|
||||
disk.setDiskId(clonedInstanceDisks.get(i).getDiskId());
|
||||
}
|
||||
List<UnmanagedInstanceTO.Nic> convertedInstanceNics = convertedInstance.getNics();
|
||||
List<UnmanagedInstanceTO.Nic> clonedInstanceNics = clonedInstance.getNics();
|
||||
if (CollectionUtils.isEmpty(convertedInstanceNics) && CollectionUtils.isNotEmpty(clonedInstanceNics)) {
|
||||
for (UnmanagedInstanceTO.Nic nic : clonedInstanceNics) {
|
||||
// In case the NICs information is not parsed from the converted XML domain, use the cloned instance NICs with virtio adapter
|
||||
nic.setAdapterType("virtio");
|
||||
}
|
||||
convertedInstance.setNics(clonedInstanceNics);
|
||||
} else {
|
||||
for (int i = 0; i < convertedInstanceNics.size(); i++) {
|
||||
UnmanagedInstanceTO.Nic nic = convertedInstanceNics.get(i);
|
||||
nic.setNicId(clonedInstanceNics.get(i).getNicId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeClonedInstance(String vcenter, String datacenterName,
|
||||
String username, String password,
|
||||
String sourceHostName, String clonedInstanceName,
|
||||
String sourceVM) {
|
||||
HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
|
||||
Map<String, String> params = createParamsForRemoveClonedInstance(vcenter, datacenterName, username, password, sourceVM);
|
||||
boolean result = vmwareGuru.removeClonedHypervisorVMOutOfBand(sourceHostName, clonedInstanceName, params);
|
||||
if (!result) {
|
||||
String msg = String.format("Could not properly remove the cloned instance %s from VMware datacenter %s:%s",
|
||||
clonedInstanceName, vcenter, datacenterName);
|
||||
LOGGER.warn(msg);
|
||||
return;
|
||||
}
|
||||
LOGGER.debug(String.format("Removed the cloned instance %s from VMWare datacenter %s:%s",
|
||||
clonedInstanceName, vcenter, datacenterName));
|
||||
}
|
||||
|
||||
private Map<String, String> createParamsForRemoveClonedInstance(String vcenter, String datacenterName, String username,
|
||||
String password, String sourceVM) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vcenter);
|
||||
params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName);
|
||||
params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, username);
|
||||
params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, password);
|
||||
return params;
|
||||
}
|
||||
|
||||
private HostVO selectInstanceConvertionKVMHostInCluster(Cluster destinationCluster, Long convertInstanceHostId) {
|
||||
if (convertInstanceHostId != null) {
|
||||
HostVO selectedHost = hostDao.findById(convertInstanceHostId);
|
||||
if (selectedHost == null) {
|
||||
String msg = String.format("Cannot find host with ID %s", convertInstanceHostId);
|
||||
LOGGER.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
if (selectedHost.getResourceState() != ResourceState.Enabled ||
|
||||
selectedHost.getStatus() != Status.Up || selectedHost.getType() != Host.Type.Routing ||
|
||||
selectedHost.getClusterId() != destinationCluster.getId()) {
|
||||
String msg = String.format("Cannot perform the conversion on the host %s as it is not a running and Enabled host", selectedHost.getName());
|
||||
LOGGER.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
return selectedHost;
|
||||
}
|
||||
List<HostVO> hosts = hostDao.listByClusterAndHypervisorType(destinationCluster.getId(), destinationCluster.getHypervisorType());
|
||||
if (CollectionUtils.isEmpty(hosts)) {
|
||||
String err = String.format("Could not find any running %s host in cluster %s",
|
||||
destinationCluster.getHypervisorType(), destinationCluster.getName());
|
||||
LOGGER.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
List<HostVO> filteredHosts = hosts.stream()
|
||||
.filter(x -> x.getResourceState() == ResourceState.Enabled)
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(filteredHosts)) {
|
||||
String err = String.format("Could not find a %s host in cluster %s to perform the instance conversion",
|
||||
destinationCluster.getHypervisorType(), destinationCluster.getName());
|
||||
LOGGER.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
return filteredHosts.get(new Random().nextInt(filteredHosts.size()));
|
||||
}
|
||||
|
||||
private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String datacenterName, String clusterName,
|
||||
String username, String password, String hostName,
|
||||
UnmanagedInstanceTO clonedInstance, Cluster destinationCluster,
|
||||
Long convertInstanceHostId, Long convertStoragePoolId) {
|
||||
HostVO convertHost = selectInstanceConvertionKVMHostInCluster(destinationCluster, convertInstanceHostId);
|
||||
String vmName = clonedInstance.getName();
|
||||
LOGGER.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" +
|
||||
" from VMware to KVM ", convertHost.getId(), convertHost.getName(), vmName));
|
||||
|
||||
RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(hostName, vmName,
|
||||
vcenter, datacenterName, clusterName, username, password);
|
||||
DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, convertHost);
|
||||
List<String> destinationStoragePools = selectInstanceConvertionStoragePools(destinationCluster, clonedInstance.getDisks());
|
||||
ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO,
|
||||
Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation);
|
||||
int timeoutSeconds = StorageManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
|
||||
cmd.setWait(timeoutSeconds);
|
||||
|
||||
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",
|
||||
vmName, convertHost.getName(), convertAnswer.getDetails());
|
||||
LOGGER.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance();
|
||||
}
|
||||
|
||||
private List<String> selectInstanceConvertionStoragePools(Cluster destinationCluster, List<UnmanagedInstanceTO.Disk> disks) {
|
||||
List<String> storagePools = new ArrayList<>(disks.size());
|
||||
List<StoragePoolVO> pools = primaryDataStoreDao.listPoolsByCluster(destinationCluster.getId());
|
||||
//TODO: Choose pools by capacity
|
||||
for (UnmanagedInstanceTO.Disk disk : disks) {
|
||||
Long capacity = disk.getCapacity();
|
||||
storagePools.add(pools.get(0).getUuid());
|
||||
}
|
||||
return storagePools;
|
||||
}
|
||||
|
||||
private void logFailureAndThrowException(String msg) {
|
||||
LOGGER.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
|
||||
protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId, HostVO convertHost) {
|
||||
if (convertStoragePoolId != null) {
|
||||
StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId);
|
||||
if (selectedStoragePool == null) {
|
||||
logFailureAndThrowException(String.format("Cannot find a storage pool with ID %s", convertStoragePoolId));
|
||||
}
|
||||
if ((selectedStoragePool.getScope() == ScopeType.CLUSTER && selectedStoragePool.getClusterId() != destinationCluster.getId()) ||
|
||||
(selectedStoragePool.getScope() == ScopeType.ZONE && selectedStoragePool.getDataCenterId() != destinationCluster.getDataCenterId())) {
|
||||
logFailureAndThrowException(String.format("Cannot use the storage pool %s for the instance conversion as " +
|
||||
"it is not in the scope of the cluster %s", selectedStoragePool.getName(), destinationCluster.getName()));
|
||||
}
|
||||
if (selectedStoragePool.getScope() == ScopeType.HOST &&
|
||||
storagePoolHostDao.findByPoolHost(selectedStoragePool.getId(), convertHost.getId()) == null) {
|
||||
logFailureAndThrowException(String.format("The storage pool %s is not a local storage pool for the host %s", selectedStoragePool.getName(), convertHost.getName()));
|
||||
} 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()));
|
||||
}
|
||||
return dataStoreManager.getPrimaryDataStore(convertStoragePoolId).getTO();
|
||||
} else {
|
||||
long zoneId = destinationCluster.getDataCenterId();
|
||||
ImageStoreVO imageStore = imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs");
|
||||
if (imageStore == null) {
|
||||
logFailureAndThrowException(String.format("Could not find an NFS secondary storage pool on zone %s to use as a temporary location " +
|
||||
"for instance conversion", zoneId));
|
||||
}
|
||||
DataStore dataStore = dataStoreManager.getDataStore(imageStore.getId(), DataStoreRole.Image);
|
||||
return dataStore.getTO();
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, String> createParamsForTemplateFromVmwareVmMigration(String vcenterHost, String datacenterName,
|
||||
String username, String password,
|
||||
String clusterName, String sourceHostName,
|
||||
String sourceVMName) {
|
||||
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);
|
||||
params.put(VmDetailConstants.VMWARE_CLUSTER_NAME, clusterName);
|
||||
params.put(VmDetailConstants.VMWARE_HOST_NAME, sourceHostName);
|
||||
params.put(VmDetailConstants.VMWARE_VM_NAME, sourceVMName);
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1238,6 +1633,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
cmdList.add(ListUnmanagedInstancesCmd.class);
|
||||
cmdList.add(ImportUnmanagedInstanceCmd.class);
|
||||
cmdList.add(UnmanageVMInstanceCmd.class);
|
||||
cmdList.add(ImportVmCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
|
||||
@ -42,10 +42,13 @@ import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
|
||||
import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
|
||||
import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse;
|
||||
import org.apache.cloudstack.api.response.NicSecondaryIpResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.UsageRecordResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.usage.UsageService;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -60,6 +63,7 @@ import java.lang.reflect.Field;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -367,4 +371,29 @@ public class ApiResponseHelperTest {
|
||||
assertNull(response.getUserDataDetails());
|
||||
}
|
||||
}
|
||||
|
||||
private UnmanagedInstanceTO getUnmanagedInstaceForTests() {
|
||||
UnmanagedInstanceTO instance = Mockito.mock(UnmanagedInstanceTO.class);
|
||||
Mockito.when(instance.getPowerState()).thenReturn(UnmanagedInstanceTO.PowerState.PowerOff);
|
||||
Mockito.when(instance.getClusterName()).thenReturn("CL1");
|
||||
UnmanagedInstanceTO.Disk disk = Mockito.mock(UnmanagedInstanceTO.Disk.class);
|
||||
Mockito.when(disk.getDiskId()).thenReturn("0");
|
||||
Mockito.when(disk.getLabel()).thenReturn("Hard disk 1");
|
||||
Mockito.when(disk.getCapacity()).thenReturn(17179869184L);
|
||||
Mockito.when(disk.getPosition()).thenReturn(0);
|
||||
Mockito.when(instance.getDisks()).thenReturn(List.of(disk));
|
||||
UnmanagedInstanceTO.Nic nic = Mockito.mock(UnmanagedInstanceTO.Nic.class);
|
||||
Mockito.when(nic.getNicId()).thenReturn("Network adapter 1");
|
||||
Mockito.when(nic.getMacAddress()).thenReturn("aa:bb:cc:dd:ee:ff");
|
||||
Mockito.when(instance.getNics()).thenReturn(List.of(nic));
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateUnmanagedInstanceResponseVmwareDcVms() {
|
||||
UnmanagedInstanceTO instance = getUnmanagedInstaceForTests();
|
||||
UnmanagedInstanceResponse response = apiResponseHelper.createUnmanagedInstanceResponse(instance, null, null);
|
||||
Assert.assertEquals(1, response.getDisks().size());
|
||||
Assert.assertEquals(1, response.getNics().size());
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,6 +116,7 @@ import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@ -1200,4 +1201,24 @@ public class UserVmManagerImplTest {
|
||||
Assert.assertEquals(expected, result);
|
||||
Assert.assertEquals(expected, userVmVoMock.getPassword());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVmRequiredFieldsForImportNotImport() {
|
||||
userVmManagerImpl.setVmRequiredFieldsForImport(false, userVmVoMock, _dcMock,
|
||||
Hypervisor.HypervisorType.VMware, Mockito.mock(HostVO.class), Mockito.mock(HostVO.class), VirtualMachine.PowerState.PowerOn);
|
||||
Mockito.verify(userVmVoMock, never()).setDataCenterId(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVmRequiredFieldsForImportFromLastHost() {
|
||||
HostVO lastHost = Mockito.mock(HostVO.class);
|
||||
HostVO host = Mockito.mock(HostVO.class);
|
||||
Mockito.when(_dcMock.getId()).thenReturn(1L);
|
||||
Mockito.when(host.getId()).thenReturn(1L);
|
||||
Mockito.when(lastHost.getId()).thenReturn(2L);
|
||||
userVmManagerImpl.setVmRequiredFieldsForImport(true, userVmVoMock, _dcMock,
|
||||
Hypervisor.HypervisorType.VMware, host, lastHost, VirtualMachine.PowerState.PowerOn);
|
||||
Mockito.verify(userVmVoMock).setLastHostId(2L);
|
||||
Mockito.verify(userVmVoMock).setState(VirtualMachine.State.Running);
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,16 +19,24 @@ package org.apache.cloudstack.vm;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.ConvertInstanceAnswer;
|
||||
import com.cloud.agent.api.ConvertInstanceCommand;
|
||||
import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
|
||||
import com.cloud.agent.api.GetUnmanagedInstancesCommand;
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.configuration.Resource;
|
||||
import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.VmwareDatacenterVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.VmwareDatacenterDao;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.UnsupportedServiceException;
|
||||
import com.cloud.host.Host;
|
||||
@ -36,21 +44,28 @@ import com.cloud.host.HostVO;
|
||||
import com.cloud.host.Status;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.HypervisorGuru;
|
||||
import com.cloud.hypervisor.HypervisorGuruManager;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.NetworkModel;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.resource.ResourceState;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.ScopeType;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.VMTemplateStoragePoolVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.VolumeApiService;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.DiskOfferingDao;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.StoragePoolHostDao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
@ -64,6 +79,7 @@ import com.cloud.user.UserVO;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.NicProfile;
|
||||
import com.cloud.vm.NicVO;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
@ -78,18 +94,25 @@ import org.apache.cloudstack.api.ResponseGenerator;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ImportVmCmd;
|
||||
import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
@ -104,15 +127,21 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class UnmanagedVMsManagerImplTest {
|
||||
|
||||
@InjectMocks
|
||||
private UnmanagedVMsManager unmanagedVMsManager = new UnmanagedVMsManagerImpl();
|
||||
private UnmanagedVMsManagerImpl unmanagedVMsManager = new UnmanagedVMsManagerImpl();
|
||||
|
||||
@Mock
|
||||
private UserVmManager userVmManager;
|
||||
@ -168,6 +197,16 @@ public class UnmanagedVMsManagerImplTest {
|
||||
private NicDao nicDao;
|
||||
@Mock
|
||||
private HostDao hostDao;
|
||||
@Mock
|
||||
private VmwareDatacenterDao vmwareDatacenterDao;
|
||||
@Mock
|
||||
private HypervisorGuruManager hypervisorGuruManager;
|
||||
@Mock
|
||||
private ImageStoreDao imageStoreDao;
|
||||
@Mock
|
||||
private DataStoreManager dataStoreManager;
|
||||
@Mock
|
||||
private StoragePoolHostDao storagePoolHostDao;
|
||||
|
||||
@Mock
|
||||
private VMInstanceVO virtualMachine;
|
||||
@ -178,15 +217,24 @@ public class UnmanagedVMsManagerImplTest {
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
private MockedStatic<ActionEventUtils> actionEventUtilsMocked;
|
||||
|
||||
private UnmanagedInstanceTO instance;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class);
|
||||
BDDMockito.given(ActionEventUtils.onStartedActionEvent(anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString(), anyBoolean(), anyLong()))
|
||||
.willReturn(1L);
|
||||
BDDMockito.given(ActionEventUtils.onCompletedActionEvent(anyLong(), anyLong(), anyString(), anyString(), anyString(), anyLong(), anyString(), anyLong()))
|
||||
.willReturn(1L);
|
||||
|
||||
AccountVO account = new AccountVO("admin", 1L, "", Account.Type.ADMIN, "uuid");
|
||||
UserVO user = new UserVO(1, "adminuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||
CallContext.register(user, account);
|
||||
|
||||
UnmanagedInstanceTO instance = new UnmanagedInstanceTO();
|
||||
instance = new UnmanagedInstanceTO();
|
||||
instance.setName("TestInstance");
|
||||
instance.setCpuCores(2);
|
||||
instance.setCpuCoresPerSocket(1);
|
||||
@ -303,6 +351,7 @@ public class UnmanagedVMsManagerImplTest {
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
closeable.close();
|
||||
actionEventUtilsMocked.close();
|
||||
CallContext.unregister();
|
||||
}
|
||||
|
||||
@ -316,7 +365,7 @@ public class UnmanagedVMsManagerImplTest {
|
||||
public void listUnmanagedInstancesInvalidHypervisorTest() {
|
||||
ListUnmanagedInstancesCmd cmd = Mockito.mock(ListUnmanagedInstancesCmd.class);
|
||||
ClusterVO cluster = new ClusterVO(1, 1, "Cluster");
|
||||
cluster.setHypervisorType(Hypervisor.HypervisorType.KVM.toString());
|
||||
cluster.setHypervisorType(Hypervisor.HypervisorType.XenServer.toString());
|
||||
when(clusterDao.findById(Mockito.anyLong())).thenReturn(cluster);
|
||||
unmanagedVMsManager.listUnmanagedInstances(cmd);
|
||||
}
|
||||
@ -389,4 +438,276 @@ public class UnmanagedVMsManagerImplTest {
|
||||
public void unmanageVMInstanceExistingISOAttachedTest() {
|
||||
unmanagedVMsManager.unmanageVMInstance(virtualMachineId);
|
||||
}
|
||||
|
||||
private void baseBasicParametersCheckForImportInstance(String name, Long domainId, String accountName) {
|
||||
unmanagedVMsManager.basicParametersCheckForImportInstance(name, domainId, accountName);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testBasicParametersCheckForImportInstanceMissingName() {
|
||||
baseBasicParametersCheckForImportInstance(null, 1L, "test");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testBasicParametersCheckForImportInstanceMissingDomainAndAccount() {
|
||||
baseBasicParametersCheckForImportInstance("vm", 1L, "");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testBasicAccessChecksMissingClusterId() {
|
||||
unmanagedVMsManager.basicAccessChecks(null);
|
||||
}
|
||||
|
||||
@Test(expected = PermissionDeniedException.class)
|
||||
public void testBasicAccessChecksNotAdminCaller() {
|
||||
CallContext.unregister();
|
||||
AccountVO account = new AccountVO("user", 1L, "", Account.Type.NORMAL, "uuid");
|
||||
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||
CallContext.register(user, account);
|
||||
unmanagedVMsManager.basicAccessChecks(1L);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testBasicAccessChecksUnsupportedHypervisorType() {
|
||||
ClusterVO clusterVO = new ClusterVO(1L, 1L, "Cluster");
|
||||
clusterVO.setHypervisorType(Hypervisor.HypervisorType.XenServer.toString());
|
||||
when(clusterDao.findById(Mockito.anyLong())).thenReturn(clusterVO);
|
||||
unmanagedVMsManager.basicAccessChecks(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTemplateForImportInstanceDefaultTemplate() {
|
||||
String defaultTemplateName = "DefaultTemplate";
|
||||
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
|
||||
when(template.getName()).thenReturn(defaultTemplateName);
|
||||
when(templateDao.findByName(anyString())).thenReturn(template);
|
||||
VMTemplateVO templateForImportInstance = unmanagedVMsManager.getTemplateForImportInstance(null, Hypervisor.HypervisorType.KVM);
|
||||
Assert.assertEquals(defaultTemplateName, templateForImportInstance.getName());
|
||||
}
|
||||
|
||||
private enum VcenterParameter {
|
||||
EXISTING, EXTERNAL, BOTH, NONE, EXISTING_INVALID, AGENT_UNAVAILABLE, CONVERT_FAILURE
|
||||
}
|
||||
|
||||
private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, boolean selectConvertHost,
|
||||
boolean selectTemporaryStorage) throws OperationTimedoutException, AgentUnavailableException {
|
||||
long clusterId = 1L;
|
||||
long zoneId = 1L;
|
||||
long podId = 1L;
|
||||
long existingDatacenterId = 1L;
|
||||
String vcenterHost = "192.168.1.2";
|
||||
String datacenter = "Datacenter";
|
||||
String username = "administrator@vsphere.local";
|
||||
String password = "password";
|
||||
String host = "192.168.1.10";
|
||||
String vmName = "TestInstanceFromVmware";
|
||||
instance.setName(vmName);
|
||||
long newVmId = 2L;
|
||||
long networkId = 1L;
|
||||
when(vmDao.getNextInSequence(Long.class, "id")).thenReturn(newVmId);
|
||||
|
||||
ClusterVO cluster = mock(ClusterVO.class);
|
||||
when(cluster.getId()).thenReturn(clusterId);
|
||||
when(cluster.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
|
||||
when(cluster.getDataCenterId()).thenReturn(zoneId);
|
||||
when(clusterDao.findById(clusterId)).thenReturn(cluster);
|
||||
|
||||
ImportVmCmd importVmCmd = Mockito.mock(ImportVmCmd.class);
|
||||
|
||||
when(importVmCmd.getName()).thenReturn(vmName);
|
||||
when(importVmCmd.getClusterId()).thenReturn(clusterId);
|
||||
when(importVmCmd.getDomainId()).thenReturn(null);
|
||||
when(importVmCmd.getImportSource()).thenReturn(VmImportService.ImportSource.VMWARE.toString());
|
||||
when(importVmCmd.getHost()).thenReturn(host);
|
||||
when(importVmCmd.getNicNetworkList()).thenReturn(Map.of("NIC 1", networkId));
|
||||
when(importVmCmd.getConvertInstanceHostId()).thenReturn(null);
|
||||
when(importVmCmd.getConvertStoragePoolId()).thenReturn(null);
|
||||
|
||||
NetworkVO networkVO = Mockito.mock(NetworkVO.class);
|
||||
when(networkVO.getGuestType()).thenReturn(Network.GuestType.L2);
|
||||
when(networkVO.getDataCenterId()).thenReturn(zoneId);
|
||||
when(networkDao.findById(networkId)).thenReturn(networkVO);
|
||||
|
||||
HypervisorGuru vmwareGuru = mock(HypervisorGuru.class);
|
||||
when(hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware)).thenReturn(vmwareGuru);
|
||||
when(vmwareGuru.cloneHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(instance);
|
||||
when(vmwareGuru.removeClonedHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(true);
|
||||
|
||||
HostVO convertHost = mock(HostVO.class);
|
||||
long convertHostId = 1L;
|
||||
when(convertHost.getStatus()).thenReturn(Status.Up);
|
||||
when(convertHost.getResourceState()).thenReturn(ResourceState.Enabled);
|
||||
when(convertHost.getId()).thenReturn(convertHostId);
|
||||
when(convertHost.getName()).thenReturn("KVM-Convert-Host");
|
||||
when(convertHost.getType()).thenReturn(Host.Type.Routing);
|
||||
when(convertHost.getClusterId()).thenReturn(clusterId);
|
||||
if (selectConvertHost) {
|
||||
when(importVmCmd.getConvertInstanceHostId()).thenReturn(convertHostId);
|
||||
when(hostDao.findById(convertHostId)).thenReturn(convertHost);
|
||||
} else {
|
||||
when(hostDao.listByClusterAndHypervisorType(clusterId, Hypervisor.HypervisorType.KVM))
|
||||
.thenReturn(List.of(convertHost));
|
||||
}
|
||||
|
||||
DataStoreTO dataStoreTO = mock(DataStoreTO.class);
|
||||
DataStore dataStore = mock(DataStore.class);
|
||||
when(dataStore.getTO()).thenReturn(dataStoreTO);
|
||||
|
||||
StoragePoolVO destPool = mock(StoragePoolVO.class);
|
||||
when(destPool.getUuid()).thenReturn(UUID.randomUUID().toString());
|
||||
when(destPool.getDataCenterId()).thenReturn(zoneId);
|
||||
when(destPool.getClusterId()).thenReturn(null);
|
||||
when(destPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
if (selectTemporaryStorage) {
|
||||
long temporaryStoragePoolId = 1L;
|
||||
when(importVmCmd.getConvertStoragePoolId()).thenReturn(temporaryStoragePoolId);
|
||||
when(primaryDataStoreDao.findById(temporaryStoragePoolId)).thenReturn(destPool);
|
||||
when(dataStoreManager.getPrimaryDataStore(temporaryStoragePoolId)).thenReturn(dataStore);
|
||||
} else {
|
||||
ImageStoreVO imageStoreVO = mock(ImageStoreVO.class);
|
||||
when(imageStoreVO.getId()).thenReturn(1L);
|
||||
when(imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs")).thenReturn(imageStoreVO);
|
||||
when(dataStoreManager.getDataStore(1L, DataStoreRole.Image)).thenReturn(dataStore);
|
||||
}
|
||||
when(primaryDataStoreDao.listPoolsByCluster(clusterId)).thenReturn(List.of(destPool));
|
||||
when(primaryDataStoreDao.listPoolByHostPath(Mockito.anyString(), Mockito.anyString())).thenReturn(List.of(destPool));
|
||||
|
||||
if (VcenterParameter.EXISTING == vcenterParameter) {
|
||||
VmwareDatacenterVO datacenterVO = mock(VmwareDatacenterVO.class);
|
||||
when(datacenterVO.getVcenterHost()).thenReturn(vcenterHost);
|
||||
when(datacenterVO.getVmwareDatacenterName()).thenReturn(datacenter);
|
||||
when(datacenterVO.getUser()).thenReturn(username);
|
||||
when(datacenterVO.getPassword()).thenReturn(password);
|
||||
when(importVmCmd.getExistingVcenterId()).thenReturn(existingDatacenterId);
|
||||
when(vmwareDatacenterDao.findById(existingDatacenterId)).thenReturn(datacenterVO);
|
||||
} else if (VcenterParameter.EXTERNAL == vcenterParameter) {
|
||||
when(importVmCmd.getVcenter()).thenReturn(vcenterHost);
|
||||
when(importVmCmd.getDatacenterName()).thenReturn(datacenter);
|
||||
when(importVmCmd.getUsername()).thenReturn(username);
|
||||
when(importVmCmd.getPassword()).thenReturn(password);
|
||||
}
|
||||
|
||||
if (VcenterParameter.BOTH == vcenterParameter) {
|
||||
when(importVmCmd.getExistingVcenterId()).thenReturn(existingDatacenterId);
|
||||
when(importVmCmd.getVcenter()).thenReturn(vcenterHost);
|
||||
} else if (VcenterParameter.NONE == vcenterParameter) {
|
||||
when(importVmCmd.getExistingVcenterId()).thenReturn(null);
|
||||
when(importVmCmd.getVcenter()).thenReturn(null);
|
||||
} else if (VcenterParameter.EXISTING_INVALID == vcenterParameter) {
|
||||
when(importVmCmd.getExistingVcenterId()).thenReturn(existingDatacenterId);
|
||||
when(vmwareDatacenterDao.findById(existingDatacenterId)).thenReturn(null);
|
||||
}
|
||||
|
||||
ConvertInstanceAnswer answer = mock(ConvertInstanceAnswer.class);
|
||||
when(answer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);
|
||||
when(answer.getConvertedInstance()).thenReturn(instance);
|
||||
if (VcenterParameter.AGENT_UNAVAILABLE != vcenterParameter) {
|
||||
when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(ConvertInstanceCommand.class))).thenReturn(answer);
|
||||
}
|
||||
|
||||
try (MockedStatic<UsageEventUtils> ignored = Mockito.mockStatic(UsageEventUtils.class)) {
|
||||
unmanagedVMsManager.importVm(importVmCmd);
|
||||
verify(vmwareGuru).cloneHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap());
|
||||
verify(vmwareGuru).removeClonedHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportVmFromVmwareToKvmExistingVcenter() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.EXISTING, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportVmFromVmwareToKvmExistingVcenterSetConvertHost() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.EXISTING, true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportVmFromVmwareToKvmExistingVcenterSetConvertHostAndTemporaryStorage() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.EXISTING, true, true);
|
||||
}
|
||||
|
||||
@Test(expected = ServerApiException.class)
|
||||
public void testImportVmFromVmwareToKvmExistingVcenterExclusiveParameters() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.BOTH, false, false);
|
||||
}
|
||||
|
||||
@Test(expected = ServerApiException.class)
|
||||
public void testImportVmFromVmwareToKvmExistingVcenterMissingParameters() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.NONE, false, false);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testImportVmFromVmwareToKvmExistingVcenterInvalid() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.EXISTING_INVALID, false, false);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testImportVmFromVmwareToKvmExistingVcenterAgentUnavailable() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.AGENT_UNAVAILABLE, false, false);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testImportVmFromVmwareToKvmExistingVcenterConvertFailure() throws OperationTimedoutException, AgentUnavailableException {
|
||||
baseTestImportVmFromVmwareToKvm(VcenterParameter.CONVERT_FAILURE, false, false);
|
||||
}
|
||||
|
||||
private ClusterVO getClusterForTests() {
|
||||
ClusterVO cluster = mock(ClusterVO.class);
|
||||
when(cluster.getId()).thenReturn(1L);
|
||||
when(cluster.getDataCenterId()).thenReturn(1L);
|
||||
return cluster;
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testSelectInstanceConversionTemporaryLocationInvalidStorage() {
|
||||
ClusterVO cluster = getClusterForTests();
|
||||
|
||||
long poolId = 1L;
|
||||
when(primaryDataStoreDao.findById(poolId)).thenReturn(null);
|
||||
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, null);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testSelectInstanceConversionTemporaryLocationPoolInvalidScope() {
|
||||
ClusterVO cluster = getClusterForTests();
|
||||
long poolId = 1L;
|
||||
StoragePoolVO pool = mock(StoragePoolVO.class);
|
||||
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
|
||||
Mockito.when(pool.getClusterId()).thenReturn(100L);
|
||||
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
|
||||
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, null);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testSelectInstanceConversionTemporaryLocationLocalStoragePoolInvalid() {
|
||||
ClusterVO cluster = getClusterForTests();
|
||||
long poolId = 1L;
|
||||
StoragePoolVO pool = mock(StoragePoolVO.class);
|
||||
Mockito.when(pool.getScope()).thenReturn(ScopeType.HOST);
|
||||
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
|
||||
HostVO convertHost = Mockito.mock(HostVO.class);
|
||||
Mockito.when(convertHost.getId()).thenReturn(1L);
|
||||
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, convertHost);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testSelectInstanceConversionTemporaryLocationStoragePoolInvalidType() {
|
||||
ClusterVO cluster = getClusterForTests();
|
||||
long poolId = 1L;
|
||||
StoragePoolVO pool = mock(StoragePoolVO.class);
|
||||
Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER);
|
||||
Mockito.when(pool.getClusterId()).thenReturn(1L);
|
||||
when(primaryDataStoreDao.findById(poolId)).thenReturn(pool);
|
||||
HostVO convertHost = Mockito.mock(HostVO.class);
|
||||
Mockito.when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD);
|
||||
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, convertHost);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() {
|
||||
ClusterVO cluster = getClusterForTests();
|
||||
Mockito.when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null);
|
||||
unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,6 +256,8 @@ known_categories = {
|
||||
'importVsphereStoragePolicies' : 'vSphere storage policies',
|
||||
'listVsphereStoragePolicies' : 'vSphere storage policies',
|
||||
'ConsoleEndpoint': 'Console Endpoint',
|
||||
'Shutdown': 'Shutdown',
|
||||
'importVm': 'Virtual Machine',
|
||||
'listQuarantinedIp': 'IP Quarantine',
|
||||
'updateQuarantinedIp': 'IP Quarantine',
|
||||
'removeQuarantinedIp': 'IP Quarantine',
|
||||
|
||||
@ -326,6 +326,7 @@
|
||||
"label.allocatedonly": "Allocated",
|
||||
"label.allocationstate": "Allocation state",
|
||||
"label.allow": "Allow",
|
||||
"label.allow.duplicate.macaddresses": "Allow duplicate MAC addresses",
|
||||
"label.allowuserdrivenbackups": "Allow User driven backups",
|
||||
"label.annotation": "Comment",
|
||||
"label.annotations": "Comments",
|
||||
@ -513,6 +514,7 @@
|
||||
"label.confirmdeclineinvitation": "Are you sure you want to decline this project invitation?",
|
||||
"label.confirmpassword": "Confirm password",
|
||||
"label.confirmpassword.description": "Please type the same password again.",
|
||||
"label.connect": "Connect",
|
||||
"label.connectiontimeout": "Connection timeout",
|
||||
"label.conservemode": "Conserve mode",
|
||||
"label.considerlasthost": "Consider Last Host",
|
||||
@ -670,6 +672,7 @@
|
||||
"label.deploymentplanner": "Deployment planner",
|
||||
"label.desc.db.stats": "Database Statistics",
|
||||
"label.desc.importexportinstancewizard": "Import and export Instances to/from an existing VMware cluster.",
|
||||
"label.desc.importmigratefromvmwarewizard": "Import instances from VMware into a KVM cluster",
|
||||
"label.desc.usage.stats": "Usage Server Statistics",
|
||||
"label.description": "Description",
|
||||
"label.destaddressgroupuuid": "Destination Address Group",
|
||||
@ -677,6 +680,10 @@
|
||||
"label.destendport": "Destination End Port",
|
||||
"label.desthost": "Destination host",
|
||||
"label.destination": "Destination",
|
||||
"label.destination.cluster": "Destination Cluster",
|
||||
"label.destination.hypervisor": "Destination Hypervisor",
|
||||
"label.destination.pod": "Destination Pod",
|
||||
"label.destination.zone": "Destination Zone",
|
||||
"label.destinationphysicalnetworkid": "Destination physical Network ID",
|
||||
"label.destinationtype": "Destination Type",
|
||||
"label.destipprefix": "Destination Network Address",
|
||||
@ -850,6 +857,7 @@
|
||||
"label.every": "Every",
|
||||
"label.example": "Example",
|
||||
"label.example.plugin": "ExamplePlugin",
|
||||
"label.existing": "Existing",
|
||||
"label.execute": "Execute",
|
||||
"label.expunge": "Expunge",
|
||||
"label.expungevmgraceperiod": "Expunge Instance grace period (in sec)",
|
||||
@ -857,6 +865,7 @@
|
||||
"label.expunging": "Expunging",
|
||||
"label.export.rules": "Export Rules",
|
||||
"label.external.managed": "ExternalManaged",
|
||||
"label.external": "External",
|
||||
"label.external.link": "External link",
|
||||
"label.externalid": "External Id",
|
||||
"label.externalloadbalanceripaddress": "External load balancer IP address.",
|
||||
@ -1194,6 +1203,7 @@
|
||||
"label.list.nodes": "List nodes",
|
||||
"label.list.pods": "List pods",
|
||||
"label.list.services": "List services",
|
||||
"label.list.vmware.vcenter.vms": "List VMware Instances",
|
||||
"label.livepatch": "Live patch Network's router(s)",
|
||||
"label.load.balancer": "Load balancer",
|
||||
"label.loadbalancerinstance": "Assigned Instances",
|
||||
@ -1815,6 +1825,7 @@
|
||||
"label.select.project": "Select project",
|
||||
"label.select.projects": "Select projects",
|
||||
"label.select.ps": "Select primary storage",
|
||||
"label.select.source.vcenter.datacenter": "Select the source VMware vCenter Datacenter",
|
||||
"label.select.tier": "Select Network Tier",
|
||||
"label.select.zones": "Select zones",
|
||||
"label.select.2fa.provider": "Select the provider",
|
||||
@ -1877,6 +1888,7 @@
|
||||
"label.snapshottype": "Snapshot Type",
|
||||
"label.sockettimeout": "Socket timeout",
|
||||
"label.softwareversion": "Software version",
|
||||
"label.source": "Select Import-Export Source Hypervisor",
|
||||
"label.source.based": "SourceBased",
|
||||
"label.sourcecidr": "Source CIDR",
|
||||
"label.sourcehost": "Source host",
|
||||
@ -2660,7 +2672,8 @@
|
||||
"message.desc.create.ssh.key.pair": "Please fill in the following data to create or register a ssh key pair.<br><br>(1) If public key is set, CloudStack will register the public key. You can use it through your private key.<br><br>(2) If public key is not set, CloudStack will create a new SSH key pair. In this case, please copy and save the private key. CloudStack will not keep it.<br>",
|
||||
"message.desc.created.ssh.key.pair": "Created a SSH key pair.",
|
||||
"message.desc.host": "Each cluster must contain at least one host (computer) for guest Instances to run on. We will add the first host now. For a host to function in CloudStack, you must install hypervisor software on the host, assign an IP address to the host, and ensure the host is connected to the CloudStack management server.<br/><br/>Give the host's DNS or IP address, the user name (usually root) and password, and any labels you use to categorize hosts.",
|
||||
"message.desc.importexportinstancewizard": "This feature only applies Cloudstack VMware clusters. By choosing to manage an Instance, CloudStack takes over the orchestration of that Instance. The Instance is left running and not physically moved. Unmanaging Instances, removes CloudStack ability to manage them (but they are left running and not destroyed).",
|
||||
"message.desc.importexportinstancewizard": "By choosing to manage an Instance, CloudStack takes over the orchestration of that Instance. Unmanaging an Instance removes CloudStack ability to manage it. In both cases, the Instance is left running and no changes are done to the VM on the hypervisor.<br><br>For KVM, managing a VM is an experimental feature.",
|
||||
"message.desc.importmigratefromvmwarewizard": "By selecting an existing or external VMware Datacenter and an instance to import, CloudStack migrates the selected instance from VMware to KVM on a conversion host using virt-v2v and imports it into a KVM cluster",
|
||||
"message.desc.primary.storage": "Each cluster must contain one or more primary storage servers. We will add the first one now. Primary storage contains the disk volumes for all the Instances running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor.",
|
||||
"message.desc.reset.ssh.key.pair": "Please specify a ssh key pair that you would like to add to this Instance.",
|
||||
"message.desc.secondary.storage": "Each zone must have at least one NFS or secondary storage server. We will add the first one now. Secondary storage stores Instance Templates, ISO images, and Instance disk volume Snapshots. This server must be available to all hosts in the zone.<br/><br/>Provide the IP address and exported path.",
|
||||
@ -2863,6 +2876,7 @@
|
||||
"message.host.controlstate.retry": "Some actions on this Instance will fail, if so please wait a while and retry.",
|
||||
"message.host.dedicated": "Host Dedicated",
|
||||
"message.host.dedication.released": "Host dedication released.",
|
||||
"message.import.running.instance.warning": "The selected VM is powered-on on the VMware Datacenter. The recommended state to convert a VMware VM into KVM is powered-off after a graceful shutdown of the guest OS.",
|
||||
"message.info.cloudian.console": "Cloudian Management Console should open in another window.",
|
||||
"message.installwizard.cloudstack.helptext.website": " * Project website:\t ",
|
||||
"message.infra.setup.tungsten.description": "This zone must contain a Tungsten-Fabric provider because the isolation method is TF",
|
||||
@ -2905,6 +2919,7 @@
|
||||
"message.launch.zone.hint": "Configure Network components and traffic including IP addresses.",
|
||||
"message.license.agreements.not.accepted": "License agreements not accepted.",
|
||||
"message.linstor.resourcegroup.description": "Linstor resource group to use for primary storage.",
|
||||
"message.list.zone.vmware.datacenter.empty": "No VMware Datacenter exists in the selected Zone",
|
||||
"message.listnsp.not.return.providerid": "error: listNetworkServiceProviders API doesn't return VirtualRouter provider ID.",
|
||||
"message.load.host.failed": "Failed to load hosts.",
|
||||
"message.loadbalancer.stickypolicy.configuration": "Customize the load balancer stickiness policy:",
|
||||
|
||||
@ -54,5 +54,6 @@ export default {
|
||||
<style scoped lang="scss">
|
||||
.tooltip-icon {
|
||||
color: rgba(0,0,0,.45);
|
||||
margin-left: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1773,6 +1773,9 @@ export default {
|
||||
this.form.iothreadsenabled = template.details && Object.prototype.hasOwnProperty.call(template.details, 'iothreads')
|
||||
this.form.iodriverpolicy = template.details?.['io.policy']
|
||||
this.form.keyboard = template.details?.keyboard
|
||||
if (template.details['vmware-to-kvm-mac-addresses']) {
|
||||
this.dataPreFill.macAddressArray = JSON.parse(template.details['vmware-to-kvm-mac-addresses'])
|
||||
}
|
||||
}
|
||||
} else if (name === 'isoid') {
|
||||
this.templateConfigurations = []
|
||||
|
||||
@ -104,6 +104,11 @@ export default {
|
||||
minimumMemory: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
allowAllOfferings: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
@ -178,6 +183,9 @@ export default {
|
||||
if (this.autoscale && item.iscustomized) {
|
||||
disabled = true
|
||||
}
|
||||
if (this.allowAllOfferings) {
|
||||
disabled = false
|
||||
}
|
||||
return {
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
|
||||
@ -36,7 +36,16 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="column.key === 'network'">
|
||||
<a-alert
|
||||
v-if="hypervisor === 'KVM' && unableToMatch"
|
||||
type="warning"
|
||||
showIcon
|
||||
banner
|
||||
style="margin-bottom: 10px"
|
||||
:message="$t('message.select.nic.network')"
|
||||
/>
|
||||
<a-select
|
||||
style="width: 100%"
|
||||
v-if="validNetworks[record.id] && validNetworks[record.id].length > 0"
|
||||
:defaultValue="validNetworks[record.id][0].id"
|
||||
@change="val => handleNetworkChange(record, val)"
|
||||
@ -46,10 +55,10 @@
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}" >
|
||||
<a-select-option
|
||||
v-for="network in validNetworks[record.id]"
|
||||
v-for="network in hypervisor !== 'KVM' ? validNetworks[record.id] : networks"
|
||||
:key="network.id"
|
||||
:label="network.displaytext + (network.broadcasturi ? ' (' + network.broadcasturi + ')' : '')">
|
||||
{{ network.displaytext + (network.broadcasturi ? ' (' + network.broadcasturi + ')' : '') }}
|
||||
<div>{{ network.displaytext + (network.broadcasturi ? ' (' + network.broadcasturi + ')' : '') }}</div>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<span v-else>
|
||||
@ -101,6 +110,10 @@ export default {
|
||||
filterMatchKey: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
hypervisor: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data () {
|
||||
@ -126,6 +139,7 @@ export default {
|
||||
selectedRowKeys: [],
|
||||
networks: [],
|
||||
validNetworks: {},
|
||||
unableToMatch: false,
|
||||
values: {},
|
||||
ipAddressesEnabled: {},
|
||||
ipAddresses: {},
|
||||
@ -201,7 +215,13 @@ export default {
|
||||
this.validNetworks[item.id] = this.validNetworks[item.id].filter(x => (x.state === 'Implemented' || (x.state === 'Setup' && ['Shared', 'L2'].includes(x.type))))
|
||||
}
|
||||
if (this.filterMatchKey) {
|
||||
this.validNetworks[item.id] = this.validNetworks[item.id].filter(x => x[this.filterMatchKey] === item[this.filterMatchKey])
|
||||
const filtered = this.networks.filter(x => x[this.filterMatchKey] === item[this.filterMatchKey])
|
||||
if (this.hypervisor === 'KVM') {
|
||||
this.unableToMatch = filtered.length === 0
|
||||
this.validNetworks[item.id] = filtered.length === 0 ? this.networks : filtered.concat(this.networks.filter(x => filtered.includes(x)))
|
||||
} else {
|
||||
this.validNetworks[item.id] = filtered
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setDefaultValues()
|
||||
@ -209,6 +229,8 @@ export default {
|
||||
},
|
||||
setIpAddressEnabled (nic, network) {
|
||||
this.ipAddressesEnabled[nic.id] = network && network.type !== 'L2'
|
||||
this.ipAddresses[nic.id] = (!network || network.type === 'L2') ? null : 'auto'
|
||||
this.values[nic.id] = network ? network.id : null
|
||||
this.indexNum = (this.indexNum % 2) + 1
|
||||
},
|
||||
setIpAddress (nicId, autoAssign, ipAddress) {
|
||||
@ -227,7 +249,11 @@ export default {
|
||||
this.sendValuesTimed()
|
||||
},
|
||||
handleNetworkChange (nic, networkId) {
|
||||
this.setIpAddressEnabled(nic, _.find(this.validNetworks[nic.id], (option) => option.id === networkId))
|
||||
if (this.hypervisor === 'KVM') {
|
||||
this.setIpAddressEnabled(nic, _.find(this.networks, (option) => option.id === networkId))
|
||||
} else {
|
||||
this.setIpAddressEnabled(nic, _.find(this.validNetworks[nic.id], (option) => option.id === networkId))
|
||||
}
|
||||
this.sendValuesTimed()
|
||||
},
|
||||
sendValuesTimed () {
|
||||
|
||||
@ -188,6 +188,8 @@ export default {
|
||||
const form = {}
|
||||
const rules = {}
|
||||
|
||||
let presetMacAddressIndex = 0
|
||||
|
||||
this.dataItems.forEach(record => {
|
||||
const ipAddressKey = 'ipAddress' + record.id
|
||||
const macAddressKey = 'macAddress' + record.id
|
||||
@ -202,6 +204,9 @@ export default {
|
||||
rules[macAddressKey] = [{ validator: this.validatorMacAddress }]
|
||||
if (record.macAddress) {
|
||||
form[macAddressKey] = record.macAddress
|
||||
} else if (this.preFillContent.macAddressArray && this.preFillContent.macAddressArray[presetMacAddressIndex]) {
|
||||
form[macAddressKey] = this.preFillContent.macAddressArray[presetMacAddressIndex]
|
||||
presetMacAddressIndex++
|
||||
}
|
||||
})
|
||||
this.form = reactive(form)
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="form-layout"
|
||||
:class="'form-layout'"
|
||||
@keyup.ctrl.enter="handleSubmit">
|
||||
<span v-if="uploadPercentage > 0">
|
||||
<loading-outlined />
|
||||
@ -42,7 +42,7 @@
|
||||
:placeholder="apiParams.url.description" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div v-if="currentForm === 'Upload'">
|
||||
<div v-else-if="currentForm === 'Upload'">
|
||||
<a-form-item :label="$t('label.templatefileupload')" name="file" ref="file">
|
||||
<a-upload-dragger
|
||||
:multiple="false"
|
||||
@ -66,7 +66,7 @@
|
||||
<a-input
|
||||
v-model:value="form.name"
|
||||
:placeholder="apiParams.name.description"
|
||||
v-focus="currentForm !== 'Create'"/>
|
||||
v-focus="currentForm === 'Upload'"/>
|
||||
</a-form-item>
|
||||
<a-form-item ref="displaytext" name="displaytext">
|
||||
<template #label>
|
||||
@ -214,7 +214,7 @@
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12" v-if="allowed && (hyperKVMShow || hyperCustomShow) && currentForm !== 'Upload'">
|
||||
<a-row :gutter="12" v-if="allowed && (hyperKVMShow || hyperCustomShow) && currentForm === 'Create'">
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-form-item ref="directdownload" name="directdownload">
|
||||
<template #label>
|
||||
@ -429,11 +429,8 @@
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
||||
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||
</div>
|
||||
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
||||
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
@ -674,7 +671,7 @@ export default {
|
||||
if (listResponse) {
|
||||
listhyperVisors = listhyperVisors.concat(listResponse)
|
||||
}
|
||||
if (this.currentForm !== 'Upload') {
|
||||
if (this.currentForm === 'Create') {
|
||||
listhyperVisors.push({
|
||||
name: 'Simulator'
|
||||
})
|
||||
@ -1087,7 +1084,7 @@ export default {
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
} else {
|
||||
} else if (this.currentForm === 'Upload') {
|
||||
this.loading = true
|
||||
if (this.fileList.length > 1) {
|
||||
this.$notification.error({
|
||||
|
||||
@ -34,6 +34,12 @@
|
||||
:rules="rules"
|
||||
@finish="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-alert
|
||||
v-if="selectedVmwareVcenter && isVmRunning"
|
||||
type="warning"
|
||||
:showIcon="true"
|
||||
:message="$t('message.import.running.instance.warning')"
|
||||
/>
|
||||
<a-form-item name="displayname" ref="displayname">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.displayname')" :tooltip="apiParams.displayname.description"/>
|
||||
@ -105,7 +111,7 @@
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="templateid" ref="templateid">
|
||||
<a-form-item name="templateid" ref="templateid" v-if="cluster.hypervisortype === 'KVM' && !selectedVmwareVcenter">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.templatename')" :tooltip="apiParams.templateid.description + '. ' + $t('message.template.import.vm.temporary')"/>
|
||||
</template>
|
||||
@ -146,42 +152,83 @@
|
||||
</a-row>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item name="converthostid" ref="converthostid">
|
||||
<check-box-select-pair
|
||||
layout="vertical"
|
||||
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
|
||||
:resourceKey="cluster.id"
|
||||
:selectOptions="kvmHostsForConversion"
|
||||
:checkBoxLabel="'(Optional) Select a KVM host in the cluster to perform the instance conversion through virt-v2v'"
|
||||
:defaultCheckBoxValue="false"
|
||||
:reversed="false"
|
||||
@handle-checkselectpair-change="updateSelectedKvmHostForConversion"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="convertstorageoption" ref="convertstorageoption">
|
||||
<check-box-select-pair
|
||||
layout="vertical"
|
||||
style="margin-bottom: 20px"
|
||||
v-if="cluster.hypervisortype === 'KVM' && selectedVmwareVcenter"
|
||||
:resourceKey="cluster.id"
|
||||
:selectOptions="storageOptionsForConversion"
|
||||
:checkBoxLabel="'(Optional) Select a Storage temporary destination for the converted disks through virt-v2v'"
|
||||
:defaultCheckBoxValue="false"
|
||||
:reversed="false"
|
||||
@handle-checkselectpair-change="updateSelectedStorageOptionForConversion"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="showStoragePoolsForConversion" name="convertstoragepool" ref="convertstoragepool" :label="$t('label.storagepool')">
|
||||
<a-select
|
||||
v-model:value="form.convertstoragepoolid"
|
||||
defaultActiveFirstOption
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="val => { selectedStoragePoolForConversion = val }">
|
||||
<a-select-option v-for="(pool) in storagePoolsForConversion" :key="pool.id" :label="pool.name">
|
||||
{{ pool.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="serviceofferingid" ref="serviceofferingid">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/>
|
||||
</template>
|
||||
<compute-offering-selection
|
||||
:compute-items="computeOfferings"
|
||||
:loading="computeOfferingLoading"
|
||||
:rowCount="totalComputeOfferings"
|
||||
:value="computeOffering ? computeOffering.id : ''"
|
||||
:minimumCpunumber="isVmRunning ? resource.cpunumber : null"
|
||||
:minimumCpuspeed="isVmRunning ? resource.cpuspeed : null"
|
||||
:minimumMemory="isVmRunning ? resource.memory : null"
|
||||
:allowAllOfferings="selectedVmwareVcenter ? true : false"
|
||||
size="small"
|
||||
@select-compute-item="($event) => updateComputeOffering($event)"
|
||||
@handle-search-filter="($event) => fetchComputeOfferings($event)" />
|
||||
<compute-selection
|
||||
class="row-element"
|
||||
v-if="computeOffering && (computeOffering.iscustomized || computeOffering.iscustomizediops)"
|
||||
:isCustomized="computeOffering.iscustomized"
|
||||
:isCustomizedIOps="'iscustomizediops' in computeOffering && computeOffering.iscustomizediops"
|
||||
:cpuNumberInputDecorator="cpuNumberKey"
|
||||
:cpuSpeedInputDecorator="cpuSpeedKey"
|
||||
:memoryInputDecorator="memoryKey"
|
||||
:computeOfferingId="computeOffering.id"
|
||||
:preFillContent="resource"
|
||||
:isConstrained="'serviceofferingdetails' in computeOffering"
|
||||
:minCpu="getMinCpu()"
|
||||
:maxCpu="getMaxCpu()"
|
||||
:minMemory="getMinMemory()"
|
||||
:maxMemory="getMaxMemory()"
|
||||
:cpuSpeed="getCPUSpeed()"
|
||||
@update-iops-value="updateFieldValue"
|
||||
@update-compute-cpunumber="updateFieldValue"
|
||||
@update-compute-cpuspeed="updateCpuSpeed"
|
||||
@update-compute-memory="updateFieldValue" />
|
||||
</a-form-item>
|
||||
<compute-offering-selection
|
||||
:compute-items="computeOfferings"
|
||||
:loading="computeOfferingLoading"
|
||||
:rowCount="totalComputeOfferings"
|
||||
:value="computeOffering ? computeOffering.id : ''"
|
||||
:minimumCpunumber="isVmRunning ? resource.cpunumber : null"
|
||||
:minimumCpuspeed="isVmRunning ? resource.cpuspeed : null"
|
||||
:minimumMemory="isVmRunning ? resource.memory : null"
|
||||
size="small"
|
||||
@select-compute-item="($event) => updateComputeOffering($event)"
|
||||
@handle-search-filter="($event) => fetchComputeOfferings($event)" />
|
||||
<compute-selection
|
||||
class="row-element"
|
||||
v-if="computeOffering && (computeOffering.iscustomized || computeOffering.iscustomizediops)"
|
||||
:isCustomized="computeOffering.iscustomized"
|
||||
:isCustomizedIOps="'iscustomizediops' in computeOffering && computeOffering.iscustomizediops"
|
||||
:cpuNumberInputDecorator="cpuNumberKey"
|
||||
:cpuSpeedInputDecorator="cpuSpeedKey"
|
||||
:memoryInputDecorator="memoryKey"
|
||||
:computeOfferingId="computeOffering.id"
|
||||
:preFillContent="resource"
|
||||
:isConstrained="'serviceofferingdetails' in computeOffering"
|
||||
:minCpu="getMinCpu()"
|
||||
:maxCpu="getMaxCpu()"
|
||||
:minMemory="getMinMemory()"
|
||||
:maxMemory="getMaxMemory()"
|
||||
:cpuSpeed="getCPUSpeed()"
|
||||
@update-iops-value="updateFieldValue"
|
||||
@update-compute-cpunumber="updateFieldValue"
|
||||
@update-compute-cpuspeed="updateCpuSpeed"
|
||||
@update-compute-memory="updateFieldValue" />
|
||||
<div v-if="resource.disk && resource.disk.length > 1">
|
||||
<a-form-item name="selection" ref="selection">
|
||||
<template #label>
|
||||
@ -219,12 +266,25 @@
|
||||
</template>
|
||||
<span>{{ $t('message.ip.address.changes.effect.after.vm.restart') }}</span>
|
||||
</a-form-item>
|
||||
<a-row :gutter="12" justify="end">
|
||||
<a-col style="text-align: right">
|
||||
<a-form-item name="forced" ref="forced">
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="selectedVmwareVcenter ? $t('label.allow.duplicate.macaddresses') : $t('label.forced')"
|
||||
:tooltip="apiParams.forced.description"/>
|
||||
</template>
|
||||
<a-switch v-model:checked="form.forced" @change="val => { switches.forced = val }" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<multi-network-selection
|
||||
:items="nics"
|
||||
:zoneId="cluster.zoneid"
|
||||
:selectionEnabled="false"
|
||||
:filterUnimplementedNetworks="true"
|
||||
filterMatchKey="broadcasturi"
|
||||
:hypervisor="this.cluster.hypervisortype"
|
||||
@select-multi-network="updateMultiNetworkOffering" />
|
||||
</div>
|
||||
<a-row v-else style="margin: 12px 0">
|
||||
@ -236,21 +296,13 @@
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-form-item name="migrateallowed" ref="migrateallowed">
|
||||
<a-form-item name="migrateallowed" ref="migrateallowed" v-if="!selectedVmwareVcenter">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.migrate.allowed')" :tooltip="apiParams.migrateallowed.description"/>
|
||||
</template>
|
||||
<a-switch v-model:checked="form.migrateallowed" @change="val => { switches.migrateAllowed = val }" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-form-item name="forced" ref="forced">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.forced')" :tooltip="apiParams.forced.description"/>
|
||||
</template>
|
||||
<a-switch v-model:checked="form.forced" @change="val => { switches.forced = val }" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
||||
@ -276,6 +328,7 @@ import MultiDiskSelection from '@views/compute/wizard/MultiDiskSelection'
|
||||
import MultiNetworkSelection from '@views/compute/wizard/MultiNetworkSelection'
|
||||
import OsLogo from '@/components/widgets/OsLogo'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import CheckBoxSelectPair from '@/components/CheckBoxSelectPair'
|
||||
|
||||
export default {
|
||||
name: 'ImportUnmanagedInstances',
|
||||
@ -287,13 +340,22 @@ export default {
|
||||
MultiDiskSelection,
|
||||
MultiNetworkSelection,
|
||||
OsLogo,
|
||||
ResourceIcon
|
||||
ResourceIcon,
|
||||
CheckBoxSelectPair
|
||||
},
|
||||
props: {
|
||||
cluster: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
importsource: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
hypervisor: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
@ -301,6 +363,10 @@ export default {
|
||||
isOpen: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
selectedVmwareVcenter: {
|
||||
type: Array,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
@ -335,7 +401,22 @@ export default {
|
||||
minIopsKey: 'minIops',
|
||||
maxIopsKey: 'maxIops',
|
||||
switches: {},
|
||||
loading: false
|
||||
loading: false,
|
||||
kvmHostsForConversion: [],
|
||||
selectedKvmHostForConversion: null,
|
||||
storageOptionsForConversion: [
|
||||
{
|
||||
id: 'secondary',
|
||||
name: 'Secondary Storage'
|
||||
}, {
|
||||
id: 'primary',
|
||||
name: 'Primary Storage'
|
||||
}
|
||||
],
|
||||
storagePoolsForConversion: [],
|
||||
selectedStorageOptionForConversion: null,
|
||||
selectedStoragePoolForConversion: null,
|
||||
showStoragePoolsForConversion: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
@ -455,7 +536,11 @@ export default {
|
||||
nic.broadcasturi = 'pvlan://' + nic.vlanid + '-i' + nic.isolatedpvlan
|
||||
}
|
||||
}
|
||||
nic.meta = this.getMeta(nic, { macaddress: 'mac', vlanid: 'vlan', networkname: 'network' })
|
||||
if (this.cluster.hypervisortype === 'VMWare') {
|
||||
nic.meta = this.getMeta(nic, { macaddress: 'mac', vlanid: 'vlan', networkname: 'network' })
|
||||
} else {
|
||||
nic.meta = this.getMeta(nic, { macaddress: 'mac', vlanid: 'vlan' })
|
||||
}
|
||||
nics.push(nic)
|
||||
}
|
||||
}
|
||||
@ -496,6 +581,7 @@ export default {
|
||||
pageSize: 10,
|
||||
page: 1
|
||||
})
|
||||
this.fetchKvmHostsForConversion()
|
||||
},
|
||||
getMeta (obj, metaKeys) {
|
||||
var meta = []
|
||||
@ -670,6 +756,74 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
fetchKvmHostsForConversion () {
|
||||
api('listHosts', {
|
||||
clusterid: this.cluster.id,
|
||||
hypervisor: this.cluster.hypervisortype,
|
||||
type: 'Routing',
|
||||
state: 'Up',
|
||||
resourcestate: 'Enabled'
|
||||
}).then(json => {
|
||||
this.kvmHostsForConversion = json.listhostsresponse.host || []
|
||||
})
|
||||
},
|
||||
fetchStoragePoolsForConversion () {
|
||||
if (this.selectedStorageOptionForConversion === 'primary') {
|
||||
api('listStoragePools', {
|
||||
zoneid: this.cluster.zoneid,
|
||||
state: 'Up'
|
||||
}).then(json => {
|
||||
this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || []
|
||||
})
|
||||
} else if (this.selectedStorageOptionForConversion === 'local') {
|
||||
const kvmHost = this.kvmHostsForConversion.filter(x => x.id === this.selectedKvmHostForConversion)[0]
|
||||
api('listStoragePools', {
|
||||
scope: 'HOST',
|
||||
ipaddress: kvmHost.ipaddress,
|
||||
state: 'Up'
|
||||
}).then(json => {
|
||||
this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || []
|
||||
})
|
||||
}
|
||||
},
|
||||
updateSelectedKvmHostForConversion (clusterid, checked, value) {
|
||||
if (checked) {
|
||||
this.selectedKvmHostForConversion = value
|
||||
const kvmHost = this.kvmHostsForConversion.filter(x => x.id === this.selectedKvmHostForConversion)[0]
|
||||
if (kvmHost.islocalstorageactive) {
|
||||
this.storageOptionsForConversion.push({
|
||||
id: 'local',
|
||||
name: 'Host Local Storage'
|
||||
})
|
||||
} else {
|
||||
this.resetStorageOptionsForConversion()
|
||||
}
|
||||
} else {
|
||||
this.selectedKvmHostForConversion = null
|
||||
this.resetStorageOptionsForConversion()
|
||||
}
|
||||
},
|
||||
updateSelectedStorageOptionForConversion (clusterid, checked, value) {
|
||||
if (checked) {
|
||||
this.selectedStorageOptionForConversion = value
|
||||
this.fetchStoragePoolsForConversion()
|
||||
this.showStoragePoolsForConversion = value !== 'secondary'
|
||||
} else {
|
||||
this.showStoragePoolsForConversion = false
|
||||
this.selectedStoragePoolForConversion = null
|
||||
}
|
||||
},
|
||||
resetStorageOptionsForConversion () {
|
||||
this.storageOptionsForConversion = [
|
||||
{
|
||||
id: 'secondary',
|
||||
name: 'Secondary Storage'
|
||||
}, {
|
||||
id: 'primary',
|
||||
name: 'Primary Storage'
|
||||
}
|
||||
]
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
if (this.loading) return
|
||||
@ -678,7 +832,13 @@ export default {
|
||||
const params = {
|
||||
name: this.resource.name,
|
||||
clusterid: this.cluster.id,
|
||||
displayname: values.displayname
|
||||
displayname: values.displayname,
|
||||
importsource: this.importsource,
|
||||
hypervisor: this.hypervisor
|
||||
}
|
||||
var importapi = 'importUnmanagedInstance'
|
||||
if (this.isExternalImport || this.isDiskImport || this.selectedVmwareVcenter) {
|
||||
importapi = 'importVm'
|
||||
}
|
||||
if (!this.computeOffering || !this.computeOffering.id) {
|
||||
this.$notification.error({
|
||||
@ -722,6 +882,24 @@ export default {
|
||||
})
|
||||
}
|
||||
}
|
||||
if (this.selectedVmwareVcenter) {
|
||||
if (this.selectedVmwareVcenter.existingvcenterid) {
|
||||
params.existingvcenterid = this.selectedVmwareVcenter.existingvcenterid
|
||||
} else {
|
||||
params.vcenter = this.selectedVmwareVcenter.vcenter
|
||||
params.datacentername = this.selectedVmwareVcenter.datacentername
|
||||
params.username = this.selectedVmwareVcenter.username
|
||||
params.password = this.selectedVmwareVcenter.password
|
||||
}
|
||||
params.hostip = this.resource.hostname
|
||||
params.clustername = this.resource.clustername
|
||||
if (this.selectedKvmHostForConversion) {
|
||||
params.convertinstancehostid = this.selectedKvmHostForConversion
|
||||
}
|
||||
if (this.selectedStoragePoolForConversion) {
|
||||
params.convertinstancepoolid = this.selectedStoragePoolForConversion
|
||||
}
|
||||
}
|
||||
var keys = ['hostname', 'domainid', 'projectid', 'account', 'migrateallowed', 'forced']
|
||||
if (this.templateType !== 'auto') {
|
||||
keys.push('templateid')
|
||||
@ -771,28 +949,46 @@ export default {
|
||||
}
|
||||
}
|
||||
this.updateLoading(true)
|
||||
const name = this.resource.name
|
||||
api('importUnmanagedInstance', params).then(json => {
|
||||
const jobId = json.importunmanagedinstanceresponse.jobid
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
title: this.$t('label.import.instance'),
|
||||
description: name,
|
||||
loadingMessage: `${this.$t('label.import.instance')} ${name} ${this.$t('label.in.progress')}`,
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
successMessage: this.$t('message.success.import.instance') + ' ' + name,
|
||||
successMethod: result => {
|
||||
this.$emit('refresh-data')
|
||||
const name = params.name
|
||||
return new Promise((resolve, reject) => {
|
||||
api(importapi, params).then(response => {
|
||||
var jobId
|
||||
if (this.isDiskImport || this.isExternalImport || this.selectedVmwareVcenter) {
|
||||
jobId = response.importvmresponse.jobid
|
||||
} else {
|
||||
jobId = response.importunmanagedinstanceresponse.jobid
|
||||
}
|
||||
let msgLoading = this.$t('label.import.instance') + ' ' + name + ' ' + this.$t('label.in.progress')
|
||||
if (this.selectedKvmHostForConversion) {
|
||||
const kvmHost = this.kvmHostsForConversion.filter(x => x.id === this.selectedKvmHostForConversion)[0]
|
||||
msgLoading += ' on host ' + kvmHost.name
|
||||
}
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
title: this.$t('label.import.instance'),
|
||||
description: name,
|
||||
loadingMessage: msgLoading,
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
successMessage: this.$t('message.success.import.instance') + ' ' + name,
|
||||
successMethod: result => {
|
||||
this.$emit('refresh-data')
|
||||
resolve(result)
|
||||
},
|
||||
errorMethod: (result) => {
|
||||
this.updateLoading(false)
|
||||
reject(result.jobresult.errortext)
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.updateLoading(false)
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.closeAction()
|
||||
this.updateLoading(false)
|
||||
})
|
||||
this.closeAction()
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.updateLoading(false)
|
||||
})
|
||||
}).catch((error) => {
|
||||
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||
}).catch(() => {
|
||||
this.$emit('loading-changed', false)
|
||||
})
|
||||
},
|
||||
updateLoading (value) {
|
||||
|
||||
@ -37,74 +37,137 @@
|
||||
:md="24">
|
||||
<div>
|
||||
<a-card>
|
||||
<a-alert type="info" :showIcon="true" :message="$t('label.desc.importexportinstancewizard')" :description="$t('message.desc.importexportinstancewizard')" />
|
||||
<a-alert
|
||||
type="info"
|
||||
:showIcon="true"
|
||||
:message="wizardTitle"
|
||||
>
|
||||
<template #description>
|
||||
<span v-html="wizardDescription" />
|
||||
</template>
|
||||
</a-alert>
|
||||
<br />
|
||||
<a-form
|
||||
style="min-width: 170px"
|
||||
:ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-col :md="24" :lg="8">
|
||||
<a-form-item name="zoneid" ref="zoneid" :label="$t('label.zoneid')">
|
||||
<a-select
|
||||
v-model:value="form.zoneid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="onSelectZoneId"
|
||||
:loading="optionLoading.zones"
|
||||
v-focus="true"
|
||||
>
|
||||
<a-select-option v-for="zoneitem in zoneSelectOptions" :key="zoneitem.value" :label="zoneitem.label">
|
||||
<span>
|
||||
<resource-icon v-if="zoneitem.icon" :image="zoneitem.icon" size="1x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ zoneitem.label }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="24" :lg="8">
|
||||
<a-form-item
|
||||
name="podid"
|
||||
ref="podid"
|
||||
:label="$t('label.podid')">
|
||||
<a-select
|
||||
v-model:value="form.podid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="filterOption"
|
||||
:options="podSelectOptions"
|
||||
:loading="optionLoading.pods"
|
||||
@change="onSelectPodId"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="24" :lg="8">
|
||||
<a-form-item
|
||||
name="clusterid"
|
||||
ref="clusterid"
|
||||
:label="$t('label.clusterid')">
|
||||
<a-select
|
||||
v-model:value="form.clusterid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="filterOption"
|
||||
:options="clusterSelectOptions"
|
||||
:loading="optionLoading.clusters"
|
||||
@change="onSelectClusterId"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-form>
|
||||
<a-divider />
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-form
|
||||
style="min-width: 170px"
|
||||
:ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-col :md="24" :lg="24">
|
||||
<a-form-item name="sourcehypervisor" ref="sourcehypervisor" :label="$t('label.source')">
|
||||
<a-radio-group
|
||||
style="text-align: center; width: 100%"
|
||||
v-model:value="form.sourceHypervisor"
|
||||
@change="selected => { onSelectHypervisor(selected.target.value) }"
|
||||
buttonStyle="solid">
|
||||
<a-radio-button value="vmware" style="width: 50%; text-align: center">
|
||||
VMware
|
||||
</a-radio-button>
|
||||
<a-radio-button value="kvm" style="width: 50%; text-align: center">
|
||||
KVM
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item name="sourceaction" ref="sourceaction" :label="$t('label.action')" v-if="sourceActions">
|
||||
<a-select
|
||||
v-model:value="form.sourceAction"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="onSelectSourceAction"
|
||||
:loading="optionLoading.sourcehypervisor"
|
||||
v-focus="true"
|
||||
>
|
||||
<a-select-option v-for="opt in sourceActions" :key="opt.name" :label="opt.label">
|
||||
<span>
|
||||
{{ opt.label }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-form>
|
||||
</a-col>
|
||||
<!-- ------------ -->
|
||||
<!-- RIGHT COLUMN -->
|
||||
<!-- ------------ -->
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-form
|
||||
style="min-width: 170px"
|
||||
:ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item
|
||||
name="zoneid"
|
||||
ref="zoneid"
|
||||
:label="isMigrateFromVmware ? $t('label.destination.zone') : $t('label.zoneid')"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="form.zoneid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="onSelectZoneId"
|
||||
:loading="optionLoading.zones"
|
||||
>
|
||||
<a-select-option v-for="zoneitem in zoneSelectOptions" :key="zoneitem.value" :label="zoneitem.label">
|
||||
<span>
|
||||
<resource-icon v-if="zoneitem.icon" :image="zoneitem.icon" size="1x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ zoneitem.label }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
name="podid"
|
||||
ref="podid"
|
||||
:label="isMigrateFromVmware ? $t('label.destination.pod') : $t('label.podid')">
|
||||
<a-select
|
||||
v-model:value="form.podid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="filterOption"
|
||||
:options="podSelectOptions"
|
||||
:loading="optionLoading.pods"
|
||||
@change="onSelectPodId"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
name="clusterid"
|
||||
ref="clusterid"
|
||||
:label="isMigrateFromVmware ? $t('label.destination.cluster') : $t('label.clusterid')">
|
||||
<a-select
|
||||
v-model:value="form.clusterid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="filterOption"
|
||||
:options="clusterSelectOptions"
|
||||
:loading="optionLoading.clusters"
|
||||
@change="onSelectClusterId"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="isDestinationKVM && isMigrateFromVmware && clusterId != undefined">
|
||||
<SelectVmwareVcenter
|
||||
@loadingVmwareUnmanagedInstances="() => this.unmanagedInstancesLoading = true"
|
||||
@listedVmwareUnmanagedInstances="($e) => onListUnmanagedInstancesFromVmware($e)"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider />
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="24" :lg="!isMigrateFromVmware ? 12 : 24">
|
||||
<a-card class="instances-card">
|
||||
<template #title>
|
||||
{{ $t('label.unmanaged.instances') }}
|
||||
@ -172,7 +235,7 @@
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="24" :lg="12">
|
||||
<a-col :md="24" :lg="12" v-if="!isMigrateFromVmware">
|
||||
<a-card class="instances-card">
|
||||
<template #title>
|
||||
{{ $t('label.managed.instances') }}
|
||||
@ -261,7 +324,10 @@
|
||||
class="importform"
|
||||
:resource="selectedUnmanagedInstance"
|
||||
:cluster="selectedCluster"
|
||||
:importsource="selectedSourceAction"
|
||||
:hypervisor="this.destinationHypervisor"
|
||||
:isOpen="showUnmanageForm"
|
||||
:selectedVmwareVcenter="selectedVmwareVcenter"
|
||||
@refresh-data="fetchInstances"
|
||||
@close-action="closeImportUnmanagedInstanceForm"
|
||||
@loading-changed="updateManageInstanceActionLoading"
|
||||
@ -273,6 +339,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { api } from '@/api'
|
||||
import _ from 'lodash'
|
||||
@ -281,6 +348,7 @@ import Status from '@/components/widgets/Status'
|
||||
import SearchView from '@/components/view/SearchView'
|
||||
import ImportUnmanagedInstances from '@/views/tools/ImportUnmanagedInstance'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import SelectVmwareVcenter from '@/views/tools/SelectVmwareVcenter'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -288,10 +356,59 @@ export default {
|
||||
Status,
|
||||
SearchView,
|
||||
ImportUnmanagedInstances,
|
||||
ResourceIcon
|
||||
ResourceIcon,
|
||||
SelectVmwareVcenter
|
||||
},
|
||||
name: 'ManageVms',
|
||||
data () {
|
||||
const AllSourceActions = [
|
||||
{
|
||||
name: 'unmanaged',
|
||||
label: 'Manage/Unmanage existing instances',
|
||||
sourceDestHypervisors: {
|
||||
vmware: 'vmware',
|
||||
kvm: 'kvm'
|
||||
},
|
||||
wizardTitle: this.$t('label.desc.importexportinstancewizard'),
|
||||
wizardDescription: this.$t('message.desc.importexportinstancewizard')
|
||||
},
|
||||
{
|
||||
name: 'vmware',
|
||||
label: 'Migrate existing instances to KVM',
|
||||
sourceDestHypervisors: {
|
||||
vmware: 'kvm'
|
||||
},
|
||||
wizardTitle: this.$t('label.desc.importmigratefromvmwarewizard'),
|
||||
wizardDescription: this.$t('message.desc.importmigratefromvmwarewizard')
|
||||
},
|
||||
{
|
||||
name: 'external',
|
||||
label: 'Import libvirt domain from KVM Host',
|
||||
sourceDestHypervisors: {
|
||||
kvm: 'kvm'
|
||||
},
|
||||
wizardTitle: 'Import libvirt domain from KVM Host',
|
||||
wizardDescription: 'Import libvirt domain from KVM Host'
|
||||
},
|
||||
{
|
||||
name: 'local',
|
||||
label: 'Import QCOW image from Local Storage',
|
||||
sourceDestHypervisors: {
|
||||
kvm: 'kvm'
|
||||
},
|
||||
wizardTitle: 'Import QCOW image from Local Storage',
|
||||
wizardDescription: 'Import QCOW image from Local Storage'
|
||||
},
|
||||
{
|
||||
name: 'shared',
|
||||
label: 'Import QCOW image from Shared Storage',
|
||||
sourceDestHypervisors: {
|
||||
kvm: 'kvm'
|
||||
},
|
||||
wizardTitle: 'Import QCOW image from Shared Storage',
|
||||
wizardDescription: 'Import QCOW image from Shared Storage'
|
||||
}
|
||||
]
|
||||
const unmanagedInstancesColumns = [
|
||||
{
|
||||
title: this.$t('label.name'),
|
||||
@ -307,6 +424,10 @@ export default {
|
||||
title: this.$t('label.hostname'),
|
||||
dataIndex: 'hostname'
|
||||
},
|
||||
{
|
||||
title: this.$t('label.clustername'),
|
||||
dataIndex: 'clustername'
|
||||
},
|
||||
{
|
||||
title: this.$t('label.ostypename'),
|
||||
dataIndex: 'osdisplayname'
|
||||
@ -339,12 +460,15 @@ export default {
|
||||
]
|
||||
return {
|
||||
options: {
|
||||
hypervisors: [],
|
||||
zones: [],
|
||||
pods: [],
|
||||
clusters: []
|
||||
},
|
||||
rowCount: {},
|
||||
optionLoading: {
|
||||
sourceaction: false,
|
||||
hypervisors: false,
|
||||
zones: false,
|
||||
pods: false,
|
||||
clusters: false
|
||||
@ -366,15 +490,24 @@ export default {
|
||||
managed: {}
|
||||
},
|
||||
itemCount: {},
|
||||
hypervisors: [],
|
||||
sourceHypervisor: 'vmware',
|
||||
destinationHypervisor: 'vmware',
|
||||
sourceActions: undefined,
|
||||
selectedSourceAction: undefined,
|
||||
wizardTitle: this.$t('label.desc.importexportinstancewizard'),
|
||||
wizardDescription: this.$t('message.desc.importexportinstancewizard'),
|
||||
zone: {},
|
||||
zoneId: undefined,
|
||||
podId: undefined,
|
||||
clusterId: undefined,
|
||||
listInstancesApi: {
|
||||
unmanaged: 'listUnmanagedInstances',
|
||||
managed: 'listVirtualMachines'
|
||||
managed: 'listVirtualMachines',
|
||||
migratefromvmware: 'listVmwareDcVms'
|
||||
},
|
||||
unmanagedInstancesColumns,
|
||||
AllSourceActions,
|
||||
unmanagedInstancesLoading: false,
|
||||
unmanagedInstances: [],
|
||||
unmanagedInstancesSelectedRowKeys: [],
|
||||
@ -385,7 +518,8 @@ export default {
|
||||
managedInstancesSelectedRowKeys: [],
|
||||
showUnmanageForm: false,
|
||||
selectedUnmanagedInstance: {},
|
||||
query: {}
|
||||
query: {},
|
||||
selectedVmwareVcenter: undefined
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -405,11 +539,23 @@ export default {
|
||||
}
|
||||
return true
|
||||
},
|
||||
isUnmanaged () {
|
||||
return this.selectedSourceAction === 'unmanaged'
|
||||
},
|
||||
isUnmanagedOrExternal () {
|
||||
return ((this.isUnmanaged) || this.selectedSourceAction === 'external')
|
||||
},
|
||||
isMigrateFromVmware () {
|
||||
return this.selectedSourceAction === 'vmware'
|
||||
},
|
||||
isDestinationKVM () {
|
||||
return this.destinationHypervisor === 'kvm'
|
||||
},
|
||||
params () {
|
||||
return {
|
||||
zones: {
|
||||
list: 'listZones',
|
||||
isLoad: true,
|
||||
isLoad: false,
|
||||
field: 'zoneid',
|
||||
options: {
|
||||
showicon: true
|
||||
@ -428,7 +574,8 @@ export default {
|
||||
isLoad: false,
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
podid: this.podId
|
||||
podid: this.podId,
|
||||
hypervisor: this.destinationHypervisor
|
||||
},
|
||||
field: 'clusterid'
|
||||
}
|
||||
@ -495,12 +642,15 @@ export default {
|
||||
methods: {
|
||||
initForm () {
|
||||
this.formRef = ref()
|
||||
this.form = reactive({})
|
||||
this.form = reactive({
|
||||
sourceHypervisor: this.sourceHypervisor
|
||||
})
|
||||
this.rules = reactive({})
|
||||
},
|
||||
fetchData () {
|
||||
this.unmanagedInstances = []
|
||||
this.managedInstances = []
|
||||
this.onSelectHypervisor(this.sourceHypervisor)
|
||||
_.each(this.params, (param, name) => {
|
||||
if (param.isLoad) {
|
||||
this.fetchOptions(param, name)
|
||||
@ -603,6 +753,24 @@ export default {
|
||||
this.managedInstances = []
|
||||
this.managedInstancesSelectedRowKeys = []
|
||||
},
|
||||
onSelectHypervisor (value) {
|
||||
this.sourceHypervisor = value
|
||||
this.sourceActions = this.AllSourceActions.filter(x => x.sourceDestHypervisors[value])
|
||||
this.form.sourceAction = this.sourceActions[0].name || ''
|
||||
this.onSelectSourceAction(this.form.sourceAction)
|
||||
},
|
||||
onSelectSourceAction (value) {
|
||||
this.selectedSourceAction = value
|
||||
const selectedAction = _.find(this.AllSourceActions, (option) => option.name === value)
|
||||
this.destinationHypervisor = selectedAction.sourceDestHypervisors[this.sourceHypervisor]
|
||||
this.wizardTitle = selectedAction.wizardTitle
|
||||
this.wizardDescription = selectedAction.wizardDescription
|
||||
this.form.zoneid = undefined
|
||||
this.form.podid = undefined
|
||||
this.form.clusterid = undefined
|
||||
this.fetchOptions(this.params.zones, 'zones')
|
||||
this.resetLists()
|
||||
},
|
||||
onSelectZoneId (value) {
|
||||
this.zoneId = value
|
||||
this.podId = null
|
||||
@ -617,6 +785,7 @@ export default {
|
||||
onSelectPodId (value) {
|
||||
this.podId = value
|
||||
this.resetLists()
|
||||
this.clusterId = null
|
||||
this.form.clusterid = undefined
|
||||
this.updateQuery('podid', value)
|
||||
this.fetchOptions(this.params.clusters, 'clusters', value)
|
||||
@ -628,8 +797,8 @@ export default {
|
||||
this.fetchInstances()
|
||||
},
|
||||
fetchInstances () {
|
||||
if (this.selectedCluster.hypervisortype === 'VMware') {
|
||||
this.fetchUnmanagedInstances()
|
||||
this.fetchUnmanagedInstances()
|
||||
if (this.isUnmanaged) {
|
||||
this.fetchManagedInstances()
|
||||
}
|
||||
},
|
||||
@ -653,12 +822,27 @@ export default {
|
||||
}
|
||||
this.unmanagedInstancesLoading = true
|
||||
this.searchParams.unmanaged = params
|
||||
api(this.listInstancesApi.unmanaged, params).then(json => {
|
||||
const listUnmanagedInstances = json.listunmanagedinstancesresponse.unmanagedinstance
|
||||
|
||||
let apiName = this.listInstancesApi.unmanaged
|
||||
if (this.isMigrateFromVmware && this.selectedVmwareVcenter) {
|
||||
apiName = this.listInstancesApi.migratefromvmware
|
||||
if (this.selectedVmwareVcenter.vcenter) {
|
||||
params.datacentername = this.selectedVmwareVcenter.datacentername
|
||||
params.vcenter = this.selectedVmwareVcenter.vcenter
|
||||
params.username = this.selectedVmwareVcenter.username
|
||||
params.password = this.selectedVmwareVcenter.password
|
||||
} else {
|
||||
params.existingvcenterid = this.selectedVmwareVcenter.existingvcenterid
|
||||
}
|
||||
}
|
||||
|
||||
api(apiName, params).then(json => {
|
||||
const response = this.isMigrateFromVmware ? json.listvmwaredcvmsresponse : json.listunmanagedinstancesresponse
|
||||
const listUnmanagedInstances = response.unmanagedinstance
|
||||
if (this.arrayHasItems(listUnmanagedInstances)) {
|
||||
this.unmanagedInstances = this.unmanagedInstances.concat(listUnmanagedInstances)
|
||||
}
|
||||
this.itemCount.unmanaged = json.listunmanagedinstancesresponse.count
|
||||
this.itemCount.unmanaged = response.count
|
||||
}).finally(() => {
|
||||
this.unmanagedInstancesLoading = false
|
||||
})
|
||||
@ -728,7 +912,18 @@ export default {
|
||||
this.selectedUnmanagedInstance.ostypename = this.selectedUnmanagedInstance.osdisplayname
|
||||
this.selectedUnmanagedInstance.state = this.selectedUnmanagedInstance.powerstate
|
||||
}
|
||||
this.showUnmanageForm = true
|
||||
if (this.isMigrateFromVmware && this.selectedUnmanagedInstance.state === 'PowerOn' && this.selectedUnmanagedInstance.ostypename.toLowerCase().includes('windows')) {
|
||||
message.error({
|
||||
content: () => 'Cannot import Running Windows VMs, please gracefully shutdown the source VM before importing',
|
||||
style: {
|
||||
marginTop: '20vh',
|
||||
color: 'red'
|
||||
}
|
||||
})
|
||||
this.showUnmanageForm = false
|
||||
} else {
|
||||
this.showUnmanageForm = true
|
||||
}
|
||||
},
|
||||
closeImportUnmanagedInstanceForm () {
|
||||
this.selectedUnmanagedInstance = {}
|
||||
@ -780,6 +975,12 @@ export default {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
onListUnmanagedInstancesFromVmware (obj) {
|
||||
this.selectedVmwareVcenter = obj.params
|
||||
this.unmanagedInstances = obj.response.unmanagedinstance
|
||||
this.itemCount.unmanaged = obj.response.count
|
||||
this.unmanagedInstancesLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
273
ui/src/views/tools/SelectVmwareVcenter.vue
Normal file
273
ui/src/views/tools/SelectVmwareVcenter.vue
Normal file
@ -0,0 +1,273 @@
|
||||
// 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.
|
||||
|
||||
<template>
|
||||
<a-form
|
||||
:ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
@finish="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-col :md="24" :lg="24">
|
||||
<div>
|
||||
<a-form-item :label="$t('label.select.source.vcenter.datacenter')" name="vmwareopt" ref="vmwareopt">
|
||||
<a-radio-group
|
||||
style="text-align: center; width: 100%"
|
||||
v-model:value="vcenterSelectedOption"
|
||||
buttonStyle="solid">
|
||||
<a-radio-button value="existing" style="width: 50%; text-align: center">
|
||||
{{ $t('label.existing') }}
|
||||
</a-radio-button>
|
||||
<a-radio-button value="new" style="width: 50%; text-align: center">
|
||||
{{ $t('label.external') }}
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div v-if="vcenterSelectedOption === 'existing'">
|
||||
<a-form-item name="sourcezoneid" ref="sourcezoneid" :label="$t('label.zoneid')">
|
||||
<a-select
|
||||
v-model:value="form.sourcezoneid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="onSelectZoneId"
|
||||
:loading="loading"
|
||||
>
|
||||
<a-select-option v-for="zoneitem in zones" :key="zoneitem.id" :label="zoneitem.name">
|
||||
<span>
|
||||
<resource-icon v-if="zoneitem.icon" :image="zoneitem.icon" size="1x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ zoneitem.name }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<div v-if="sourcezoneid">
|
||||
<a-form-item :label="$t('label.vcenter')" name="vmwaredatacenter" ref="vmwaredatacenter" v-if="existingvcenter.length > 0">
|
||||
<a-select
|
||||
v-model:value="form.vmwaredatacenter"
|
||||
:loading="loading"
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:placeholder="$t('label.vcenter.datacenter')"
|
||||
@change="onSelectExistingVmwareDatacenter">
|
||||
<a-select-option v-for="opt in existingvcenter" :key="opt.id">
|
||||
{{ 'VC: ' + opt.vcenter + ' - DC: ' + opt.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<div v-else>
|
||||
{{ $t('message.list.zone.vmware.datacenter.empty') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="vcenterSelectedOption === 'new'">
|
||||
<a-form-item ref="vcenter" name="vcenter">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.vcenter')" :tooltip="apiParams.vcenter.description"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="vcenter"
|
||||
:placeholder="apiParams.vcenter.description"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item ref="datacenter" name="datacenter">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.vcenter.datacenter')" :tooltip="apiParams.datacentername.description"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="datacenter"
|
||||
:placeholder="apiParams.datacentername.description"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item ref="username" name="username">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.vcenter.username')" :tooltip="apiParams.username.description"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="username"
|
||||
:placeholder="apiParams.username.description"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item ref="password" name="password">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.vcenter.password')" :tooltip="apiParams.password.description"/>
|
||||
</template>
|
||||
<a-input-password
|
||||
v-model:value="password"
|
||||
:placeholder="apiParams.password.description"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a-button
|
||||
v-if="vcenterSelectedOption == 'existing' || vcenterSelectedOption == 'new'"
|
||||
:disabled="(vcenterSelectedOption === 'new' && (vcenter === '' || datacentername === '' || username === '' || password === '')) ||
|
||||
(vcenterSelectedOption === 'existing' && selectedExistingVcenterId === '')"
|
||||
:loading="loading"
|
||||
type="primary"
|
||||
@click="listVmwareDatacenterVms">{{ $t('label.list.vmware.vcenter.vms') }}</a-button>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import { ref, reactive } from 'vue'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
import Status from '@/components/widgets/Status'
|
||||
|
||||
export default {
|
||||
name: 'SelectVmwareVcenter',
|
||||
components: {
|
||||
TooltipLabel,
|
||||
Status
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
vcenter: '',
|
||||
datacenter: '',
|
||||
username: '',
|
||||
password: '',
|
||||
loading: false,
|
||||
zones: {},
|
||||
vcenterSelectedOption: '',
|
||||
existingvcenter: [],
|
||||
selectedExistingVcenterId: '',
|
||||
selectedPoweredOnVm: false,
|
||||
vmwareDcVms: [],
|
||||
vmwareDcVmSelectedRows: [],
|
||||
vmwareDcVmsColumns: [
|
||||
{
|
||||
title: this.$t('label.hostname'),
|
||||
dataIndex: 'hostname'
|
||||
},
|
||||
{
|
||||
title: this.$t('label.cluster'),
|
||||
dataIndex: 'clustername'
|
||||
},
|
||||
{
|
||||
title: this.$t('label.virtualmachinename'),
|
||||
dataIndex: 'virtualmachinename'
|
||||
},
|
||||
{
|
||||
title: this.$t('label.powerstate'),
|
||||
key: 'powerstate',
|
||||
dataIndex: 'powerstate'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
vmwareDcVmsSelection () {
|
||||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.vmwareDcVmSelectedRows || [],
|
||||
onChange: this.onVmwareDcVmSelectRow
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.apiParams = this.$getApiParams('listVmwareDcVms')
|
||||
},
|
||||
created () {
|
||||
this.initForm()
|
||||
this.fetchZones()
|
||||
},
|
||||
methods: {
|
||||
initForm () {
|
||||
this.formRef = ref()
|
||||
this.form = reactive({
|
||||
vcenter: '',
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
this.rules = reactive({})
|
||||
},
|
||||
listVmwareDatacenterVms () {
|
||||
this.loading = true
|
||||
this.$emit('loadingVmwareUnmanagedInstances')
|
||||
const params = {}
|
||||
if (this.vcenterSelectedOption === 'new') {
|
||||
params.datacentername = this.datacenter
|
||||
params.vcenter = this.vcenter
|
||||
params.username = this.username
|
||||
params.password = this.password
|
||||
} else {
|
||||
params.existingvcenterid = this.selectedExistingVcenterId
|
||||
}
|
||||
api('listVmwareDcVms', params).then(json => {
|
||||
const obj = {
|
||||
params: params,
|
||||
response: json.listvmwaredcvmsresponse
|
||||
}
|
||||
this.$emit('listedVmwareUnmanagedInstances', obj)
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
fetchZones () {
|
||||
this.loading = true
|
||||
api('listZones', { showicon: true }).then(response => {
|
||||
this.zones = response.listzonesresponse.zone || []
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
onSelectZoneId (value) {
|
||||
this.sourcezoneid = value
|
||||
this.listZoneVmwareDcs()
|
||||
},
|
||||
listZoneVmwareDcs () {
|
||||
this.loading = true
|
||||
api('listVmwareDcs', { zoneid: this.sourcezoneid }).then(response => {
|
||||
if (response.listvmwaredcsresponse.VMwareDC && response.listvmwaredcsresponse.VMwareDC.length > 0) {
|
||||
this.existingvcenter = response.listvmwaredcsresponse.VMwareDC
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
onSelectExistingVmwareDatacenter (value) {
|
||||
this.selectedExistingVcenterId = value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.card-footer button {
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@ -22,6 +22,7 @@ package com.cloud.utils.script;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
@ -138,4 +139,31 @@ public abstract class OutputInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
public static class LineByLineOutputLogger extends OutputInterpreter {
|
||||
private Logger logger;
|
||||
private String logPrefix;
|
||||
|
||||
public LineByLineOutputLogger(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public LineByLineOutputLogger(Logger logger, String logPrefix) {
|
||||
this.logger = logger;
|
||||
this.logPrefix = logPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drain() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String interpret(BufferedReader reader) throws IOException {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
logger.info(StringUtils.isNotBlank(logPrefix) ? String.format("(%s) %s", logPrefix, line) : line);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ public class Script implements Callable<String> {
|
||||
private volatile boolean _isTimeOut = false;
|
||||
|
||||
private boolean _passwordCommand = false;
|
||||
private boolean avoidLoggingCommand = false;
|
||||
|
||||
private static final ScheduledExecutorService s_executors = Executors.newScheduledThreadPool(10, new NamedThreadFactory("Script"));
|
||||
|
||||
@ -73,6 +74,10 @@ public class Script implements Callable<String> {
|
||||
return _process.exitValue();
|
||||
}
|
||||
|
||||
public void setAvoidLoggingCommand(boolean avoid) {
|
||||
avoidLoggingCommand = avoid;
|
||||
}
|
||||
|
||||
public Script(String command, Duration timeout, Logger logger) {
|
||||
this(command, timeout.getMillis(), logger);
|
||||
}
|
||||
@ -204,9 +209,10 @@ public class Script implements Callable<String> {
|
||||
|
||||
public String execute(OutputInterpreter interpreter) {
|
||||
String[] command = _command.toArray(new String[_command.size()]);
|
||||
|
||||
String commandLine = buildCommandLine(command);
|
||||
_logger.debug(String.format("Executing command [%s].", commandLine.split(KeyStoreUtils.KS_FILENAME)[0]));
|
||||
if (_logger.isDebugEnabled() && !avoidLoggingCommand) {
|
||||
_logger.debug(String.format("Executing command [%s].", commandLine.split(KeyStoreUtils.KS_FILENAME)[0]));
|
||||
}
|
||||
|
||||
try {
|
||||
_logger.trace(String.format("Creating process for command [%s].", commandLine));
|
||||
@ -518,14 +524,19 @@ public class Script implements Callable<String> {
|
||||
}
|
||||
|
||||
public static int runSimpleBashScriptForExitValue(String command) {
|
||||
return runSimpleBashScriptForExitValue(command, 0);
|
||||
return runSimpleBashScriptForExitValue(command, 0, false);
|
||||
}
|
||||
|
||||
public static int runSimpleBashScriptForExitValue(String command, int timeout) {
|
||||
public static int runSimpleBashScriptForExitValueAvoidLogging(String command) {
|
||||
return runSimpleBashScriptForExitValue(command, 0, true);
|
||||
}
|
||||
|
||||
public static int runSimpleBashScriptForExitValue(String command, int timeout, boolean avoidLogging) {
|
||||
|
||||
Script s = new Script("/bin/bash", timeout);
|
||||
s.add("-c");
|
||||
s.add(command);
|
||||
s.setAvoidLoggingCommand(avoidLogging);
|
||||
|
||||
String result = s.execute(null);
|
||||
if (result == null || result.trim().isEmpty())
|
||||
|
||||
@ -78,5 +78,11 @@
|
||||
<artifactId>maven-artifact</artifactId>
|
||||
<version>3.6.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-core</artifactId>
|
||||
<version>4.19.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -21,6 +21,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.hypervisor.vmware.util.VmwareHelper;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -159,14 +161,24 @@ public class DatacenterMO extends BaseMO {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Pair<ManagedObjectReference, String>> getAllVmsOnDatacenter() throws Exception {
|
||||
List<Pair<ManagedObjectReference, String>> vms = new ArrayList<Pair<ManagedObjectReference, String>>();
|
||||
|
||||
public List<UnmanagedInstanceTO> getAllVmsOnDatacenter() throws Exception {
|
||||
List<UnmanagedInstanceTO> vms = new ArrayList<>();
|
||||
List<ObjectContent> ocs = getVmPropertiesOnDatacenterVmFolder(new String[] {"name"});
|
||||
if (ocs != null) {
|
||||
for (ObjectContent oc : ocs) {
|
||||
String vmName = oc.getPropSet().get(0).getVal().toString();
|
||||
vms.add(new Pair<ManagedObjectReference, String>(oc.getObj(), vmName));
|
||||
ManagedObjectReference vmMor = oc.getObj();
|
||||
if (vmMor != null) {
|
||||
VirtualMachineMO vmMo = new VirtualMachineMO(_context, vmMor);
|
||||
try {
|
||||
if (!vmMo.isTemplate()) {
|
||||
HostMO hostMO = vmMo.getRunningHost();
|
||||
UnmanagedInstanceTO unmanagedInstance = VmwareHelper.getUnmanagedInstance(hostMO, vmMo);
|
||||
vms.add(unmanagedInstance);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.debug(String.format("Unexpected error checking unmanaged instance %s, excluding it: %s", vmMo.getVmName(), e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,8 +26,11 @@ import java.io.StringWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
@ -37,6 +40,30 @@ import javax.xml.datatype.DatatypeConfigurationException;
|
||||
import javax.xml.datatype.DatatypeFactory;
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
|
||||
import com.cloud.hypervisor.vmware.mo.ClusterMO;
|
||||
import com.cloud.hypervisor.vmware.mo.DatastoreFile;
|
||||
import com.cloud.hypervisor.vmware.mo.DistributedVirtualSwitchMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
|
||||
import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
import com.vmware.vim25.DatastoreInfo;
|
||||
import com.vmware.vim25.DistributedVirtualPort;
|
||||
import com.vmware.vim25.DistributedVirtualSwitchPortCriteria;
|
||||
import com.vmware.vim25.GuestInfo;
|
||||
import com.vmware.vim25.GuestNicInfo;
|
||||
import com.vmware.vim25.HostPortGroupSpec;
|
||||
import com.vmware.vim25.NasDatastoreInfo;
|
||||
import com.vmware.vim25.VMwareDVSPortSetting;
|
||||
import com.vmware.vim25.VirtualDeviceFileBackingInfo;
|
||||
import com.vmware.vim25.VirtualIDEController;
|
||||
import com.vmware.vim25.VirtualMachineGuestOsIdentifier;
|
||||
import com.vmware.vim25.VirtualMachineToolsStatus;
|
||||
import com.vmware.vim25.VirtualSCSIController;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec;
|
||||
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -763,4 +790,268 @@ public class VmwareHelper {
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
public static UnmanagedInstanceTO getUnmanagedInstance(VmwareHypervisorHost hyperHost, VirtualMachineMO vmMo) {
|
||||
UnmanagedInstanceTO instance = null;
|
||||
try {
|
||||
instance = new UnmanagedInstanceTO();
|
||||
instance.setName(vmMo.getVmName());
|
||||
instance.setInternalCSName(vmMo.getInternalCSName());
|
||||
instance.setCpuCores(vmMo.getConfigSummary().getNumCpu());
|
||||
instance.setCpuCoresPerSocket(vmMo.getCoresPerSocket());
|
||||
instance.setCpuSpeed(vmMo.getConfigSummary().getCpuReservation());
|
||||
instance.setMemory(vmMo.getConfigSummary().getMemorySizeMB());
|
||||
instance.setOperatingSystemId(vmMo.getVmGuestInfo().getGuestId());
|
||||
|
||||
ClusterMO clusterMo = new ClusterMO(hyperHost.getContext(), hyperHost.getHyperHostCluster());
|
||||
instance.setClusterName(clusterMo.getName());
|
||||
instance.setHostName(hyperHost.getHyperHostName());
|
||||
|
||||
if (StringUtils.isEmpty(instance.getOperatingSystemId())) {
|
||||
instance.setOperatingSystemId(vmMo.getConfigSummary().getGuestId());
|
||||
}
|
||||
VirtualMachineGuestOsIdentifier osIdentifier = VirtualMachineGuestOsIdentifier.OTHER_GUEST;
|
||||
try {
|
||||
osIdentifier = VirtualMachineGuestOsIdentifier.fromValue(instance.getOperatingSystemId());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
if (StringUtils.isNotEmpty(instance.getOperatingSystemId()) && instance.getOperatingSystemId().contains("64")) {
|
||||
osIdentifier = VirtualMachineGuestOsIdentifier.OTHER_GUEST_64;
|
||||
}
|
||||
}
|
||||
instance.setOperatingSystem(vmMo.getGuestInfo().getGuestFullName());
|
||||
if (StringUtils.isEmpty(instance.getOperatingSystem())) {
|
||||
instance.setOperatingSystem(vmMo.getConfigSummary().getGuestFullName());
|
||||
}
|
||||
UnmanagedInstanceTO.PowerState powerState = UnmanagedInstanceTO.PowerState.PowerUnknown;
|
||||
if (vmMo.getPowerState().toString().equalsIgnoreCase("POWERED_ON")) {
|
||||
powerState = UnmanagedInstanceTO.PowerState.PowerOn;
|
||||
instance.setCpuSpeed(vmMo.getRuntimeInfo().getMaxCpuUsage() / instance.getCpuCores());
|
||||
}
|
||||
if (vmMo.getPowerState().toString().equalsIgnoreCase("POWERED_OFF")) {
|
||||
powerState = UnmanagedInstanceTO.PowerState.PowerOff;
|
||||
}
|
||||
instance.setPowerState(powerState);
|
||||
instance.setDisks(getUnmanageInstanceDisks(vmMo));
|
||||
instance.setNics(getUnmanageInstanceNics(hyperHost, vmMo));
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance info. " + e.getMessage());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected static List<UnmanagedInstanceTO.Disk> getUnmanageInstanceDisks(VirtualMachineMO vmMo) {
|
||||
List<UnmanagedInstanceTO.Disk> instanceDisks = new ArrayList<>();
|
||||
VirtualDisk[] disks = null;
|
||||
try {
|
||||
disks = vmMo.getAllDiskDevice();
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance disks. " + e.getMessage());
|
||||
}
|
||||
if (disks != null) {
|
||||
for (VirtualDevice diskDevice : disks) {
|
||||
try {
|
||||
if (diskDevice instanceof VirtualDisk) {
|
||||
UnmanagedInstanceTO.Disk instanceDisk = new UnmanagedInstanceTO.Disk();
|
||||
VirtualDisk disk = (VirtualDisk) diskDevice;
|
||||
instanceDisk.setDiskId(disk.getDiskObjectId());
|
||||
instanceDisk.setLabel(disk.getDeviceInfo() != null ? disk.getDeviceInfo().getLabel() : "");
|
||||
instanceDisk.setFileBaseName(vmMo.getVmdkFileBaseName(disk));
|
||||
instanceDisk.setImagePath(getAbsoluteVmdkFile(disk));
|
||||
instanceDisk.setCapacity(disk.getCapacityInBytes());
|
||||
instanceDisk.setPosition(diskDevice.getUnitNumber());
|
||||
DatastoreFile file = new DatastoreFile(getAbsoluteVmdkFile(disk));
|
||||
if (StringUtils.isNoneEmpty(file.getFileBaseName(), file.getDatastoreName())) {
|
||||
VirtualMachineDiskInfo diskInfo = vmMo.getDiskInfoBuilder().getDiskInfoByBackingFileBaseName(file.getFileBaseName(), file.getDatastoreName());
|
||||
instanceDisk.setChainInfo(GsonHelper.getGsonLogger().toJson(diskInfo));
|
||||
}
|
||||
for (VirtualDevice device : vmMo.getAllDeviceList()) {
|
||||
if (diskDevice.getControllerKey() == device.getKey()) {
|
||||
if (device instanceof VirtualIDEController) {
|
||||
instanceDisk.setController(DiskControllerType.getType(device.getClass().getSimpleName()).toString());
|
||||
instanceDisk.setControllerUnit(((VirtualIDEController) device).getBusNumber());
|
||||
} else if (device instanceof VirtualSCSIController) {
|
||||
instanceDisk.setController(DiskControllerType.getType(device.getClass().getSimpleName()).toString());
|
||||
instanceDisk.setControllerUnit(((VirtualSCSIController) device).getBusNumber());
|
||||
} else {
|
||||
instanceDisk.setController(DiskControllerType.none.toString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (disk.getBacking() instanceof VirtualDeviceFileBackingInfo) {
|
||||
VirtualDeviceFileBackingInfo diskBacking = (VirtualDeviceFileBackingInfo) disk.getBacking();
|
||||
ManagedObjectReference morDs = diskBacking.getDatastore();
|
||||
DatastoreInfo info = (DatastoreInfo)vmMo.getContext().getVimClient().getDynamicProperty(diskBacking.getDatastore(), "info");
|
||||
if (info instanceof NasDatastoreInfo) {
|
||||
NasDatastoreInfo dsInfo = (NasDatastoreInfo) info;
|
||||
instanceDisk.setDatastoreName(dsInfo.getName());
|
||||
if (dsInfo.getNas() != null) {
|
||||
instanceDisk.setDatastoreHost(dsInfo.getNas().getRemoteHost());
|
||||
instanceDisk.setDatastorePath(dsInfo.getNas().getRemotePath());
|
||||
instanceDisk.setDatastoreType(dsInfo.getNas().getType());
|
||||
}
|
||||
} else {
|
||||
instanceDisk.setDatastoreName(info.getName());
|
||||
}
|
||||
}
|
||||
s_logger.info(vmMo.getName() + " " + disk.getDeviceInfo().getLabel() + " " + disk.getDeviceInfo().getSummary() + " " + disk.getDiskObjectId() + " " + disk.getCapacityInKB() + " " + instanceDisk.getController());
|
||||
instanceDisks.add(instanceDisk);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance disk info. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
Collections.sort(instanceDisks, new Comparator<UnmanagedInstanceTO.Disk>() {
|
||||
@Override
|
||||
public int compare(final UnmanagedInstanceTO.Disk disk1, final UnmanagedInstanceTO.Disk disk2) {
|
||||
return extractInt(disk1) - extractInt(disk2);
|
||||
}
|
||||
|
||||
int extractInt(UnmanagedInstanceTO.Disk disk) {
|
||||
String num = disk.getLabel().replaceAll("\\D", "");
|
||||
// return 0 if no digits found
|
||||
return num.isEmpty() ? 0 : Integer.parseInt(num);
|
||||
}
|
||||
});
|
||||
}
|
||||
return instanceDisks;
|
||||
}
|
||||
|
||||
private static List<UnmanagedInstanceTO.Nic> getUnmanageInstanceNics(VmwareHypervisorHost hyperHost, VirtualMachineMO vmMo) {
|
||||
List<UnmanagedInstanceTO.Nic> instanceNics = new ArrayList<>();
|
||||
|
||||
HashMap<String, List<String>> guestNicMacIPAddressMap = new HashMap<>();
|
||||
try {
|
||||
GuestInfo guestInfo = vmMo.getGuestInfo();
|
||||
if (guestInfo.getToolsStatus() == VirtualMachineToolsStatus.TOOLS_OK) {
|
||||
for (GuestNicInfo nicInfo: guestInfo.getNet()) {
|
||||
if (CollectionUtils.isNotEmpty(nicInfo.getIpAddress())) {
|
||||
List<String> ipAddresses = new ArrayList<>();
|
||||
for (String ipAddress : nicInfo.getIpAddress()) {
|
||||
if (NetUtils.isValidIp4(ipAddress)) {
|
||||
ipAddresses.add(ipAddress);
|
||||
}
|
||||
}
|
||||
guestNicMacIPAddressMap.put(nicInfo.getMacAddress(), ipAddresses);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s_logger.info(String.format("Unable to retrieve guest nics for instance: %s from VMware tools as tools status: %s", vmMo.getName(), guestInfo.getToolsStatus().toString()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve guest nics for instance from VMware tools. " + e.getMessage());
|
||||
}
|
||||
VirtualDevice[] nics = null;
|
||||
try {
|
||||
nics = vmMo.getNicDevices();
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance nics. " + e.getMessage());
|
||||
}
|
||||
if (nics != null) {
|
||||
for (VirtualDevice nic : nics) {
|
||||
try {
|
||||
VirtualEthernetCard ethCardDevice = (VirtualEthernetCard) nic;
|
||||
s_logger.error(nic.getClass().getCanonicalName() + " " + nic.getBacking().getClass().getCanonicalName() + " " + ethCardDevice.getMacAddress());
|
||||
UnmanagedInstanceTO.Nic instanceNic = new UnmanagedInstanceTO.Nic();
|
||||
instanceNic.setNicId(ethCardDevice.getDeviceInfo().getLabel());
|
||||
if (ethCardDevice instanceof VirtualPCNet32) {
|
||||
instanceNic.setAdapterType(VirtualEthernetCardType.PCNet32.toString());
|
||||
} else if (ethCardDevice instanceof VirtualVmxnet2) {
|
||||
instanceNic.setAdapterType(VirtualEthernetCardType.Vmxnet2.toString());
|
||||
} else if (ethCardDevice instanceof VirtualVmxnet3) {
|
||||
instanceNic.setAdapterType(VirtualEthernetCardType.Vmxnet3.toString());
|
||||
} else {
|
||||
instanceNic.setAdapterType(VirtualEthernetCardType.E1000.toString());
|
||||
}
|
||||
instanceNic.setMacAddress(ethCardDevice.getMacAddress());
|
||||
if (guestNicMacIPAddressMap.containsKey(instanceNic.getMacAddress())) {
|
||||
instanceNic.setIpAddress(guestNicMacIPAddressMap.get(instanceNic.getMacAddress()));
|
||||
}
|
||||
if (ethCardDevice.getSlotInfo() != null) {
|
||||
instanceNic.setPciSlot(ethCardDevice.getSlotInfo().toString());
|
||||
}
|
||||
VirtualDeviceBackingInfo backing = ethCardDevice.getBacking();
|
||||
if (backing instanceof VirtualEthernetCardDistributedVirtualPortBackingInfo) {
|
||||
VirtualEthernetCardDistributedVirtualPortBackingInfo backingInfo = (VirtualEthernetCardDistributedVirtualPortBackingInfo) backing;
|
||||
DistributedVirtualSwitchPortConnection port = backingInfo.getPort();
|
||||
String portKey = port.getPortKey();
|
||||
String portGroupKey = port.getPortgroupKey();
|
||||
String dvSwitchUuid = port.getSwitchUuid();
|
||||
|
||||
s_logger.debug("NIC " + nic.toString() + " is connected to dvSwitch " + dvSwitchUuid + " pg " + portGroupKey + " port " + portKey);
|
||||
|
||||
ManagedObjectReference dvSwitchManager = vmMo.getContext().getVimClient().getServiceContent().getDvSwitchManager();
|
||||
ManagedObjectReference dvSwitch = vmMo.getContext().getVimClient().getService().queryDvsByUuid(dvSwitchManager, dvSwitchUuid);
|
||||
|
||||
// Get all ports
|
||||
DistributedVirtualSwitchPortCriteria criteria = new DistributedVirtualSwitchPortCriteria();
|
||||
criteria.setInside(true);
|
||||
criteria.getPortgroupKey().add(portGroupKey);
|
||||
List<DistributedVirtualPort> dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria);
|
||||
|
||||
for (DistributedVirtualPort dvPort : dvPorts) {
|
||||
// Find the port for this NIC by portkey
|
||||
if (portKey.equals(dvPort.getKey())) {
|
||||
VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPort.getConfig().getSetting();
|
||||
if (settings.getVlan() instanceof VmwareDistributedVirtualSwitchVlanIdSpec) {
|
||||
VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings.getVlan();
|
||||
s_logger.trace("Found port " + dvPort.getKey() + " with vlan " + vlanId.getVlanId());
|
||||
if (vlanId.getVlanId() > 0 && vlanId.getVlanId() < 4095) {
|
||||
instanceNic.setVlan(vlanId.getVlanId());
|
||||
}
|
||||
} else if (settings.getVlan() instanceof VmwareDistributedVirtualSwitchPvlanSpec) {
|
||||
VmwareDistributedVirtualSwitchPvlanSpec pvlanSpec = (VmwareDistributedVirtualSwitchPvlanSpec) settings.getVlan();
|
||||
s_logger.trace("Found port " + dvPort.getKey() + " with pvlan " + pvlanSpec.getPvlanId());
|
||||
if (pvlanSpec.getPvlanId() > 0 && pvlanSpec.getPvlanId() < 4095) {
|
||||
DistributedVirtualSwitchMO dvSwitchMo = new DistributedVirtualSwitchMO(vmMo.getContext(), dvSwitch);
|
||||
Pair<Integer, HypervisorHostHelper.PvlanType> vlanDetails = dvSwitchMo.retrieveVlanFromPvlan(pvlanSpec.getPvlanId(), dvSwitch);
|
||||
if (vlanDetails != null && vlanDetails.first() != null && vlanDetails.second() != null) {
|
||||
instanceNic.setVlan(vlanDetails.first());
|
||||
instanceNic.setPvlan(pvlanSpec.getPvlanId());
|
||||
instanceNic.setPvlanType(vlanDetails.second().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (backing instanceof VirtualEthernetCardNetworkBackingInfo) {
|
||||
VirtualEthernetCardNetworkBackingInfo backingInfo = (VirtualEthernetCardNetworkBackingInfo) backing;
|
||||
instanceNic.setNetwork(backingInfo.getDeviceName());
|
||||
if (hyperHost instanceof HostMO) {
|
||||
HostMO hostMo = (HostMO) hyperHost;
|
||||
HostPortGroupSpec portGroupSpec = hostMo.getHostPortGroupSpec(backingInfo.getDeviceName());
|
||||
instanceNic.setVlan(portGroupSpec.getVlanId());
|
||||
}
|
||||
}
|
||||
instanceNics.add(instanceNic);
|
||||
} catch (Exception e) {
|
||||
s_logger.info("Unable to retrieve unmanaged instance nic info. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
Collections.sort(instanceNics, new Comparator<UnmanagedInstanceTO.Nic>() {
|
||||
@Override
|
||||
public int compare(final UnmanagedInstanceTO.Nic nic1, final UnmanagedInstanceTO.Nic nic2) {
|
||||
return extractInt(nic1) - extractInt(nic2);
|
||||
}
|
||||
|
||||
int extractInt(UnmanagedInstanceTO.Nic nic) {
|
||||
String num = nic.getNicId().replaceAll("\\D", "");
|
||||
// return 0 if no digits found
|
||||
return num.isEmpty() ? 0 : Integer.parseInt(num);
|
||||
}
|
||||
});
|
||||
}
|
||||
return instanceNics;
|
||||
}
|
||||
|
||||
public static String getAbsoluteVmdkFile(VirtualDisk disk) {
|
||||
String vmdkAbsFile = null;
|
||||
VirtualDeviceBackingInfo backingInfo = disk.getBacking();
|
||||
if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
|
||||
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo) backingInfo;
|
||||
vmdkAbsFile = diskBackingInfo.getFileName();
|
||||
}
|
||||
return vmdkAbsFile;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,13 @@ package com.cloud.hypervisor.vmware.util;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import com.vmware.vim25.DatastoreInfo;
|
||||
import com.vmware.vim25.Description;
|
||||
import com.vmware.vim25.ManagedObjectReference;
|
||||
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
@ -29,11 +36,45 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
|
||||
import com.vmware.vim25.VirtualDisk;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class VmwareHelperTest {
|
||||
@Mock
|
||||
private VirtualMachineMO virtualMachineMO;
|
||||
|
||||
private static final String diskLabel = "disk1";
|
||||
private static final String diskFileBaseName = "xyz.vmdk";
|
||||
private static final String dataStoreName = "Datastore";
|
||||
private static final String vmName = "VM1";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
VirtualDiskFlatVer2BackingInfo backingInfo = Mockito.mock(VirtualDiskFlatVer2BackingInfo.class);
|
||||
Mockito.when(backingInfo.getFileName()).thenReturn("abc");
|
||||
Mockito.when(backingInfo.getDatastore()).thenReturn(Mockito.mock(ManagedObjectReference.class));
|
||||
VirtualDisk disk = Mockito.mock(VirtualDisk.class);
|
||||
VirtualDisk[] disks = new VirtualDisk[1];
|
||||
disks[0] = disk;
|
||||
Description description = Mockito.mock(Description.class);
|
||||
Mockito.when(description.getLabel()).thenReturn(diskLabel);
|
||||
Mockito.when(description.getSummary()).thenReturn("");
|
||||
Mockito.when(disk.getBacking()).thenReturn(backingInfo);
|
||||
Mockito.when(disk.getDeviceInfo()).thenReturn(description);
|
||||
Mockito.when(virtualMachineMO.getAllDiskDevice()).thenReturn(disks);
|
||||
Mockito.when(virtualMachineMO.getVmdkFileBaseName(disk)).thenReturn(diskFileBaseName);
|
||||
|
||||
DatastoreInfo datastoreInfo = Mockito.mock(DatastoreInfo.class);
|
||||
Mockito.when(datastoreInfo.getName()).thenReturn(dataStoreName);
|
||||
VmwareClient client = Mockito.mock(VmwareClient.class);
|
||||
Mockito.when(client.getDynamicProperty(Mockito.any(ManagedObjectReference.class), Mockito.anyString()))
|
||||
.thenReturn(datastoreInfo);
|
||||
VmwareContext context = Mockito.mock(VmwareContext.class);
|
||||
Mockito.when(context.getVimClient()).thenReturn(client);
|
||||
Mockito.when(virtualMachineMO.getContext()).thenReturn(context);
|
||||
Mockito.when(virtualMachineMO.getName()).thenReturn(vmName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prepareDiskDeviceTestNotLimitingIOPS() throws Exception {
|
||||
Mockito.when(virtualMachineMO.getIDEDeviceControllerKey()).thenReturn(1);
|
||||
@ -54,4 +95,14 @@ public class VmwareHelperTest {
|
||||
VirtualDisk virtualDisk = (VirtualDisk) VmwareHelper.prepareDiskDevice(virtualMachineMO, null, -1, new String[1], null, 0, 0, Long.valueOf(0));
|
||||
assertNull(virtualDisk.getStorageIOAllocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnmanageInstanceDisks() {
|
||||
List<UnmanagedInstanceTO.Disk> disks = VmwareHelper.getUnmanageInstanceDisks(virtualMachineMO);
|
||||
Assert.assertEquals(1, disks.size());
|
||||
UnmanagedInstanceTO.Disk disk = disks.get(0);
|
||||
Assert.assertEquals(diskLabel, disk.getLabel());
|
||||
Assert.assertEquals(diskFileBaseName, disk.getFileBaseName());
|
||||
Assert.assertEquals(dataStoreName, disk.getDatastoreName());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user