shwstppr commented on code in PR #7881:
URL: https://github.com/apache/cloudstack/pull/7881#discussion_r1345542278


##########
api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java:
##########
@@ -157,6 +159,40 @@ public class ImportUnmanagedInstanceCmd extends 
BaseAsyncCmd {
             description = "VM is imported despite some of its NIC's MAC 
addresses are already present")
     private Boolean forced;
 
+    @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.HOST_IP,
+            type = BaseCmd.CommandType.STRING,
+            required = true,
+            description = "VMware ESXi host IP/Name.")
+    private String host;
+
+    @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.CLUSTER_NAME, type = CommandType.STRING,
+            required = true,
+            description = "Name of VMware cluster.")
+    private String clusterName;
+
+    @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 the specified username.")
+    private String password;
+

Review Comment:
   should there be `since` property for new params



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java:
##########
@@ -0,0 +1,309 @@
+//
+// 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.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.Script;
+import org.apache.cloudstack.vm.UnmanagedInstanceTO;
+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;
+
+@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);
+
+    @Override
+    public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource 
serverResource) {

Review Comment:
   minor - is this method little too big?



##########
server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java:
##########
@@ -457,6 +419,13 @@ private Map<String, Network.IpAddresses> 
getNicIpAddresses(final List<UnmanagedI
 
     private StoragePool getStoragePool(final UnmanagedInstanceTO.Disk disk, 
final DataCenter zone, final Cluster cluster) {
         StoragePool storagePool = null;
+//        if (cluster.getHypervisorType() == Hypervisor.HypervisorType.KVM) {
+//            List<StoragePoolVO> poolsInCluster = 
primaryDataStoreDao.findPoolsInClusters(List.of(cluster.getId()));
+//            if (CollectionUtils.isEmpty(poolsInCluster)) {
+//                throw new CloudRuntimeException(String.format("Cannot find a 
storage pool for cluster %s", cluster.getName()));
+//            }
+//            return poolsInCluster.get(0);
+//        }

Review Comment:
   do we need this?



##########
server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java:
##########
@@ -1220,18 +1222,260 @@ public UserVmResponse 
importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) {
                             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, 
true, params);
+    }
+
+    private UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, 
Cluster destinationCluster, Account caller,
+                                                          Account owner, long 
userId, VMTemplateVO template,
+                                                          ServiceOfferingVO 
serviceOffering, Map<String, Long> dataDiskOfferingMap,
+                                                          Map<String, String> 
details, String displayName,
+                                                          String hostName, 
Map<String, Long> nicNetworkMap,
+                                                          Map<String, 
Network.IpAddresses> nicIpAddressMap,
+                                                          
ImportUnmanagedInstanceCmd cmd) {
+        Long existingVcenterId = cmd.getExistingVcenterId();
+        String vcenter = cmd.getVcenter();
+        String datacenterName = cmd.getDatacenterName();
+        String username = cmd.getUsername();
+        String password = cmd.getPassword();

Review Comment:
   Don't know if it is an issue so just a question - will we pass the password 
as plain text?



##########
server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java:
##########
@@ -1220,18 +1222,260 @@ public UserVmResponse 
importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) {
                             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, 
true, params);
+    }
+
+    private UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, 
Cluster destinationCluster, Account caller,
+                                                          Account owner, long 
userId, VMTemplateVO template,
+                                                          ServiceOfferingVO 
serviceOffering, Map<String, Long> dataDiskOfferingMap,
+                                                          Map<String, String> 
details, String displayName,
+                                                          String hostName, 
Map<String, Long> nicNetworkMap,
+                                                          Map<String, 
Network.IpAddresses> nicIpAddressMap,
+                                                          
ImportUnmanagedInstanceCmd cmd) {
+        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 sourceVM = cmd.getName();
+        String sourceHostName = cmd.getHost();
+        boolean forced = cmd.isForced();
+
+        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");
+        }
+
+        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();
         }
