This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch 4.15
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit b6b778f0036ed6387856f5b504fbde2afdc96722
Merge: 1bccb95 9b45ec2
Author: Daan Hoogland <[email protected]>
AuthorDate: Mon Feb 1 09:57:35 2021 +0000

    Merge release branch 4.14 to 4.15
    
    * 4.14:
      server: select root disk based on user input during vm import (#4591)
      kvm: Use Q35 chipset for UEFI x86_64 (#4576)
      server: fix wrong error message when create isolated network without 
SourceNat (#4624)
      server: add possibility to scale vm to current customer offerings (#4622)
      server: keep networks order and ips while move a vm with multiple 
networks (#4602)
      server: throw exception when update vm nic on L2 network (#4625)
      doc: fix typo in install notes (#4633)

 INSTALL.md                                         |  2 +-
 .../service/NetworkOrchestrationService.java       |  2 +-
 .../engine/orchestration/NetworkOrchestrator.java  | 13 +--
 .../kvm/resource/LibvirtComputingResource.java     |  2 +-
 .../java/com/cloud/api/query/QueryManagerImpl.java |  4 +-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  | 99 ++++++++++++++--------
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     | 38 +++++++--
 .../java/com/cloud/vpc/MockNetworkManagerImpl.java |  4 +-
 8 files changed, 113 insertions(+), 51 deletions(-)

diff --cc 
server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
index 532f5f9,0000000..501ca63
mode 100644,000000..100644
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@@ -1,1367 -1,0 +1,1395 @@@
 +// 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.vm;
 +
 +import java.util.ArrayList;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +
 +import javax.inject.Inject;
 +
 +import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer;
 +import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
 +import com.cloud.event.ActionEvent;
 +import com.cloud.exception.UnsupportedServiceException;
 +import com.cloud.storage.Snapshot;
 +import com.cloud.storage.SnapshotVO;
 +import com.cloud.storage.dao.SnapshotDao;
 +import com.cloud.vm.NicVO;
 +import com.cloud.vm.UserVmVO;
 +import com.cloud.vm.dao.UserVmDao;
 +import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 +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.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.framework.config.ConfigKey;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 +import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
 +import org.apache.commons.collections.CollectionUtils;
 +import org.apache.commons.collections.MapUtils;
 +import org.apache.log4j.Logger;
 +
 +import com.cloud.agent.AgentManager;
 +import com.cloud.agent.api.Answer;
 +import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
 +import com.cloud.agent.api.GetUnmanagedInstancesCommand;
 +import com.cloud.capacity.CapacityManager;
 +import com.cloud.configuration.Config;
 +import com.cloud.configuration.Resource;
 +import com.cloud.dc.DataCenter;
 +import com.cloud.dc.dao.ClusterDao;
 +import com.cloud.dc.dao.DataCenterDao;
 +import com.cloud.deploy.DataCenterDeployment;
 +import com.cloud.deploy.DeployDestination;
 +import com.cloud.deploy.DeploymentPlanner;
 +import com.cloud.deploy.DeploymentPlanningManager;
 +import com.cloud.event.EventTypes;
 +import com.cloud.event.UsageEventUtils;
 +import com.cloud.exception.InsufficientAddressCapacityException;
 +import com.cloud.exception.InsufficientCapacityException;
 +import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 +import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.exception.PermissionDeniedException;
 +import com.cloud.exception.ResourceAllocationException;
 +import com.cloud.host.Host;
 +import com.cloud.host.HostVO;
 +import com.cloud.host.Status;
 +import com.cloud.host.dao.HostDao;
 +import com.cloud.hypervisor.Hypervisor;
 +import com.cloud.network.Network;
 +import com.cloud.network.NetworkModel;
 +import com.cloud.network.Networks;
 +import com.cloud.network.dao.NetworkDao;
 +import com.cloud.network.dao.NetworkVO;
 +import com.cloud.offering.DiskOffering;
 +import com.cloud.offering.ServiceOffering;
 +import com.cloud.org.Cluster;
 +import com.cloud.resource.ResourceManager;
 +import com.cloud.serializer.GsonHelper;
 +import com.cloud.server.ManagementService;
 +import com.cloud.service.ServiceOfferingVO;
 +import com.cloud.service.dao.ServiceOfferingDao;
 +import com.cloud.storage.GuestOS;
 +import com.cloud.storage.GuestOSHypervisor;
 +import com.cloud.storage.StoragePool;
 +import com.cloud.storage.VMTemplateStoragePoolVO;
 +import com.cloud.storage.VMTemplateVO;
 +import com.cloud.storage.Volume;
 +import com.cloud.storage.VolumeApiService;
 +import com.cloud.storage.VolumeVO;
 +import com.cloud.storage.dao.DiskOfferingDao;
 +import com.cloud.storage.dao.GuestOSDao;
 +import com.cloud.storage.dao.GuestOSHypervisorDao;
 +import com.cloud.storage.dao.VMTemplateDao;
 +import com.cloud.storage.dao.VMTemplatePoolDao;
 +import com.cloud.storage.dao.VolumeDao;
 +import com.cloud.template.VirtualMachineTemplate;
 +import com.cloud.user.Account;
 +import com.cloud.user.AccountService;
 +import com.cloud.user.ResourceLimitService;
 +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.utils.net.NetUtils;
 +import com.cloud.vm.DiskProfile;
 +import com.cloud.vm.NicProfile;
 +import com.cloud.vm.UserVmManager;
 +import com.cloud.vm.VMInstanceVO;
 +import com.cloud.vm.VirtualMachine;
 +import com.cloud.vm.VirtualMachineManager;
 +import com.cloud.vm.VirtualMachineProfile;
 +import com.cloud.vm.VirtualMachineProfileImpl;
 +import com.cloud.vm.VmDetailConstants;
 +import com.cloud.vm.dao.NicDao;
 +import com.cloud.vm.dao.VMInstanceDao;
 +import com.google.common.base.Strings;
 +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);
 +
 +    @Inject
 +    private AgentManager agentManager;
 +    @Inject
 +    private DataCenterDao dataCenterDao;
 +    @Inject
 +    private ClusterDao clusterDao;
 +    @Inject
 +    private HostDao hostDao;
 +    @Inject
 +    private AccountService accountService;
 +    @Inject
 +    private UserDao userDao;
 +    @Inject
 +    private VMTemplateDao templateDao;
 +    @Inject
 +    private VMTemplatePoolDao templatePoolDao;
 +    @Inject
 +    private ServiceOfferingDao serviceOfferingDao;
 +    @Inject
 +    private DiskOfferingDao diskOfferingDao;
 +    @Inject
 +    private ResourceManager resourceManager;
 +    @Inject
 +    private ResourceLimitService resourceLimitService;
 +    @Inject
 +    private UserVmManager userVmManager;
 +    @Inject
 +    private ResponseGenerator responseGenerator;
 +    @Inject
 +    private VolumeOrchestrationService volumeManager;
 +    @Inject
 +    private VolumeDao volumeDao;
 +    @Inject
 +    private PrimaryDataStoreDao primaryDataStoreDao;
 +    @Inject
 +    private NetworkDao networkDao;
 +    @Inject
 +    private NetworkOrchestrationService networkOrchestrationService;
 +    @Inject
 +    private VMInstanceDao vmDao;
 +    @Inject
 +    private CapacityManager capacityManager;
 +    @Inject
 +    private VolumeApiService volumeApiService;
 +    @Inject
 +    private DeploymentPlanningManager deploymentPlanningManager;
 +    @Inject
 +    private VirtualMachineManager virtualMachineManager;
 +    @Inject
 +    private ManagementService managementService;
 +    @Inject
 +    private NicDao nicDao;
 +    @Inject
 +    private NetworkModel networkModel;
 +    @Inject
 +    private ConfigurationDao configurationDao;
 +    @Inject
 +    private GuestOSDao guestOSDao;
 +    @Inject
 +    private GuestOSHypervisorDao guestOSHypervisorDao;
 +    @Inject
 +    private VMSnapshotDao vmSnapshotDao;
 +    @Inject
 +    private SnapshotDao snapshotDao;
 +    @Inject
 +    private UserVmDao userVmDao;
 +
 +    protected Gson gson;
 +
 +    public UnmanagedVMsManagerImpl() {
 +        gson = GsonHelper.getGsonLogger();
 +    }
 +
 +    private VMTemplateVO createDefaultDummyVmImportTemplate() {
 +        VMTemplateVO template = null;
 +        try {
 +            template = 
VMTemplateVO.createSystemIso(templateDao.getNextInSequence(Long.class, "id"), 
VM_IMPORT_DEFAULT_TEMPLATE_NAME, VM_IMPORT_DEFAULT_TEMPLATE_NAME, true,
 +                    "", true, 64, Account.ACCOUNT_ID_SYSTEM, "",
 +                    "VM Import Default Template", false, 1);
 +            template.setState(VirtualMachineTemplate.State.Inactive);
 +            template = templateDao.persist(template);
 +            if (template == null) {
 +                return null;
 +            }
 +            templateDao.remove(template.getId());
 +            template = 
templateDao.findByName(VM_IMPORT_DEFAULT_TEMPLATE_NAME);
 +        } catch (Exception e) {
 +            LOGGER.error("Unable to create default dummy template for VM 
import", e);
 +        }
 +        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.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 (!Strings.isNullOrEmpty(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 (!Strings.isNullOrEmpty(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) {
 +            return additionalNameFilter;
 +        }
 +        if (cluster.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
 +            // VMWare considers some templates as VM and they are not 
filtered by VirtualMachineMO.isTemplate()
 +            List<VMTemplateStoragePoolVO> templates = 
templatePoolDao.listAll();
 +            for (VMTemplateStoragePoolVO template : templates) {
 +                additionalNameFilter.add(template.getInstallPath());
 +            }
 +
 +            // VMWare considers some removed volumes as VM
 +            List<VolumeVO> volumes = 
volumeDao.findIncludingRemovedByZone(cluster.getDataCenterId());
 +            for (VolumeVO volumeVO : volumes) {
 +                if (volumeVO.getRemoved() == null) {
 +                    continue;
 +                }
 +                if (Strings.isNullOrEmpty(volumeVO.getChainInfo())) {
 +                    continue;
 +                }
 +                List<String> volumeFileNames = new ArrayList<>();
 +                try {
 +                    VirtualMachineDiskInfo diskInfo = 
gson.fromJson(volumeVO.getChainInfo(), VirtualMachineDiskInfo.class);
 +                    String[] files = diskInfo.getDiskChain();
 +                    if (files.length == 1) {
 +                        continue;
 +                    }
 +                    boolean firstFile = true;
 +                    for (final String file : files) {
 +                        if (firstFile) {
 +                            firstFile = false;
 +                            continue;
 +                        }
 +                        String path = file;
 +                        String[] split = path.split(" ");
 +                        path = split[split.length - 1];
 +                        split = path.split("/");
 +                        ;
 +                        path = split[split.length - 1];
 +                        split = path.split("\\.");
 +                        path = split[0];
 +                        if (!Strings.isNullOrEmpty(path)) {
 +                            if (!additionalNameFilter.contains(path)) {
 +                                volumeFileNames.add(path);
 +                            }
 +                            if (path.contains("-")) {
 +                                split = path.split("-");
 +                                path = split[0];
 +                                if (!Strings.isNullOrEmpty(path) && 
!path.equals("ROOT") && !additionalNameFilter.contains(path)) {
 +                                    volumeFileNames.add(path);
 +                                }
 +                            }
 +                        }
 +                    }
 +                } catch (Exception e) {
 +                    LOGGER.warn(String.format("Unable to find volume file 
name for volume ID: %s while adding filters unmanaged VMs", 
volumeVO.getUuid()), e);
 +                }
 +                if (!volumeFileNames.isEmpty()) {
 +                    additionalNameFilter.addAll(volumeFileNames);
 +                }
 +            }
 +        }
 +        return additionalNameFilter;
 +    }
 +
 +    private List<String> getHostManagedVms(Host host) {
 +        List<String> managedVms = new ArrayList<>();
 +        List<VMInstanceVO> instances = vmDao.listByHostId(host.getId());
 +        for (VMInstanceVO instance : instances) {
 +            managedVms.add(instance.getInstanceName());
 +        }
 +        instances = vmDao.listByLastHostIdAndStates(host.getId(),
 +                VirtualMachine.State.Stopped, VirtualMachine.State.Destroyed,
 +                VirtualMachine.State.Expunging, VirtualMachine.State.Error,
 +                VirtualMachine.State.Unknown, VirtualMachine.State.Shutdown);
 +        for (VMInstanceVO instance : instances) {
 +            managedVms.add(instance.getInstanceName());
 +        }
 +        return managedVms;
 +    }
 +
 +    private boolean hostSupportsServiceOffering(HostVO host, ServiceOffering 
serviceOffering) {
 +        if (host == null) {
 +            return false;
 +        }
 +        if (serviceOffering == null) {
 +            return false;
 +        }
 +        if (Strings.isNullOrEmpty(serviceOffering.getHostTag())) {
 +            return true;
 +        }
 +        hostDao.loadHostTags(host);
 +        return host.getHostTags() != null && 
host.getHostTags().contains(serviceOffering.getHostTag());
 +    }
 +
 +    private boolean storagePoolSupportsDiskOffering(StoragePool pool, 
DiskOffering diskOffering) {
 +        if (pool == null) {
 +            return false;
 +        }
 +        if (diskOffering == null) {
 +            return false;
 +        }
 +        return volumeApiService.doesTargetStorageSupportDiskOffering(pool, 
diskOffering.getTags());
 +    }
 +
 +    private boolean storagePoolSupportsServiceOffering(StoragePool pool, 
ServiceOffering serviceOffering) {
 +        if (pool == null) {
 +            return false;
 +        }
 +        if (serviceOffering == null) {
 +            return false;
 +        }
 +        return volumeApiService.doesTargetStorageSupportDiskOffering(pool, 
serviceOffering.getTags());
 +    }
 +
 +    private ServiceOfferingVO getUnmanagedInstanceServiceOffering(final 
UnmanagedInstanceTO instance, ServiceOfferingVO serviceOffering, final Account 
owner, final DataCenter zone, final Map<String, String> details)
 +            throws ServerApiException, PermissionDeniedException, 
ResourceAllocationException {
 +        if (instance == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM is not valid"));
 +        }
 +        if (serviceOffering == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Service offering is not valid"));
 +        }
 +        accountService.checkAccess(owner, serviceOffering, zone);
 +        final Integer cpu = instance.getCpuCores();
 +        final Integer memory = instance.getMemory();
 +        Integer cpuSpeed = instance.getCpuSpeed() == null ? 0 : 
instance.getCpuSpeed();
 +        if (cpu == null || cpu == 0) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("CPU cores for VM (%s) not valid", instance.getName()));
 +        }
 +        if (memory == null || memory == 0) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Memory for VM (%s) not valid", instance.getName()));
 +        }
 +        if (serviceOffering.isDynamic()) {
 +            if (details.containsKey(VmDetailConstants.CPU_SPEED)) {
 +                try {
 +                    cpuSpeed = 
Integer.parseInt(details.get(VmDetailConstants.CPU_SPEED));
 +                } catch (Exception e) {
 +                }
 +            }
 +            Map<String, String> parameters = new HashMap<>();
 +            parameters.put(VmDetailConstants.CPU_NUMBER, String.valueOf(cpu));
 +            parameters.put(VmDetailConstants.MEMORY, String.valueOf(memory));
 +            if (serviceOffering.getSpeed() == null && cpuSpeed > 0) {
 +                parameters.put(VmDetailConstants.CPU_SPEED, 
String.valueOf(cpuSpeed));
 +            }
 +            serviceOffering.setDynamicFlag(true);
 +            userVmManager.validateCustomParameters(serviceOffering, 
parameters);
 +            serviceOffering = 
serviceOfferingDao.getComputeOffering(serviceOffering, parameters);
 +        } else {
 +            if (!cpu.equals(serviceOffering.getCpu()) && 
!instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) {
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Service offering (%s) %d CPU cores do not match VM CPU cores %d 
and VM is not in powered off state (Power state: %s)", 
serviceOffering.getUuid(), serviceOffering.getCpu(), cpu, 
instance.getPowerState()));
 +            }
 +            if (!memory.equals(serviceOffering.getRamSize()) && 
!instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) {
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Service offering (%s) %dMB memory does not match VM memory %dMB 
and VM is not in powered off state (Power state: %s)", 
serviceOffering.getUuid(), serviceOffering.getRamSize(), memory, 
instance.getPowerState()));
 +            }
 +            if (cpuSpeed != null && cpuSpeed > 0 && 
!cpuSpeed.equals(serviceOffering.getSpeed()) && 
!instance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) {
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Service offering (%s) %dMHz CPU speed does not match VM CPU 
speed %dMHz and VM is not in powered off state (Power state: %s)", 
serviceOffering.getUuid(), serviceOffering.getSpeed(), cpuSpeed, 
instance.getPowerState()));
 +            }
 +        }
 +        resourceLimitService.checkResourceLimit(owner, 
Resource.ResourceType.cpu, new Long(serviceOffering.getCpu()));
 +        resourceLimitService.checkResourceLimit(owner, 
Resource.ResourceType.memory, new Long(serviceOffering.getRamSize()));
 +        return serviceOffering;
 +    }
 +
 +    private Map<String, Network.IpAddresses> getNicIpAddresses(final 
List<UnmanagedInstanceTO.Nic> nics, final Map<String, Network.IpAddresses> 
callerNicIpAddressMap) {
 +        Map<String, Network.IpAddresses> nicIpAddresses = new HashMap<>();
 +        for (UnmanagedInstanceTO.Nic nic : nics) {
 +            Network.IpAddresses ipAddresses = null;
 +            if (MapUtils.isNotEmpty(callerNicIpAddressMap) && 
callerNicIpAddressMap.containsKey(nic.getNicId())) {
 +                ipAddresses = callerNicIpAddressMap.get(nic.getNicId());
 +            }
 +            // If IP is set to auto-assign, check NIC doesn't have more that 
one IP from SDK
 +            if (ipAddresses != null && ipAddresses.getIp4Address() != null && 
ipAddresses.getIp4Address().equals("auto") && 
!CollectionUtils.isEmpty(nic.getIpAddress())) {
 +                if (nic.getIpAddress().size() > 1) {
 +                    throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Multiple IP addresses (%s, %s) present for nic ID: %s. IP 
address cannot be assigned automatically, only single IP address auto-assigning 
supported", nic.getIpAddress().get(0), nic.getIpAddress().get(1), 
nic.getNicId()));
 +                }
 +                String address = nic.getIpAddress().get(0);
 +                if (NetUtils.isValidIp4(address)) {
 +                    ipAddresses.setIp4Address(address);
 +                }
 +            }
 +            if (ipAddresses != null) {
 +                nicIpAddresses.put(nic.getNicId(), ipAddresses);
 +            }
 +        }
 +        return nicIpAddresses;
 +    }
 +
 +    private StoragePool getStoragePool(final UnmanagedInstanceTO.Disk disk, 
final DataCenter zone, final Cluster cluster) {
 +        StoragePool storagePool = null;
 +        final String dsHost = disk.getDatastoreHost();
 +        final String dsPath = disk.getDatastorePath();
 +        final String dsType = disk.getDatastoreType();
 +        final String dsName = disk.getDatastoreName();
 +        if (dsType != null) {
 +            List<StoragePoolVO> pools = 
primaryDataStoreDao.listPoolByHostPath(dsHost, dsPath);
 +            for (StoragePool pool : pools) {
 +                if (pool.getDataCenterId() == zone.getId() &&
 +                        (pool.getClusterId() == null || 
pool.getClusterId().equals(cluster.getId()))) {
 +                    storagePool = pool;
 +                    break;
 +                }
 +            }
 +        }
 +
 +        if (storagePool == null) {
 +            List<StoragePoolVO> pools = 
primaryDataStoreDao.listPoolsByCluster(cluster.getId());
 +            
pools.addAll(primaryDataStoreDao.listByDataCenterId(zone.getId()));
 +            for (StoragePool pool : pools) {
 +                if (pool.getPath().endsWith(dsName)) {
 +                    storagePool = pool;
 +                    break;
 +                }
 +            }
 +        }
 +        if (storagePool == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Storage pool for disk %s(%s) with datastore: %s not found in 
zone ID: %s", disk.getLabel(), disk.getDiskId(), disk.getDatastoreName(), 
zone.getUuid()));
 +        }
 +        return storagePool;
 +    }
 +
