private List relocateVirtualMachine()

in plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java [7292:7524]


    private List<VolumeObjectTO> relocateVirtualMachine(final VmwareHypervisorHost hypervisorHost,
                                                        final String name, final VirtualMachineTO vmTo,
                                                        final String targetHost, final VmwareHypervisorHost hostInTargetCluster,
                                                        final String poolUuid, final List<Pair<VolumeTO, StorageFilerTO>> volToFiler) throws Exception {
        String vmName = name;
        if (vmName == null && vmTo != null) {
            vmName = vmTo.getName();
        }
        VmwareHypervisorHost sourceHyperHost = hypervisorHost;
        VmwareHypervisorHost targetHyperHost = hostInTargetCluster;
        VirtualMachineMO vmMo = null;
        ManagedObjectReference morSourceHostDc = null;
        VirtualMachineRelocateSpec relocateSpec = new VirtualMachineRelocateSpec();
        List<VirtualMachineRelocateSpecDiskLocator> diskLocators = new ArrayList<VirtualMachineRelocateSpecDiskLocator>();
        Set<String> mountedDatastoresAtSource = new HashSet<String>();
        List<VolumeObjectTO> volumeToList =  new ArrayList<>();
        Map<Long, Integer> volumeDeviceKey = new HashMap<Long, Integer>();

        try {
            if (sourceHyperHost == null) {
                sourceHyperHost = getHyperHost(getServiceContext());
            }
            if (targetHyperHost == null && StringUtils.isNotBlank(targetHost)) {
                targetHyperHost = VmwareHelper.getHostMOFromHostName(getServiceContext(), targetHost);
            }
            morSourceHostDc = sourceHyperHost.getHyperHostDatacenter();
            DatacenterMO dcMo = new DatacenterMO(sourceHyperHost.getContext(), morSourceHostDc);
            if (targetHyperHost != null) {
                ManagedObjectReference morTargetHostDc = targetHyperHost.getHyperHostDatacenter();
                if (!morSourceHostDc.getValue().equalsIgnoreCase(morTargetHostDc.getValue())) {
                    String msg = String.format("VM: %s cannot be migrated between different datacenter", vmName);
                    throw new CloudRuntimeException(msg);
                }
            }

            // find VM through source host (VM is not at the target host yet)
            vmMo = sourceHyperHost.findVmOnHyperHost(vmName);
            if (vmMo == null) {
                String msg = String.format("VM: %s does not exist on host: %s", vmName, sourceHyperHost.getHyperHostName());
                logger.warn(msg);
                // find VM through source host (VM is not at the target host yet)
                vmMo = dcMo.findVm(vmName);
                if (vmMo == null) {
                    msg = String.format("VM: %s does not exist on datacenter: %s", vmName, dcMo.getName());
                    logger.error(msg);
                    throw new Exception(msg);
                }
                // VM host has changed
                sourceHyperHost = vmMo.getRunningHost();
            }

            vmName = vmMo.getName();
            String srcHostApiVersion = ((HostMO)sourceHyperHost).getHostAboutInfo().getApiVersion();

            if (StringUtils.isNotBlank(poolUuid)) {
                VmwareHypervisorHost dsHost = targetHyperHost == null ? sourceHyperHost : targetHyperHost;
                ManagedObjectReference morDatastore = null;
                morDatastore = getTargetDatastoreMOReference(poolUuid, dsHost);
                if (morDatastore == null) {
                    String msg = String.format("Unable to find the target datastore: %s on host: %s to execute migration", poolUuid, dsHost.getHyperHostName());
                    logger.error(msg);
                    throw new CloudRuntimeException(msg);
                }
                relocateSpec.setDatastore(morDatastore);
            } else if (CollectionUtils.isNotEmpty(volToFiler)) {
                // Specify destination datastore location for each volume
                VmwareHypervisorHost dsHost = targetHyperHost == null ? sourceHyperHost : targetHyperHost;
                for (Pair<VolumeTO, StorageFilerTO> entry : volToFiler) {
                    VolumeTO volume = entry.first();
                    StorageFilerTO filerTo = entry.second();
                    if (logger.isDebugEnabled()) {
                        logger.debug(String.format("Preparing spec for volume: %s to migrate it to datastore: %s", volume.getName(), filerTo.getUuid()));
                    }
                    ManagedObjectReference morVolumeDatastore = getTargetDatastoreMOReference(filerTo.getUuid(), dsHost);
                    if (morVolumeDatastore == null) {
                        String msg = String.format("Unable to find the target datastore: %s in datacenter: %s to execute migration", filerTo.getUuid(), dcMo.getName());
                        logger.error(msg);
                        throw new CloudRuntimeException(msg);
                    }

                    String mountedDs = getMountedDatastoreName(sourceHyperHost, srcHostApiVersion, filerTo);
                    if (mountedDs != null) {
                        mountedDatastoresAtSource.add(mountedDs);
                    }

                    if (volume.getType() == Volume.Type.ROOT) {
                        relocateSpec.setDatastore(morVolumeDatastore);
                    }
                    VirtualMachineRelocateSpecDiskLocator diskLocator = new VirtualMachineRelocateSpecDiskLocator();
                    diskLocator.setDatastore(morVolumeDatastore);
                    Pair<VirtualDisk, String> diskInfo = getVirtualDiskInfo(vmMo, volume.getPath() + VMDK_EXTENSION);
                    String vmdkAbsFile = VmwareHelper.getAbsoluteVmdkFile(diskInfo.first());
                    if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) {
                        vmMo.updateAdapterTypeIfRequired(vmdkAbsFile);
                    }
                    int diskId = diskInfo.first().getKey();
                    diskLocator.setDiskId(diskId);

                    diskLocators.add(diskLocator);
                    volumeDeviceKey.put(volume.getId(), diskId);
                }
                // If a target datastore is provided for the VM, then by default all volumes associated with the VM will be migrated to that target datastore.
                // Hence set the existing datastore as target datastore for volumes that are not to be migrated.
                List<Pair<Integer, ManagedObjectReference>> diskDatastores = vmMo.getAllDiskDatastores();
                for (Pair<Integer, ManagedObjectReference> diskDatastore : diskDatastores) {
                    if (!volumeDeviceKey.containsValue(diskDatastore.first().intValue())) {
                        VirtualMachineRelocateSpecDiskLocator diskLocator = new VirtualMachineRelocateSpecDiskLocator();
                        diskLocator.setDiskId(diskDatastore.first().intValue());
                        diskLocator.setDatastore(diskDatastore.second());
                        diskLocators.add(diskLocator);
                    }
                }

                relocateSpec.getDisk().addAll(diskLocators);
            }

            // Specific section for MigrateVmWithStorageCommand
            if (vmTo != null) {
                // Prepare network at target before migration
                NicTO[] nics = vmTo.getNics();
                for (NicTO nic : nics) {
                    // prepare network on the host
                    prepareNetworkFromNicInfo((HostMO)targetHyperHost, nic, false,
                            vmTo.getNetworkIdToNetworkNameMap().get(nic.getNetworkId()), vmTo.getType());
                }

                if (targetHyperHost == null) {
                    throw new CloudRuntimeException(String.format("Trying to relocate VM [%s], but target hyper host is null.", vmTo.getUuid()));
                }

                // Ensure secondary storage mounted on target host
                VmwareManager mgr = targetHyperHost.getContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
                Pair<String, Long> secStoreUrlAndId = mgr.getSecondaryStorageStoreUrlAndId(Long.parseLong(_dcId));
                String secStoreUrl = secStoreUrlAndId.first();
                if (secStoreUrl == null) {
                    String msg = String.format("NFS secondary or cache storage of dc %s either doesn't have enough capacity (has reached %d%% usage threshold) or not ready yet, or non-NFS secondary storage is used",
                            _dcId, Math.round(CapacityManager.SecondaryStorageCapacityThreshold.value() * 100));
                    throw new Exception(msg);
                }
                ManagedObjectReference morSecDs = prepareSecondaryDatastoreOnSpecificHost(secStoreUrl, targetHyperHost);
                if (morSecDs == null) {
                    throw new Exception(String.format("Failed to prepare secondary storage on host, NFS secondary or cache store url: %s in dc %s", secStoreUrl, _dcId));
                }
            }

            if (srcHostApiVersion.compareTo("5.1") < 0) {
                // Migrate VM's volumes to target datastore(s).
                if (!vmMo.changeDatastore(relocateSpec)) {
                    throw new Exception("Change datastore operation failed during storage migration");
                } else {
                    logger.debug(String.format("Successfully migrated storage of VM: %s to target datastore(s)", vmName));
                }
                // Migrate VM to target host.
                if (targetHyperHost != null) {
                    ManagedObjectReference morPool = targetHyperHost.getHyperHostOwnerResourcePool();
                    if (!vmMo.migrate(morPool, targetHyperHost.getMor())) {
                        throw new Exception("VM migration to target host failed during storage migration");
                    } else {
                        logger.debug(String.format("Successfully migrated VM: %s from host %s to %s", vmName , sourceHyperHost.getHyperHostName(), targetHyperHost.getHyperHostName()));
                    }
                }
            } else {
                // Add target host to relocate spec
                if (targetHyperHost != null) {
                    relocateSpec.setHost(targetHyperHost.getMor());
                    relocateSpec.setPool(targetHyperHost.getHyperHostOwnerResourcePool());
                }
                if (!vmMo.changeDatastore(relocateSpec)) {
                    throw new Exception("Change datastore operation failed during storage migration");
                } else {
                    String msg = String.format("Successfully migrated VM: %s with its storage to target datastore(s)", vmName);
                    if (targetHyperHost != null) {
                        msg = String.format("%s from host %s to %s", msg, sourceHyperHost.getHyperHostName(), targetHyperHost.getHyperHostName());
                    }
                    logger.debug(msg);
                }
            }

            // Consolidate VM disks.
            // In case of a linked clone VM, if VM's disks are not consolidated, further VM operations such as volume snapshot, VM snapshot etc. will result in DB inconsistencies.
            if (!vmMo.consolidateVmDisks()) {
                logger.warn("VM disk consolidation failed after storage migration. Yet proceeding with VM migration.");
            } else {
                logger.debug(String.format("Successfully consolidated disks of VM: %s", vmName));
            }

            if (MapUtils.isNotEmpty(volumeDeviceKey)) {
                // Update and return volume path and chain info for every disk because that could have changed after migration
                VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
                for (Pair<VolumeTO, StorageFilerTO> entry : volToFiler) {
                    final VolumeTO volume = entry.first();
                    final long volumeId = volume.getId();
                    VirtualDisk[] disks = vmMo.getAllDiskDevice();
                    for (VirtualDisk disk : disks) {
                        if (volumeDeviceKey.get(volumeId) == disk.getKey()) {
                            VolumeObjectTO newVol = new VolumeObjectTO();
                            newVol.setDataStoreUuid(entry.second().getUuid());
                            String newPath = vmMo.getVmdkFileBaseName(disk);
                            ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(targetHyperHost != null ? targetHyperHost : sourceHyperHost, entry.second().getUuid());
                            DatastoreMO dsMo = new DatastoreMO(getServiceContext(), morDs);
                            VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByBackingFileBaseName(newPath, dsMo.getName());
                            newVol.setId(volumeId);
                            newVol.setPath(newPath);
                            newVol.setChainInfo(_gson.toJson(diskInfo));
                            volumeToList.add(newVol);
                            break;
                        }
                    }
                }
            }
        } catch (Throwable e) {
            if (e instanceof RemoteException) {
                logger.warn("Encountered remote exception at vCenter, invalidating VMware session context");
                invalidateServiceContext();
            }
            throw e;
        } finally {
            // Cleanup datastores mounted on source host
            for (String mountedDatastore : mountedDatastoresAtSource) {
                logger.debug("Attempting to unmount datastore " + mountedDatastore + " at " + sourceHyperHost.getHyperHostName());
                try {
                    sourceHyperHost.unmountDatastore(mountedDatastore);
                } catch (Exception unmountEx) {
                    logger.warn("Failed to unmount datastore " + mountedDatastore + " at " + sourceHyperHost.getHyperHostName() + ". Seems the datastore is still being used by " + sourceHyperHost.getHyperHostName() +
                            ". Please unmount manually to cleanup.");
                }
                logger.debug("Successfully unmounted datastore " + mountedDatastore + " at " + sourceHyperHost.getHyperHostName());
            }
        }

        // Only when volToFiler is not empty a filled list of VolumeObjectTO is returned else it will be empty
        return volumeToList;
    }