-        return 
responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Full, 
"virtualmachine", userVm).get(0);
+
+        UnmanagedInstanceTO clonedInstance = null;
+        boolean isSourceVMRestoreNeeded = false;
+        try {
+            clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, 
datacenterName, username, password,
+                    clusterName, sourceHostName, sourceVM);
+            isSourceVMRestoreNeeded = 
clonedInstance.getCloneSourcePowerState() == 
UnmanagedInstanceTO.PowerState.PowerOn;
+            checkClonedInstanceMacAddresses(clonedInstance, nicNetworkMap, 
forced);
+            UnmanagedInstanceTO convertedInstance = 
convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, 
password,
+                    sourceHostName, clonedInstance, destinationCluster);
+            sanitizeConvertedInstance(convertedInstance, clonedInstance);
+            String instanceName = getGeneratedInstanceName(owner);
+            return importVirtualMachineInternal(convertedInstance, 
instanceName, zone, destinationCluster, null,
+                    template, displayName, hostName, caller, owner, userId,
+                    serviceOffering, dataDiskOfferingMap,
+                    nicNetworkMap, nicIpAddressMap,
+                    details, false, forced);
+        } catch (CloudRuntimeException e) {
+            LOGGER.error(String.format("Error importing VM: %s", 
e.getMessage()), e);
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
e.getMessage());
+        } finally {
+            removeClonedInstance(vcenter, datacenterName, username, password, 
sourceHostName, clonedInstance.getName(), sourceVM, isSourceVMRestoreNeeded);
+        }
+    }
+
+    private void checkClonedInstanceMacAddresses(UnmanagedInstanceTO 
clonedInstance, Map<String, Long> nicNetworkMap, 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 (int i = 0; i < nics.size(); i++) {
+            UnmanagedInstanceTO.Nic nic = nics.get(i);
+            Long networkId = networkIds.get(i);
+            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);
+            }
+            NicVO existingNic = nicDao.findByNetworkIdAndMacAddress(networkId, 
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(), networkId);
+                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";
+        }

Review Comment:
   I see similar code is used in other places as well. Should it be refactored? 
or maybe something for later



##########
server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java:
##########
@@ -1220,18 +1222,260 @@ public UserVmResponse 
importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) {
                             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, 
true, params);
+    }
+
+    private UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, 
Cluster destinationCluster, Account caller,
+                                                          Account owner, long 
userId, VMTemplateVO template,
+                                                          ServiceOfferingVO 
serviceOffering, Map<String, Long> dataDiskOfferingMap,
+                                                          Map<String, String> 
details, String displayName,
+                                                          String hostName, 
Map<String, Long> nicNetworkMap,
+                                                          Map<String, 
Network.IpAddresses> nicIpAddressMap,
+                                                          
ImportUnmanagedInstanceCmd cmd) {
+        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 sourceVM = cmd.getName();
+        String sourceHostName = cmd.getHost();
+        boolean forced = cmd.isForced();
+
+        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");
+        }
+
+        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();
         }