++    private Pair<UnmanagedInstanceTO.Disk, List<UnmanagedInstanceTO.Disk>> 
getRootAndDataDisks(List<UnmanagedInstanceTO.Disk> disks, final Map<String, 
Long> dataDiskOfferingMap) {
++        UnmanagedInstanceTO.Disk rootDisk = null;
++        List<UnmanagedInstanceTO.Disk> dataDisks = new ArrayList<>();
++        if (disks.size() == 1) {
++            rootDisk = disks.get(0);
++            return new Pair<>(rootDisk, dataDisks);
++        }
++        Set<String> callerDiskIds = dataDiskOfferingMap.keySet();
++        if (callerDiskIds.size() != disks.size() - 1) {
++            String msg = String.format("VM has total %d disks for which %d 
disk offering mappings provided. %d disks need a disk offering for import", 
disks.size(), callerDiskIds.size(), disks.size()-1);
++            LOGGER.error(String.format("%s. %s parameter can be used to 
provide disk offerings for the disks", msg, 
ApiConstants.DATADISK_OFFERING_LIST));
++            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg);
++        }
++        List<String> diskIdsWithoutOffering = new ArrayList<>();
++        for (UnmanagedInstanceTO.Disk disk : disks) {
++            String diskId = disk.getDiskId();
++            if (!callerDiskIds.contains(diskId)) {
++                diskIdsWithoutOffering.add(diskId);
++                rootDisk = disk;
++            } else {
++                dataDisks.add(disk);
++            }
++        }
++        if (diskIdsWithoutOffering.size() > 1) {
++            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM has total %d disks, disk offering mapping not provided for %d 
disks. Disk IDs that may need a disk offering - %s", disks.size(), 
diskIdsWithoutOffering.size()-1, String.join(", ", diskIdsWithoutOffering)));
++        }
++        return new Pair<>(rootDisk, dataDisks);
++    }
++
 +    private void 
checkUnmanagedDiskAndOfferingForImport(UnmanagedInstanceTO.Disk disk, 
DiskOffering diskOffering, ServiceOffering serviceOffering, final Account 
owner, final DataCenter zone, final Cluster cluster, final boolean 
migrateAllowed)
 +            throws ServerApiException, PermissionDeniedException, 
ResourceAllocationException {
 +        if (serviceOffering == null && diskOffering == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Disk offering for disk ID: %s not found during VM import", 
disk.getDiskId()));
 +        }
 +        if (diskOffering != null) {
 +            accountService.checkAccess(owner, diskOffering, zone);
 +        }
 +        resourceLimitService.checkResourceLimit(owner, 
Resource.ResourceType.volume);
 +        if (disk.getCapacity() == null || disk.getCapacity() == 0) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Size of disk(ID: %s) is found invalid during VM import", 
disk.getDiskId()));
 +        }
 +        if (diskOffering != null && !diskOffering.isCustomized() && 
diskOffering.getDiskSize() == 0) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Size of fixed disk offering(ID: %s) is found invalid during VM 
import", diskOffering.getUuid()));
 +        }
 +        if (diskOffering != null && !diskOffering.isCustomized() && 
diskOffering.getDiskSize() < disk.getCapacity()) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Size of disk offering(ID: %s) %dGB is found less than the size 
of disk(ID: %s) %dGB during VM import", diskOffering.getUuid(), 
(diskOffering.getDiskSize() / Resource.ResourceType.bytesToGiB), 
disk.getDiskId(), (disk.getCapacity() / (Resource.ResourceType.bytesToGiB))));
 +        }
 +        StoragePool storagePool = getStoragePool(disk, zone, cluster);
 +        if (diskOffering != null && !migrateAllowed && 
!storagePoolSupportsDiskOffering(storagePool, diskOffering)) {
 +            throw new InvalidParameterValueException(String.format("Disk 
offering: %s is not compatible with storage pool: %s of unmanaged disk: %s", 
diskOffering.getUuid(), storagePool.getUuid(), disk.getDiskId()));
 +        }
 +        if (serviceOffering != null && !migrateAllowed && 
!storagePoolSupportsServiceOffering(storagePool, serviceOffering)) {
 +            throw new InvalidParameterValueException(String.format("Service 
offering: %s is not compatible with storage pool: %s of unmanaged disk: %s", 
serviceOffering.getUuid(), storagePool.getUuid(), disk.getDiskId()));
 +        }
 +    }
 +
 +    private void 
checkUnmanagedDiskAndOfferingForImport(List<UnmanagedInstanceTO.Disk> disks, 
final Map<String, Long> diskOfferingMap, final Account owner, final DataCenter 
zone, final Cluster cluster, final boolean migrateAllowed)
 +            throws ServerApiException, PermissionDeniedException, 
ResourceAllocationException {
 +        String diskController = null;
 +        for (UnmanagedInstanceTO.Disk disk : disks) {
 +            if (disk == null) {
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Unable to retrieve disk details for VM"));
 +            }
 +            if (!diskOfferingMap.containsKey(disk.getDiskId())) {
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Disk offering for disk ID: %s not found during VM import", 
disk.getDiskId()));
 +            }
 +            if (Strings.isNullOrEmpty(diskController)) {
 +                diskController = disk.getController();
 +            } else {
 +                if (!diskController.equals(disk.getController())) {
 +                    throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Multiple data disk controllers of different type (%s, %s) are 
not supported for import. Please make sure that all data disk controllers are 
of the same type", diskController, disk.getController()));
 +                }
 +            }
 +            checkUnmanagedDiskAndOfferingForImport(disk, 
diskOfferingDao.findById(diskOfferingMap.get(disk.getDiskId())), null, owner, 
zone, cluster, migrateAllowed);
 +        }
 +    }
 +
 +    private void checkUnmanagedNicAndNetworkForImport(UnmanagedInstanceTO.Nic 
nic, Network network, final DataCenter zone, final Account owner, final boolean 
autoAssign) throws ServerApiException {
 +        if (nic == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Unable to retrieve NIC details during VM import"));
 +        }
 +        if (network == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Network for nic ID: %s not found during VM import", 
nic.getNicId()));
 +        }
 +        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()));
 +        }
 +        networkModel.checkNetworkPermissions(owner, network);
 +        if (!autoAssign && 
network.getGuestType().equals(Network.GuestType.Isolated)) {
 +            return;
 +        }
 +
 +        String networkBroadcastUri = network.getBroadcastUri() == null ? null 