-        return 
responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Full, 
"virtualmachine", userVm).get(0);
+
+        UnmanagedInstanceTO clonedInstance = null;
+        boolean isSourceVMRestoreNeeded = false;
+        try {
+            clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, 
datacenterName, username, password,
+                    clusterName, sourceHostName, sourceVM);
+            isSourceVMRestoreNeeded = 
clonedInstance.getCloneSourcePowerState() == 
UnmanagedInstanceTO.PowerState.PowerOn;
+            checkClonedInstanceMacAddresses(clonedInstance, nicNetworkMap, 
forced);
+            UnmanagedInstanceTO convertedInstance = 
convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, 
password,
+                    sourceHostName, clonedInstance, destinationCluster);
+            sanitizeConvertedInstance(convertedInstance, clonedInstance);
+            String instanceName = getGeneratedInstanceName(owner);
+            return importVirtualMachineInternal(convertedInstance, 
instanceName, zone, destinationCluster, null,
+                    template, displayName, hostName, caller, owner, userId,
+                    serviceOffering, dataDiskOfferingMap,
+                    nicNetworkMap, nicIpAddressMap,
+                    details, false, forced);
+        } catch (CloudRuntimeException e) {
+            LOGGER.error(String.format("Error importing VM: %s", 
e.getMessage()), e);
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
e.getMessage());
+        } finally {
+            removeClonedInstance(vcenter, datacenterName, username, password, 
sourceHostName, clonedInstance.getName(), sourceVM, isSourceVMRestoreNeeded);
+        }
+    }
+
+    private void checkClonedInstanceMacAddresses(UnmanagedInstanceTO 
clonedInstance, Map<String, Long> nicNetworkMap, 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 (int i = 0; i < nics.size(); i++) {
+            UnmanagedInstanceTO.Nic nic = nics.get(i);
+            Long networkId = networkIds.get(i);
+            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);
+            }
+            NicVO existingNic = nicDao.findByNetworkIdAndMacAddress(networkId, 
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(), networkId);
+                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());
+        }
+    }
+
+    private void removeClonedInstance(String vcenter, String datacenterName,
+                                      String username, String password,
+                                      String sourceHostName, String 
clonedInstanceName,
+                                      String sourceVM, boolean 
powerOnSourceVM) {
+        HypervisorGuru vmwareGuru = 
hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
+        Map<String, String> params = 
createParamsForRemoveClonedInstance(vcenter, datacenterName, username, 
password, sourceVM, powerOnSourceVM);
+        boolean result = 
vmwareGuru.removeClonedHypervisorVMOutOfBand(sourceHostName, 
clonedInstanceName, powerOnSourceVM, 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, boolean powerOnSourceVM) {
+        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);
+        if (powerOnSourceVM) {
+            params.put(VmDetailConstants.VMWARE_VM_NAME, sourceVM);
+        }
+        return params;
+    }
+
+    private HostVO selectInstanceConvertionKVMHostInCluster(Cluster 
destinationCluster) {

Review Comment:
   minor - typo here in method name



##########
engine/components-api/src/main/java/com/cloud/storage/StorageManager.java:
##########
@@ -96,6 +96,14 @@ public interface StorageManager extends StorageService {
             true,
             ConfigKey.Scope.Global,
             null);
+    ConfigKey<Integer> ConvertInstanceProcessTimeout = new 
ConfigKey<>(Integer.class,
+            "convert.instance.process.timeout",
+            "Storage",
+            "14400",
+            "Timeout (in seconds) for the instance conversion process through 
the virt-v2v binary on a KVM host",
+            true,
+            ConfigKey.Scope.Global,

Review Comment:
   Just a thought, should this be zone or cluster based which would allow the 
operator to finely control the timeout when add DCs with different latency?



##########
plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcVmsCmd.java:
##########
@@ -0,0 +1,134 @@
+// 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.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)

Review Comment:
   Would be good to have `since` and `authorized` property



##########
plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcVmsCmd.java:
##########
@@ -0,0 +1,134 @@
+// 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.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<UnmanagedInstanceResponse> responses = new ArrayList<>();
+            if (CollectionUtils.isNotEmpty(vms)) {
+                for (UnmanagedInstanceTO vmwareVm : vms) {
+                    UnmanagedInstanceResponse resp = 
_responseGenerator.createUnmanagedInstanceResponse(vmwareVm, null, null);
+                    responses.add(resp);
+                }
+            }
+            ListResponse<UnmanagedInstanceResponse> response = new 
ListResponse<>();
+            response.setResponses(responses, responses.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";
+    }

Review Comment:
   As per my knowledge we do not need to do this anymore unless it is very 
different from API name



##########
ui/src/views/image/RegisterOrUploadTemplate.vue:
##########
@@ -17,7 +17,7 @@
 
 <template>
   <div
-    class="form-layout"

Review Comment:
   Are changes in this file related to the feature/PR?



##########
api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java:
##########
@@ -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")

Review Comment:
   `since` can be added here



##########
server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java:
##########
@@ -1220,18 +1222,260 @@ public UserVmResponse 
importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) {
                             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, 
true, params);
+    }
+
+    private UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, 
Cluster destinationCluster, Account caller,
+                                                          Account owner, long 
userId, VMTemplateVO template,
+                                                          ServiceOfferingVO 
serviceOffering, Map<String, Long> dataDiskOfferingMap,
+                                                          Map<String, String> 
details, String displayName,
+                                                          String hostName, 
Map<String, Long> nicNetworkMap,
+                                                          Map<String, 
Network.IpAddresses> nicIpAddressMap,
+                                                          
ImportUnmanagedInstanceCmd cmd) {
+        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 sourceVM = cmd.getName();
+        String sourceHostName = cmd.getHost();
+        boolean forced = cmd.isForced();
+
+        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");
+        }
+
+        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();
         }