: network.getBroadcastUri().toString();
 +        if (nic.getVlan() != null && nic.getVlan() != 0 && nic.getPvlan() == 
null &&
 +                (Strings.isNullOrEmpty(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()));
 +        }
 +        if (nic.getVlan() != null && nic.getVlan() != 0 && nic.getPvlan() != 
null && nic.getPvlan() != 0 &&
 +                (Strings.isNullOrEmpty(network.getBroadcastUri().toString()) 
||
 +                        
!networkBroadcastUri.equals(String.format("pvlan://%d-i%d", nic.getVlan(), 
nic.getPvlan())))) {
 +            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-i%d during VM import", network.getUuid(), 
networkBroadcastUri, nic.getNicId(), nic.getVlan(), nic.getPvlan()));
 +        }
 +    }
 +
 +    private void 
checkUnmanagedNicAndNetworkHostnameForImport(UnmanagedInstanceTO.Nic nic, 
Network network, final String hostName) throws ServerApiException {
 +        if (nic == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Unable to retrieve NIC details during VM import"));
 +        }
 +        if (network == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Network for nic ID: %s not found during VM import", 
nic.getNicId()));
 +        }
 +        // Check for duplicate hostname in network, get all vms hostNames in 
the network
 +        List<String> hostNames = vmDao.listDistinctHostNames(network.getId());
 +        if (CollectionUtils.isNotEmpty(hostNames) && 
hostNames.contains(hostName)) {
 +            throw new InvalidParameterValueException("The vm with hostName " 
+ hostName + " already exists in the network domain: " + 
network.getNetworkDomain() + "; network="
 +                    + network);
 +        }
 +    }
 +
 +    private void 
checkUnmanagedNicIpAndNetworkForImport(UnmanagedInstanceTO.Nic nic, Network 
network, final Network.IpAddresses ipAddresses) throws ServerApiException {
 +        if (nic == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Unable to retrieve NIC details during VM import"));
 +        }
 +        if (network == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Network for nic ID: %s not found during VM import", 
nic.getNicId()));
 +        }
 +        // Check IP is assigned for non L2 networks
 +        if (!network.getGuestType().equals(Network.GuestType.L2) && 
(ipAddresses == null || Strings.isNullOrEmpty(ipAddresses.getIp4Address()))) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("NIC(ID: %s) needs a valid IP address for it to be associated 
with network(ID: %s). %s parameter of API can be used for this", 
nic.getNicId(), network.getUuid(), ApiConstants.NIC_IP_ADDRESS_LIST));
 +        }
 +        // If network is non L2, IP v4 is assigned and not set to 
auto-assign, check it is available for network
 +        if (!network.getGuestType().equals(Network.GuestType.L2) && 
ipAddresses != null && !Strings.isNullOrEmpty(ipAddresses.getIp4Address()) && 
!ipAddresses.getIp4Address().equals("auto")) {
 +            Set<Long> ips = networkModel.getAvailableIps(network, 
ipAddresses.getIp4Address());
 +            if (CollectionUtils.isEmpty(ips) || 
!ips.contains(NetUtils.ip2Long(ipAddresses.getIp4Address()))) {
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("IP address %s for NIC(ID: %s) is not available in network(ID: 
%s)", ipAddresses.getIp4Address(), nic.getNicId(), network.getUuid()));
 +            }
 +        }
 +    }
 +
 +    private Map<String, Long> 
getUnmanagedNicNetworkMap(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 {
 +        Map<String, Long> nicNetworkMap = new HashMap<>();
 +        String nicAdapter = null;
 +        for (UnmanagedInstanceTO.Nic nic : nics) {
 +            if (Strings.isNullOrEmpty(nicAdapter)) {
 +                nicAdapter = nic.getAdapterType();
 +            } else {
 +                if (!nicAdapter.equals(nic.getAdapterType())) {
 +                    throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Multiple network adapter of different type (%s, %s) are not 
supported for import. Please make sure that all network adapters are of the 
same type", nicAdapter, nic.getAdapterType()));
 +                }
 +            }
 +            Network network = null;
 +            Network.IpAddresses ipAddresses = null;
 +            if (MapUtils.isNotEmpty(callerNicIpAddressMap) && 
callerNicIpAddressMap.containsKey(nic.getNicId())) {
 +                ipAddresses = callerNicIpAddressMap.get(nic.getNicId());
 +            }
 +            if (!callerNicNetworkMap.containsKey(nic.getNicId())) {
 +                if (nic.getVlan() != null && nic.getVlan() != 0) {
 +                    // Find a suitable network
 +                    List<NetworkVO> networks = 
networkDao.listByZone(zone.getId());
 +                    for (NetworkVO networkVO : networks) {
 +                        if (networkVO.getTrafficType() == 
Networks.TrafficType.None || 
Networks.TrafficType.isSystemNetwork(networkVO.getTrafficType())) {
 +                            continue;
 +                        }
 +                        try {
 +                            checkUnmanagedNicAndNetworkForImport(nic, 
networkVO, zone, owner, true);
 +                            network = networkVO;
 +                        } catch (Exception e) {
 +                        }
 +                        if (network != null) {
 +                            checkUnmanagedNicAndNetworkHostnameForImport(nic, 
network, hostName);
 +                            checkUnmanagedNicIpAndNetworkForImport(nic, 
network, ipAddresses);
 +                            break;
 +                        }
 +                    }
 +                }
 +            } else {
 +                network = 
networkDao.findById(callerNicNetworkMap.get(nic.getNicId()));
 +                checkUnmanagedNicAndNetworkForImport(nic, network, zone, 
owner, false);
 +                checkUnmanagedNicAndNetworkHostnameForImport(nic, network, 
hostName);
 +                checkUnmanagedNicIpAndNetworkForImport(nic, network, 
ipAddresses);
 +            }
 +            if (network == null) {
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Suitable network for nic(ID: %s) not found during VM import", 
nic.getNicId()));
 +            }
 +            nicNetworkMap.put(nic.getNicId(), network.getId());
 +        }
 +        return nicNetworkMap;
 +    }
 +
 +    private Pair<DiskProfile, StoragePool> 
importDisk(UnmanagedInstanceTO.Disk disk, VirtualMachine vm, Cluster cluster, 
DiskOffering diskOffering,
 +                                                      Volume.Type type, 
String name, Long diskSize, Long minIops, Long maxIops, VirtualMachineTemplate 
template,
 +                                                      Account owner, Long 
deviceId) {
 +        final DataCenter zone = dataCenterDao.findById(vm.getDataCenterId());
 +        final String path = Strings.isNullOrEmpty(disk.getFileBaseName()) ? 
disk.getImagePath() : disk.getFileBaseName();
 +        String chainInfo = disk.getChainInfo();
 +        if (Strings.isNullOrEmpty(chainInfo)) {
 +            VirtualMachineDiskInfo diskInfo = new VirtualMachineDiskInfo();
 +            diskInfo.setDiskDeviceBusName(String.format("%s%d:%d", 
disk.getController(), disk.getControllerUnit(), disk.getPosition()));
 +            diskInfo.setDiskChain(new String[]{disk.getImagePath()});
 +            chainInfo = gson.toJson(diskInfo);
 +        }
 +        StoragePool storagePool = getStoragePool(disk, zone, cluster);
 +        DiskProfile profile = volumeManager.importVolume(type, name, 
diskOffering, diskSize,
 +                minIops, maxIops, vm, template, owner, deviceId, 
storagePool.getId(), path, chainInfo);
 +
 +        return new Pair<DiskProfile, StoragePool>(profile, storagePool);
 +    }
 +
 +    private NicProfile importNic(UnmanagedInstanceTO.Nic nic, VirtualMachine 
vm, Network network, Network.IpAddresses ipAddresses, boolean isDefaultNic, 
boolean forced) throws InsufficientVirtualNetworkCapacityException, 
InsufficientAddressCapacityException {
 +        Pair<NicProfile, Integer> result = 
networkOrchestrationService.importNic(nic.getMacAddress(), 0, network, 
isDefaultNic, vm, ipAddresses, forced);
 +        if (result == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("NIC ID: %s import failed", nic.getNicId()));
 +        }
 +        return result.first();
 +    }
 +
 +    private void cleanupFailedImportVM(final UserVm userVm) {
 +        if (userVm == null) {
 +            return;
 +        }
 +        VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm);
 +        // Remove all volumes
 +        volumeDao.deleteVolumesByInstance(userVm.getId());
 +        // Remove all nics
 +        try {
 +            networkOrchestrationService.release(profile, true);
 +        } catch (Exception e) {
 +            LOGGER.error(String.format("Unable to release NICs for 
unsuccessful import unmanaged VM: %s", userVm.getInstanceName()), e);
 +            nicDao.removeNicsForInstance(userVm.getId());
 +        }
 +        // Remove vm
 +        vmDao.remove(userVm.getId());
 +    }
 +
 +    private UserVm migrateImportedVM(HostVO sourceHost, 
VirtualMachineTemplate template, ServiceOfferingVO serviceOffering, UserVm 
userVm, final Account owner, List<Pair<DiskProfile, StoragePool>> 
diskProfileStoragePoolList) {
 +        UserVm vm = userVm;
 +        if (vm == null) {
 +            LOGGER.error(String.format("Failed to check migrations need 
during VM import"));
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Failed to check migrations need during VM import"));
 +        }
 +        if (sourceHost == null || serviceOffering == null || 
diskProfileStoragePoolList == null) {
 +            LOGGER.error(String.format("Failed to check migrations need 
during import, VM: %s", userVm.getInstanceName()));
 +            cleanupFailedImportVM(vm);
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Failed to check migrations need during import, VM: %s", 
userVm.getInstanceName()));
 +        }
 +        if (!hostSupportsServiceOffering(sourceHost, serviceOffering)) {
 +            LOGGER.debug(String.format("VM %s needs to be migrated", 
vm.getUuid()));
 +            final VirtualMachineProfile profile = new 
VirtualMachineProfileImpl(vm, template, serviceOffering, owner, null);
 +            DeploymentPlanner.ExcludeList excludeList = new 
DeploymentPlanner.ExcludeList();
 +            excludeList.addHost(sourceHost.getId());
 +            final DataCenterDeployment plan = new 
DataCenterDeployment(sourceHost.getDataCenterId(), sourceHost.getPodId(), 
sourceHost.getClusterId(), null, null, null);
 +            DeployDestination dest = null;
 +            try {
 +                dest = deploymentPlanningManager.planDeployment(profile, 
plan, excludeList, null);
 +            } catch (Exception e) {
 +                LOGGER.warn(String.format("VM import failed for unmanaged vm: 
%s during vm migration, finding deployment destination", vm.getInstanceName()), 
e);
 +                cleanupFailedImportVM(vm);
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed for unmanaged vm: %s during vm migration, 
finding deployment destination", vm.getInstanceName()));
 +            }
 +            if (dest != null) {
 +                if (LOGGER.isDebugEnabled()) {
 +                    LOGGER.debug(" Found " + dest + " for migrating the vm 
to");
 +                }
 +            }
 +            if (dest == null) {
 +                cleanupFailedImportVM(vm);
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed for unmanaged vm: %s during vm migration, no 
deployment destination found", vm.getInstanceName()));
 +            }
 +            try {
 +                if (vm.getState().equals(VirtualMachine.State.Stopped)) {
 +                    VMInstanceVO vmInstanceVO = 
vmDao.findById(userVm.getId());
 +                    vmInstanceVO.setHostId(dest.getHost().getId());
 +                    vmInstanceVO.setLastHostId(dest.getHost().getId());
 +                    vmDao.update(vmInstanceVO.getId(), vmInstanceVO);
 +                } else {
 +                    virtualMachineManager.migrate(vm.getUuid(), 
sourceHost.getId(), dest);
 +                }
 +                vm = userVmManager.getUserVm(vm.getId());
 +            } catch (Exception e) {
 +                LOGGER.error(String.format("VM import failed for unmanaged 
vm: %s during vm migration", vm.getInstanceName()), e);
 +                cleanupFailedImportVM(vm);
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed for unmanaged vm: %s during vm migration. %s", 
userVm.getInstanceName(), e.getMessage()));
 +            }
 +        }
 +        for (Pair<DiskProfile, StoragePool> diskProfileStoragePool : 
diskProfileStoragePoolList) {
 +            if (diskProfileStoragePool == null ||
 +                    diskProfileStoragePool.first() == null ||
 +                    diskProfileStoragePool.second() == null) {
 +                continue;
 +            }
 +            DiskProfile profile = diskProfileStoragePool.first();
 +            DiskOffering dOffering = 
diskOfferingDao.findById(profile.getDiskOfferingId());
 +            if (dOffering == null) {
 +                continue;
 +            }
 +            VolumeVO volumeVO = volumeDao.findById(profile.getVolumeId());
 +            if (volumeVO == null) {
 +                continue;
 +            }
 +            boolean poolSupportsOfferings = 
storagePoolSupportsDiskOffering(diskProfileStoragePool.second(), dOffering);
 +            if (poolSupportsOfferings && profile.getType() == 
Volume.Type.ROOT) {
 +                poolSupportsOfferings = 
storagePoolSupportsServiceOffering(diskProfileStoragePool.second(), 
serviceOffering);
 +            }
 +            if (poolSupportsOfferings) {
 +                continue;
 +            }
 +            LOGGER.debug(String.format("Volume %s needs to be migrated", 
volumeVO.getUuid()));
 +            Pair<List<? extends StoragePool>, List<? extends StoragePool>> 
poolsPair = 
managementService.listStoragePoolsForMigrationOfVolume(profile.getVolumeId());
 +            if (CollectionUtils.isEmpty(poolsPair.first()) && 
CollectionUtils.isEmpty(poolsPair.second())) {
 +                cleanupFailedImportVM(vm);
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed for unmanaged vm: %s during volume ID: %s 
migration as no suitable pool(s) found", userVm.getInstanceName(), 
volumeVO.getUuid()));
 +            }
 +            List<? extends StoragePool> storagePools = poolsPair.second();
 +            StoragePool storagePool = null;
 +            if (CollectionUtils.isNotEmpty(storagePools)) {
 +                for (StoragePool pool : storagePools) {
 +                    if (diskProfileStoragePool.second().getId() != 
pool.getId() &&
 +                            storagePoolSupportsDiskOffering(pool, dOffering) 
&&
 +                            (!profile.getType().equals(Volume.Type.ROOT) ||
 +                                    
profile.getType().equals(Volume.Type.ROOT) && 
storagePoolSupportsServiceOffering(pool, serviceOffering))) {
 +                        storagePool = pool;
 +                        break;
 +                    }
 +                }
 +            }
 +            // For zone-wide pools, at times, suitable storage pools are not 
returned therefore consider all pools.
 +            if (storagePool == null && 
CollectionUtils.isNotEmpty(poolsPair.first())) {
 +                storagePools = poolsPair.first();
 +                for (StoragePool pool : storagePools) {
 +                    if (diskProfileStoragePool.second().getId() != 
pool.getId() &&
 +                            storagePoolSupportsDiskOffering(pool, dOffering) 
&&
 +                            (!profile.getType().equals(Volume.Type.ROOT) ||
 +                                    
profile.getType().equals(Volume.Type.ROOT) && 
storagePoolSupportsServiceOffering(pool, serviceOffering))) {
 +                        storagePool = pool;
 +                        break;
 +                    }
 +                }
 +            }
 +            if (storagePool == null) {
 +                cleanupFailedImportVM(vm);
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed for unmanaged vm: %s during volume ID: %s 
migration as no suitable pool found", userVm.getInstanceName(), 
volumeVO.getUuid()));
 +            } else {
 +                LOGGER.debug(String.format("Found storage pool %s(%s) for 
migrating the volume %s to", storagePool.getName(), storagePool.getUuid(), 
volumeVO.getUuid()));
 +            }
 +            try {
 +                Volume volume = null;
 +                if (vm.getState().equals(VirtualMachine.State.Running)) {
 +                    volume = volumeManager.liveMigrateVolume(volumeVO, 
storagePool);
 +                } else {
 +                    volume = volumeManager.migrateVolume(volumeVO, 
storagePool);
 +                }
 +                if (volume == null) {
 +                    String msg = "";
 +                    if (vm.getState().equals(VirtualMachine.State.Running)) {
 +                        msg = String.format("Live migration for volume ID: %s 
to destination pool ID: %s failed", volumeVO.getUuid(), storagePool.getUuid());
 +                    } else {
 +                        msg = String.format("Migration for volume ID: %s to 
destination pool ID: %s failed", volumeVO.getUuid(), storagePool.getUuid());
 +                    }
 +                    LOGGER.error(msg);
 +                    throw new CloudRuntimeException(msg);
 +                }
 +            } catch (Exception e) {
 +                LOGGER.error(String.format("VM import failed for unmanaged 
vm: %s during volume migration", vm.getInstanceName()), e);
 +                cleanupFailedImportVM(vm);
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed for unmanaged vm: %s during volume migration. 
%s", userVm.getInstanceName(), Strings.nullToEmpty(e.getMessage())));
 +            }
 +        }
 +        return userVm;
 +    }
 +
 +    private void publishVMUsageUpdateResourceCount(final UserVm userVm, 
ServiceOfferingVO serviceOfferingVO) {
 +        if (userVm == null || serviceOfferingVO == null) {
 +            LOGGER.error("Failed to publish usage records during VM import");
 +            cleanupFailedImportVM(userVm);
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed for unmanaged vm during publishing usage 
records"));
 +        }
 +        try {
 +            if (!serviceOfferingVO.isDynamic()) {
 +                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, 
userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), 
userVm.getHostName(), serviceOfferingVO.getId(), userVm.getTemplateId(),
 +                        userVm.getHypervisorType().toString(), 
VirtualMachine.class.getName(), userVm.getUuid(), userVm.isDisplayVm());
 +            } else {
 +                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, 
userVm.getAccountId(), userVm.getAccountId(), userVm.getDataCenterId(), 
userVm.getHostName(), serviceOfferingVO.getId(), userVm.getTemplateId(),
 +                        userVm.getHypervisorType().toString(), 
VirtualMachine.class.getName(), userVm.getUuid(), userVm.getDetails(), 
userVm.isDisplayVm());
 +            }
 +            if (userVm.getState() == VirtualMachine.State.Running) {
 +                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_START, 
userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), 
userVm.getHostName(), serviceOfferingVO.getId(), userVm.getTemplateId(),
 +                        userVm.getHypervisorType().toString(), 
VirtualMachine.class.getName(), userVm.getUuid(), userVm.isDisplayVm());
 +            }
 +        } catch (Exception e) {
 +            LOGGER.error(String.format("Failed to publish usage records 
during VM import for unmanaged vm %s", userVm.getInstanceName()), e);
 +            cleanupFailedImportVM(userVm);
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed for unmanaged vm %s during publishing usage 
records", userVm.getInstanceName()));
 +        }
 +        resourceLimitService.incrementResourceCount(userVm.getAccountId(), 
Resource.ResourceType.user_vm, userVm.isDisplayVm());
 +        resourceLimitService.incrementResourceCount(userVm.getAccountId(), 
Resource.ResourceType.cpu, userVm.isDisplayVm(), new 
Long(serviceOfferingVO.getCpu()));
 +        resourceLimitService.incrementResourceCount(userVm.getAccountId(), 
Resource.ResourceType.memory, userVm.isDisplayVm(), new 
Long(serviceOfferingVO.getRamSize()));
 +        // Save usage event and update resource count for user vm volumes
 +        List<VolumeVO> volumes = volumeDao.findByInstance(userVm.getId());
 +        for (VolumeVO volume : volumes) {
 +            try {
 +                
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, 
volume.getAccountId(), volume.getDataCenterId(), volume.getId(), 
volume.getName(), volume.getDiskOfferingId(), null, volume.getSize(),
 +                        Volume.class.getName(), volume.getUuid(), 
volume.isDisplayVolume());
 +            } catch (Exception e) {
 +                LOGGER.error(String.format("Failed to publish volume ID: %s 
usage records during VM import", volume.getUuid()), e);
 +            }
 +            
resourceLimitService.incrementResourceCount(userVm.getAccountId(), 
Resource.ResourceType.volume, volume.isDisplayVolume());
 +            
resourceLimitService.incrementResourceCount(userVm.getAccountId(), 
Resource.ResourceType.primary_storage, volume.isDisplayVolume(), 
volume.getSize());
 +        }
 +
 +        List<NicVO> nics = nicDao.listByVmId(userVm.getId());
 +        for (NicVO nic : nics) {
 +            try {
 +                NetworkVO network = networkDao.findById(nic.getNetworkId());
 +                
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, 
userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(),
 +                        Long.toString(nic.getId()), 
network.getNetworkOfferingId(), null, 1L, VirtualMachine.class.getName(), 
userVm.getUuid(), userVm.isDisplay());
 +            } catch (Exception e) {
 +                LOGGER.error(String.format("Failed to publish network usage 
records during VM import. %s", Strings.nullToEmpty(e.getMessage())));
 +            }
 +        }
 +    }
 +
 +    private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO 
unmanagedInstance, final String instanceName, final DataCenter zone, final 
Cluster cluster, final HostVO host,
 +                                                final VirtualMachineTemplate 
template, final String displayName, final String hostName, final Account 
caller, final Account owner, final Long userId,
 +                                                final ServiceOfferingVO 
serviceOffering, final Map<String, Long> dataDiskOfferingMap,
 +                                                final Map<String, Long> 
nicNetworkMap, final Map<String, Network.IpAddresses> callerNicIpAddressMap,
 +                                                final Map<String, String> 
details, final boolean migrateAllowed, final boolean forced) {
 +        UserVm userVm = null;
 +
 +        ServiceOfferingVO validatedServiceOffering = null;
 +        try {
 +            validatedServiceOffering = 
getUnmanagedInstanceServiceOffering(unmanagedInstance, serviceOffering, owner, 
zone, details);
 +        } catch (Exception e) {
 +            LOGGER.error("Service offering for VM import not compatible", e);
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Failed to import VM: %s. %s", unmanagedInstance.getName(), 
Strings.nullToEmpty(e.getMessage())));
 +        }
 +
 +        Map<String, String> allDetails = new HashMap<>(details);
 +        if (validatedServiceOffering.isDynamic()) {
 +            allDetails.put(VmDetailConstants.CPU_NUMBER, 
String.valueOf(validatedServiceOffering.getCpu()));
 +            allDetails.put(VmDetailConstants.MEMORY, 
String.valueOf(validatedServiceOffering.getRamSize()));
 +            if (serviceOffering.getSpeed() == null) {
 +                allDetails.put(VmDetailConstants.CPU_SPEED, 
String.valueOf(validatedServiceOffering.getSpeed()));
 +            }
 +        }
 +
 +        if (!migrateAllowed && !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
 +        List<UnmanagedInstanceTO.Disk> unmanagedInstanceDisks = 
unmanagedInstance.getDisks();
 +        if (CollectionUtils.isEmpty(unmanagedInstanceDisks)) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("No attached disks found for the unmanaged VM: %s", 
instanceName));
 +        }
-         final UnmanagedInstanceTO.Disk rootDisk = 
unmanagedInstance.getDisks().get(0);
++        Pair<UnmanagedInstanceTO.Disk, List<UnmanagedInstanceTO.Disk>> 
rootAndDataDisksPair = getRootAndDataDisks(unmanagedInstanceDisks, 
dataDiskOfferingMap);
++        final UnmanagedInstanceTO.Disk rootDisk = 
rootAndDataDisksPair.first();
++        final List<UnmanagedInstanceTO.Disk> dataDisks = 
rootAndDataDisksPair.second();
 +        if (rootDisk == null || 
Strings.isNullOrEmpty(rootDisk.getController())) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("VM import failed. Unable to retrieve root disk details for VM: 
%s ", instanceName));
 +        }
 +        allDetails.put(VmDetailConstants.ROOT_DISK_CONTROLLER, 
rootDisk.getController());
-         List<UnmanagedInstanceTO.Disk> dataDisks = new ArrayList<>();
 +        try {
 +            checkUnmanagedDiskAndOfferingForImport(rootDisk, null, 
validatedServiceOffering, owner, zone, cluster, migrateAllowed);
-             if (unmanagedInstanceDisks.size() > 1) { // Data disk(s) present
-                 dataDisks.addAll(unmanagedInstanceDisks);
-                 dataDisks.remove(0);
++            if (CollectionUtils.isNotEmpty(dataDisks)) { // Data disk(s) 
present
 +                checkUnmanagedDiskAndOfferingForImport(dataDisks, 
dataDiskOfferingMap, owner, zone, cluster, migrateAllowed);
 +                allDetails.put(VmDetailConstants.DATA_DISK_CONTROLLER, 
dataDisks.get(0).getController());
 +            }
 +            resourceLimitService.checkResourceLimit(owner, 
Resource.ResourceType.volume, unmanagedInstanceDisks.size());
 +        } catch (ResourceAllocationException e) {
 +            LOGGER.error(String.format("Volume resource allocation error for 
owner: %s", owner.getUuid()), e);
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Volume resource allocation error for owner: %s. %s", 
owner.getUuid(), Strings.nullToEmpty(e.getMessage())));
 +        }
 +        // Check NICs and supplied networks
 +        Map<String, Network.IpAddresses> nicIpAddressMap = 
getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap);
 +        Map<String, Long> allNicNetworkMap = 
getUnmanagedNicNetworkMap(unmanagedInstance.getNics(), nicNetworkMap, 
nicIpAddressMap, zone, hostName, owner);
 +        if (!CollectionUtils.isEmpty(unmanagedInstance.getNics())) {
 +            allDetails.put(VmDetailConstants.NIC_ADAPTER, 
unmanagedInstance.getNics().get(0).getAdapterType());
 +        }
 +        VirtualMachine.PowerState powerState = 
VirtualMachine.PowerState.PowerOff;
 +        if 
(unmanagedInstance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOn))
 {
 +            powerState = VirtualMachine.PowerState.PowerOn;
 +        }
 +        try {
 +            userVm = userVmManager.importVM(zone, host, template, 
instanceName, displayName, owner,
 +                    null, caller, true, null, owner.getAccountId(), userId,
 +                    validatedServiceOffering, null, hostName,
 +                    cluster.getHypervisorType(), allDetails, powerState);
 +        } catch (InsufficientCapacityException ice) {
 +            LOGGER.error(String.format("Failed to import vm name: %s", 
instanceName), ice);
 +            throw new 
ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage());
 +        }
 +        if (userVm == null) {
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Failed to import vm name: %s", instanceName));
 +        }
 +        List<Pair<DiskProfile, StoragePool>> diskProfileStoragePoolList = new 