-        return 
responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Full, 
"virtualmachine", userVm).get(0);
+
+        UnmanagedInstanceTO clonedInstance = null;
+        boolean isSourceVMRestoreNeeded = false;
+        try {
+            clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, 
datacenterName, username, password,
+                    clusterName, sourceHostName, sourceVM);
+            isSourceVMRestoreNeeded = 
clonedInstance.getCloneSourcePowerState() == 
UnmanagedInstanceTO.PowerState.PowerOn;
+            checkClonedInstanceMacAddresses(clonedInstance, nicNetworkMap, 
forced);
+            UnmanagedInstanceTO convertedInstance = 
convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, 
password,
+                    sourceHostName, clonedInstance, destinationCluster);
+            sanitizeConvertedInstance(convertedInstance, clonedInstance);
+            String instanceName = getGeneratedInstanceName(owner);
+            return importVirtualMachineInternal(convertedInstance, 
instanceName, zone, destinationCluster, null,
+                    template, displayName, hostName, caller, owner, userId,
+                    serviceOffering, dataDiskOfferingMap,
+                    nicNetworkMap, nicIpAddressMap,
+                    details, false, forced);
+        } catch (CloudRuntimeException e) {
+            LOGGER.error(String.format("Error importing VM: %s", 
e.getMessage()), e);
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
e.getMessage());
+        } finally {
+            removeClonedInstance(vcenter, datacenterName, username, password, 
sourceHostName, clonedInstance.getName(), sourceVM, isSourceVMRestoreNeeded);
+        }
+    }
+
+    private void checkClonedInstanceMacAddresses(UnmanagedInstanceTO 
clonedInstance, Map<String, Long> nicNetworkMap, 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 (int i = 0; i < nics.size(); i++) {
+            UnmanagedInstanceTO.Nic nic = nics.get(i);
+            Long networkId = networkIds.get(i);
+            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);
+            }
+            NicVO existingNic = nicDao.findByNetworkIdAndMacAddress(networkId, 
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(), networkId);
+                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());
+        }
+    }
+
+    private void removeClonedInstance(String vcenter, String datacenterName,
+                                      String username, String password,
+                                      String sourceHostName, String 
clonedInstanceName,
+                                      String sourceVM, boolean 
powerOnSourceVM) {
+        HypervisorGuru vmwareGuru = 
hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
+        Map<String, String> params = 
createParamsForRemoveClonedInstance(vcenter, datacenterName, username, 
password, sourceVM, powerOnSourceVM);
+        boolean result = 
vmwareGuru.removeClonedHypervisorVMOutOfBand(sourceHostName, 
clonedInstanceName, powerOnSourceVM, 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, boolean powerOnSourceVM) {
+        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);
+        if (powerOnSourceVM) {
+            params.put(VmDetailConstants.VMWARE_VM_NAME, sourceVM);
+        }
+        return params;
+    }
+
+    private HostVO selectInstanceConvertionKVMHostInCluster(Cluster 
destinationCluster) {
+        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 ||
+                        x.getResourceState() == ResourceState.Disabled)
+                .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) {
+        HostVO convertHost = 
selectInstanceConvertionKVMHostInCluster(destinationCluster);
+        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);
+        String temporaryConvertLocation = 
selectInstanceConvertionTemporaryLocation(destinationCluster);
+        List<String> destinationStoragePools = 
selectInstanceConvertionStoragePools(destinationCluster, 
clonedInstance.getDisks());
+        ConvertInstanceCommand cmd = new 
ConvertInstanceCommand(remoteInstanceTO,
+                Hypervisor.HypervisorType.KVM, destinationStoragePools, 
temporaryConvertLocation);
+        cmd.setWait(StorageManager.ConvertInstanceProcessTimeout.value());
+
+        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 due to: %s",
+                    vmName, 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

Review Comment:
   to be done?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to