ArrayList<>();
 +        try {
 +            if (rootDisk.getCapacity() == null || rootDisk.getCapacity() == 
0) {
 +                throw new InvalidParameterValueException(String.format("Root 
disk ID: %s size is invalid", rootDisk.getDiskId()));
 +            }
 +            Long minIops = null;
 +            if (details.containsKey("minIops")) {
 +                minIops = Long.parseLong(details.get("minIops"));
 +            }
 +            Long maxIops = null;
 +            if (details.containsKey("maxIops")) {
 +                maxIops = Long.parseLong(details.get("maxIops"));
 +            }
 +            diskProfileStoragePoolList.add(importDisk(rootDisk, userVm, 
cluster, serviceOffering, Volume.Type.ROOT, String.format("ROOT-%d", 
userVm.getId()),
 +                    (rootDisk.getCapacity() / 
Resource.ResourceType.bytesToGiB), minIops, maxIops,
 +                    template, owner, null));
 +            for (UnmanagedInstanceTO.Disk disk : dataDisks) {
 +                if (disk.getCapacity() == null || disk.getCapacity() == 0) {
 +                    throw new 
InvalidParameterValueException(String.format("Disk ID: %s size is invalid", 
rootDisk.getDiskId()));
 +                }
 +                DiskOffering offering = 
diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId()));
 +                diskProfileStoragePoolList.add(importDisk(disk, userVm, 
cluster, offering, Volume.Type.DATADISK, String.format("DATA-%d-%s", 
userVm.getId(), disk.getDiskId()),
 +                        (disk.getCapacity() / 
Resource.ResourceType.bytesToGiB), offering.getMinIops(), offering.getMaxIops(),
 +                        template, owner, null));
 +            }
 +        } catch (Exception e) {
 +            LOGGER.error(String.format("Failed to import volumes while 
importing vm: %s", instanceName), e);
 +            cleanupFailedImportVM(userVm);
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Failed to import volumes while importing vm: %s. %s", 
instanceName, Strings.nullToEmpty(e.getMessage())));
 +        }
 +        try {
 +            boolean firstNic = true;
 +            for (UnmanagedInstanceTO.Nic nic : unmanagedInstance.getNics()) {
 +                Network network = 
networkDao.findById(allNicNetworkMap.get(nic.getNicId()));
 +                Network.IpAddresses ipAddresses = 
nicIpAddressMap.get(nic.getNicId());
 +                importNic(nic, userVm, network, ipAddresses, firstNic, 
forced);
 +                firstNic = false;
 +            }
 +        } catch (Exception e) {
 +            LOGGER.error(String.format("Failed to import NICs while importing 
vm: %s", instanceName), e);
 +            cleanupFailedImportVM(userVm);
 +            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Failed to import NICs while importing vm: %s. %s", instanceName, 
Strings.nullToEmpty(e.getMessage())));
 +        }
 +        if (migrateAllowed) {
 +            userVm = migrateImportedVM(host, template, 
validatedServiceOffering, userVm, owner, diskProfileStoragePoolList);
 +        }
 +        publishVMUsageUpdateResourceCount(userVm, validatedServiceOffering);
 +        return userVm;
 +    }
 +
 +    @Override
 +    public ListResponse<UnmanagedInstanceResponse> 
listUnmanagedInstances(ListUnmanagedInstancesCmd cmd) {
 +        final Account caller = CallContext.current().getCallingAccount();
 +        if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
 +            throw new PermissionDeniedException(String.format("Cannot perform 
this operation, Calling account is not root admin: %s", caller.getUuid()));
 +        }
 +        final Long clusterId = cmd.getClusterId();
 +        if (clusterId == null) {
 +            throw new InvalidParameterValueException(String.format("Cluster 
ID cannot be null"));
 +        }
 +        final Cluster cluster = clusterDao.findById(clusterId);
 +        if (cluster == null) {
 +            throw new InvalidParameterValueException(String.format("Cluster 
ID: %d cannot be found", clusterId));
 +        }
 +        if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
 +            throw new InvalidParameterValueException(String.format("VM 
ingestion is currently not supported for hypervisor: %s", 
cluster.getHypervisorType().toString()));
 +        }
 +        List<HostVO> hosts = 
resourceManager.listHostsInClusterByStatus(clusterId, Status.Up);
 +        List<String> additionalNameFilters = 
getAdditionalNameFilters(cluster);
 +        List<UnmanagedInstanceResponse> responses = new ArrayList<>();
 +        for (HostVO host : hosts) {
 +            if (host.isInMaintenanceStates()) {
 +                continue;
 +            }
 +            List<String> managedVms = new ArrayList<>();
 +            managedVms.addAll(additionalNameFilters);
 +            managedVms.addAll(getHostManagedVms(host));
 +
 +            GetUnmanagedInstancesCommand command = new 
GetUnmanagedInstancesCommand();
 +            command.setInstanceName(cmd.getName());
 +            command.setManagedInstancesNames(managedVms);
 +            Answer answer = agentManager.easySend(host.getId(), command);
 +            if (!(answer instanceof GetUnmanagedInstancesAnswer)) {
 +                continue;
 +            }
 +            GetUnmanagedInstancesAnswer unmanagedInstancesAnswer = 
(GetUnmanagedInstancesAnswer) answer;
 +            HashMap<String, UnmanagedInstanceTO> unmanagedInstances = new 
HashMap<>();
 +            
unmanagedInstances.putAll(unmanagedInstancesAnswer.getUnmanagedInstances());
 +            Set<String> keys = unmanagedInstances.keySet();
 +            for (String key : keys) {
 +                
responses.add(createUnmanagedInstanceResponse(unmanagedInstances.get(key), 
cluster, host));
 +            }
 +        }
 +        ListResponse<UnmanagedInstanceResponse> listResponses = new 
ListResponse<>();
 +        listResponses.setResponses(responses, responses.size());
 +        return listResponses;
 +    }
 +
 +    @Override
 +    public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd 
cmd) {
 +        final Account caller = CallContext.current().getCallingAccount();
 +        if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
 +            throw new PermissionDeniedException(String.format("Cannot perform 
this operation, Calling account is not root admin: %s", caller.getUuid()));
 +        }
 +        final Long clusterId = cmd.getClusterId();
 +        if (clusterId == null) {
 +            throw new InvalidParameterValueException(String.format("Cluster 
ID cannot be null"));
 +        }
 +        final Cluster cluster = clusterDao.findById(clusterId);
 +        if (cluster == null) {
 +            throw new InvalidParameterValueException(String.format("Cluster 
ID: %d cannot be found", clusterId));
 +        }
 +        if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
 +            throw new InvalidParameterValueException(String.format("VM import 
is currently not supported for hypervisor: %s", 
cluster.getHypervisorType().toString()));
 +        }
 +        final DataCenter zone = 
dataCenterDao.findById(cluster.getDataCenterId());
 +        final String instanceName = cmd.getName();
 +        if (Strings.isNullOrEmpty(instanceName)) {
 +            throw new InvalidParameterValueException(String.format("Instance 
name cannot be empty"));
 +        }
 +        if (cmd.getDomainId() != null && 
Strings.isNullOrEmpty(cmd.getAccountName())) {
 +            throw new InvalidParameterValueException("domainid parameter must 
be specified with account parameter");
 +        }
 +        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 = null;
 +        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 
paramter 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(String.format("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(), Strings.nullToEmpty(e.getMessage())));
 +        }
 +        String displayName = cmd.getDisplayName();
 +        if (Strings.isNullOrEmpty(displayName)) {
 +            displayName = instanceName;
 +        }
 +        String hostName = cmd.getHostName();
 +        if (Strings.isNullOrEmpty(hostName)) {
 +            if (!NetUtils.verifyDomainNameLabel(instanceName, true)) {
 +                throw new 
InvalidParameterValueException(String.format("Please provide hostname for the 
VM. VM name contains unsupported characters for it to 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()));
 +            }
 +        }
 +        final Map<String, Long> nicNetworkMap = cmd.getNicNetworkList();
 +        final Map<String, Network.IpAddresses> nicIpAddressMap = 
cmd.getNicIpAddressList();
 +        final Map<String, Long> dataDiskOfferingMap = 
cmd.getDataDiskToDiskOfferingList();
 +        final Map<String, String> details = cmd.getDetails();
 +        final boolean forced = cmd.isForced();
 +        List<HostVO> hosts = 
resourceManager.listHostsInClusterByStatus(clusterId, Status.Up);
 +        UserVm userVm = null;
 +        List<String> additionalNameFilters = 
getAdditionalNameFilters(cluster);
 +        for (HostVO host : hosts) {
 +            if (host.isInMaintenanceStates()) {
 +                continue;
 +            }
 +            List<String> managedVms = new ArrayList<>();
 +            managedVms.addAll(additionalNameFilters);
 +            managedVms.addAll(getHostManagedVms(host));
 +            GetUnmanagedInstancesCommand command = new 
GetUnmanagedInstancesCommand(instanceName);
 +            command.setManagedInstancesNames(managedVms);
 +            Answer answer = agentManager.easySend(host.getId(), command);
 +            if (!(answer instanceof GetUnmanagedInstancesAnswer)) {
 +                continue;
 +            }
 +            GetUnmanagedInstancesAnswer unmanagedInstancesAnswer = 
(GetUnmanagedInstancesAnswer) answer;
 +            HashMap<String, UnmanagedInstanceTO> unmanagedInstances = 
unmanagedInstancesAnswer.getUnmanagedInstances();
 +            if (MapUtils.isEmpty(unmanagedInstances)) {
 +                continue;
 +            }
 +            Set<String> names = unmanagedInstances.keySet();
 +            for (String name : names) {
 +                if (instanceName.equals(name)) {
 +                    UnmanagedInstanceTO unmanagedInstance = 
unmanagedInstances.get(name);
 +                    if (unmanagedInstance == null) {
 +                        throw new 
ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to 
retrieve details for unmanaged VM: %s", name));
 +                    }
 +                    if 
(template.getName().equals(VM_IMPORT_DEFAULT_TEMPLATE_NAME)) {
 +                        String osName = 
unmanagedInstance.getOperatingSystem();
 +                        GuestOS guestOS = null;
 +                        if (!Strings.isNullOrEmpty(osName)) {
 +                            guestOS = guestOSDao.listByDisplayName(osName);
 +                        }
 +                        GuestOSHypervisor guestOSHypervisor = null;
 +                        if (guestOS != null) {
 +                            guestOSHypervisor = 
guestOSHypervisorDao.findByOsIdAndHypervisor(guestOS.getId(), 
host.getHypervisorType().toString(), host.getHypervisorVersion());
 +                        }
 +                        if (guestOSHypervisor == null && 
!Strings.isNullOrEmpty(unmanagedInstance.getOperatingSystemId())) {
 +                            guestOSHypervisor = 
guestOSHypervisorDao.findByOsNameAndHypervisor(unmanagedInstance.getOperatingSystemId(),
 host.getHypervisorType().toString(), host.getHypervisorVersion());
 +                        }
 +                        if (guestOSHypervisor == null) {
 +                            if (guestOS != null) {
 +                                throw new 
ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to find 
hypervisor guest OS ID: %s details for unmanaged VM: %s for hypervisor: %s 
version: %s. templateid parameter can be used to assign template for VM", 
guestOS.getUuid(), name, host.getHypervisorType().toString(), 
host.getHypervisorVersion()));
 +                            }
 +                            throw new 
ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to 
retrieve guest OS details for unmanaged VM: %s with OS name: %s, OS ID: %s for 
hypervisor: %s version: %s. templateid parameter can be used to assign template 
for VM", name, osName, unmanagedInstance.getOperatingSystemId(), 
host.getHypervisorType().toString(), host.getHypervisorVersion()));
 +                        }
 +                        
template.setGuestOSId(guestOSHypervisor.getGuestOsId());
 +                    }
 +                    userVm = importVirtualMachineInternal(unmanagedInstance, 
instanceName, zone, cluster, host,
 +                            template, displayName, hostName, caller, owner, 
userId,
 +                            serviceOffering, dataDiskOfferingMap,
 +                            nicNetworkMap, nicIpAddressMap,
 +                            details, cmd.getMigrateAllowed(), 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 
responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Full, 
"virtualmachine", userVm).get(0);
 +    }
 +
 +    @Override
 +    public List<Class<?>> getCommands() {
 +        final List<Class<?>> cmdList = new ArrayList<Class<?>>();
 +        cmdList.add(ListUnmanagedInstancesCmd.class);
 +        cmdList.add(ImportUnmanagedInstanceCmd.class);
 +        cmdList.add(UnmanageVMInstanceCmd.class);
 +        return cmdList;
 +    }
 +
 +    /**
 +     * Perform validations before attempting to unmanage a VM from CloudStack:
 +     * - VM must not have any associated volume snapshot
 +     * - VM must not have an attached ISO
 +     */
 +    private void performUnmanageVMInstancePrechecks(VMInstanceVO vmVO) {
 +        if (hasVolumeSnapshotsPriorToUnmanageVM(vmVO)) {
 +            throw new UnsupportedServiceException("Cannot unmanage VM with id 
= " + vmVO.getUuid() +
 +                    " as there are volume snapshots for its volume(s). Please 
remove snapshots before unmanaging.");
 +        }
 +
 +        if (hasISOAttached(vmVO)) {
 +            throw new UnsupportedServiceException("Cannot unmanage VM with id 
= " + vmVO.getUuid() +
 +                    " as there is an ISO attached. Please detach ISO before 
unmanaging.");
 +        }
 +    }
 +
 +    private boolean hasVolumeSnapshotsPriorToUnmanageVM(VMInstanceVO vmVO) {
 +        List<VolumeVO> volumes = volumeDao.findByInstance(vmVO.getId());
 +        for (VolumeVO volume : volumes) {
 +            List<SnapshotVO> snaps = 
snapshotDao.listByVolumeId(volume.getId());
 +            if (CollectionUtils.isNotEmpty(snaps)) {
 +                for (SnapshotVO snap : snaps) {
 +                    if (snap.getState() != Snapshot.State.Destroyed && 
snap.getRemoved() == null) {
 +                        return true;
 +                    }
 +                }
 +            }
 +        }
 +        return false;
 +    }
 +
 +    private boolean hasISOAttached(VMInstanceVO vmVO) {
 +        UserVmVO userVM = userVmDao.findById(vmVO.getId());
 +        if (userVM == null) {
 +            throw new InvalidParameterValueException("Could not find user VM 
with ID = " + vmVO.getUuid());
 +        }
 +        return userVM.getIsoId() != null;
 +    }
 +
 +    /**
 +     * Find a suitable host within the scope of the VM to unmanage to verify 
the VM exists
 +     */
 +    private Long findSuitableHostId(VMInstanceVO vmVO) {
 +        Long hostId = vmVO.getHostId();
 +        if (hostId == null) {
 +            long zoneId = vmVO.getDataCenterId();
 +            List<HostVO> hosts = 
hostDao.listAllHostsUpByZoneAndHypervisor(zoneId, vmVO.getHypervisorType());
 +            for (HostVO host : hosts) {
 +                if (host.isInMaintenanceStates() || host.getState() != 
Status.Up || host.getStatus() != Status.Up) {
 +                    continue;
 +                }
 +                hostId = host.getId();
 +                break;
 +            }
 +        }
 +
 +        if (hostId == null) {
 +            throw new CloudRuntimeException("Cannot find a host to verify if 
the VM to unmanage " +
 +                    "with id = " + vmVO.getUuid() + " exists.");
 +        }
 +        return hostId;
 +    }
 +
 +    @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VM_UNMANAGE, eventDescription = 
"unmanaging VM", async = true)
 +    public boolean unmanageVMInstance(long vmId) {
 +        VMInstanceVO vmVO = vmDao.findById(vmId);
 +        if (vmVO == null || vmVO.getRemoved() != null) {
 +            throw new InvalidParameterValueException("Could not find VM to 
unmanage, it is either removed or not existing VM");
 +        } else if (vmVO.getState() != VirtualMachine.State.Running && 
vmVO.getState() != VirtualMachine.State.Stopped) {
 +            throw new InvalidParameterValueException("VM with id = " + 
vmVO.getUuid() + " must be running or stopped to be unmanaged");
 +        } else if (vmVO.getHypervisorType() != 
Hypervisor.HypervisorType.VMware) {
 +            throw new UnsupportedServiceException("Unmanage VM is currently 
allowed for VMware VMs only");
 +        } else if (vmVO.getType() != VirtualMachine.Type.User) {
 +            throw new UnsupportedServiceException("Unmanage VM is currently 
allowed for guest VMs only");
 +        }
 +
 +        performUnmanageVMInstancePrechecks(vmVO);
 +
 +        Long hostId = findSuitableHostId(vmVO);
 +        String instanceName = vmVO.getInstanceName();
 +
 +        if (!existsVMToUnmanage(instanceName, hostId)) {
 +            throw new CloudRuntimeException("VM with id = " + vmVO.getUuid() 
+ " is not found in the hypervisor");
 +        }
 +
 +        return userVmManager.unmanageUserVM(vmId);
 +    }
 +
 +    /**
 +     * Verify the VM to unmanage exists on the hypervisor
 +     */
 +    private boolean existsVMToUnmanage(String instanceName, Long hostId) {
 +        PrepareUnmanageVMInstanceCommand command = new 
PrepareUnmanageVMInstanceCommand();
 +        command.setInstanceName(instanceName);
 +        Answer ans = agentManager.easySend(hostId, command);
 +        if (!(ans instanceof PrepareUnmanageVMInstanceAnswer)) {
 +            throw new CloudRuntimeException("Error communicating with host " 
+ hostId);
 +        }
 +        PrepareUnmanageVMInstanceAnswer answer = 
(PrepareUnmanageVMInstanceAnswer) ans;
 +        if (!answer.getResult()) {
 +            LOGGER.error("Error verifying VM " + instanceName + " exists on 
host with ID = " + hostId + ": " + answer.getDetails());
 +        }
 +        return answer.getResult();
 +    }
 +
 +    @Override
 +    public String getConfigComponentName() {
 +        return UnmanagedVMsManagerImpl.class.getSimpleName();
 +    }
 +
 +    @Override
 +    public ConfigKey<?>[] getConfigKeys() {
 +        return new ConfigKey<?>[] { UnmanageVMPreserveNic };
 +    }
 +}

Reply via email to