This is an automated email from the ASF dual-hosted git repository. nvazquez pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit d2ab350a40cffe4eacf625e1648db7c12341f2cb Merge: 07cabbe 46a3032 Author: nicolas <[email protected]> AuthorDate: Wed Jun 16 15:38:18 2021 -0300 Merge branch '4.15' into main .../com/cloud/vm/VirtualMachineManagerImpl.java | 3 + .../hypervisor/kvm/resource/BridgeVifDriver.java | 4 +- .../hypervisor/kvm/resource/IvsVifDriver.java | 2 +- .../hypervisor/vmware/resource/VmwareResource.java | 7 +- .../java/com/cloud/storage/StorageManagerImpl.java | 2 +- .../com/cloud/storage/VolumeApiServiceImpl.java | 45 +- .../main/java/com/cloud/vm/UserVmManagerImpl.java | 2 +- .../cloud/storage/VolumeApiServiceImplTest.java | 56 +- .../test/java/com/cloud/vm/UserVmManagerTest.java | 5 +- tools/travis/xunit-reader.py | 2 +- ui/public/locales/el_GR.json | 3332 ++++++++++++++++++++ ui/public/locales/en.json | 4 +- ui/src/components/header/TranslationMenu.vue | 1 + ui/src/config/section/infra/primaryStorages.js | 8 + ui/src/views/image/RegisterOrUploadTemplate.vue | 6 +- 15 files changed, 3450 insertions(+), 29 deletions(-) diff --cc engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index e378842,3f435e9..0cb766e --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@@ -2395,20 -2297,19 +2395,23 @@@ public class VirtualMachineManagerImpl } List<VolumeVO> volumes = _volsDao.findUsableVolumesForInstance(vm.getId()); if(s_logger.isDebugEnabled()) { - String msg = String.format("found %d volumes for VM %s(uuid:%s, id:%d)", volumes.size(), vm.getInstanceName(), vm.getUuid(), vm.getId()); + String msg = String.format("Found %d volumes for VM %s(uuid:%s, id:%d)", results.size(), vm.getInstanceName(), vm.getUuid(), vm.getId()); s_logger.debug(msg); } - for (VolumeObjectTO result : relevantAnswer.getVolumeTos() ) { + for (VolumeObjectTO result : results ) { if(s_logger.isDebugEnabled()) { - s_logger.debug(String.format("updating volume (%d) with path '%s' on pool '%d'", result.getId(), result.getPath(), destPool.getId())); + s_logger.debug(String.format("Updating volume (%d) with path '%s' on pool '%s'", result.getId(), result.getPath(), result.getDataStoreUuid())); } VolumeVO volume = _volsDao.findById(result.getId()); + StoragePool pool = _storagePoolDao.findPoolByUUID(result.getDataStoreUuid()); + if (volume == null || pool == null) { + continue; + } volume.setPath(result.getPath()); - volume.setPoolId(destPool.getId()); + volume.setPoolId(pool.getId()); + if (result.getChainInfo() != null) { + volume.setChainInfo(result.getChainInfo()); + } _volsDao.update(volume.getId(), volume); } } diff --cc plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index a9a953a,f8fe2d6..f19f7cc --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@@ -7302,281 -7539,6 +7302,282 @@@ public class VmwareResource implements return new PrepareUnmanageVMInstanceAnswer(cmd, true, "OK"); } + /* + * Method to relocate a virtual machine. This migrates VM and its volumes to given host, datastores. + * It is used for MigrateVolumeCommand (detached volume case), MigrateVmToPoolCommand and MigrateVmWithStorageCommand. + */ + + 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 = "VM " + vmName + " cannot be migrated between different datacenter"; + 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 = "VM " + vmName + " does not exist on host: " + sourceHyperHost.getHyperHostName(); + s_logger.warn(msg); + // find VM through source host (VM is not at the target host yet) + vmMo = dcMo.findVm(vmName); + if (vmMo == null) { + msg = "VM " + vmName + " does not exist on datacenter: " + dcMo.getName(); + s_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; + String msg; + morDatastore = getTargetDatastoreMOReference(poolUuid, dsHost); + if (morDatastore == null) { + msg = "Unable to find the target datastore: " + poolUuid + " on host: " + dsHost.getHyperHostName() + + " to execute migration"; + s_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 (s_logger.isDebugEnabled()) { + s_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 = "Unable to find the target datastore: " + filerTo.getUuid() + " in datacenter: " + dcMo.getName() + " to execute migration"; + s_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 = 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.getType()); + } + // 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(); + Long secStoreId = secStoreUrlAndId.second(); + if (secStoreUrl == null) { + String msg = "secondary storage for dc " + _dcId + " is not ready yet?"; + throw new Exception(msg); + } + mgr.prepareSecondaryStorageStore(secStoreUrl, secStoreId); + ManagedObjectReference morSecDs = prepareSecondaryDatastoreOnSpecificHost(secStoreUrl, targetHyperHost); + if (morSecDs == null) { + String msg = "Failed to prepare secondary storage on host, secondary store url: " + secStoreUrl; + throw new Exception(msg); + } + } + + 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 { + s_logger.debug("Successfully migrated storage of VM " + vmName + " to target datastore(s)"); + } + // 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 { + s_logger.debug("Successfully migrated VM " + vmName + " from " + sourceHyperHost.getHyperHostName() + " to " + 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 { + s_logger.debug("Successfully migrated VM " + vmName + + (hostInTargetCluster != null ? " from " + sourceHyperHost.getHyperHostName() + " to " + targetHyperHost.getHyperHostName() + " and " : " with ") + + "its storage to target datastore(s)"); + } + } + + // 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()) { + s_logger.warn("VM disk consolidation failed after storage migration. Yet proceeding with VM migration."); + } else { + s_logger.debug("Successfully consolidated disks of VM " + 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); - String poolName = entry.second().getUuid().replace("-", ""); - VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByBackingFileBaseName(newPath, poolName); ++ ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(targetHyperHost, 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) { + s_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) { + s_logger.debug("Attempting to unmount datastore " + mountedDatastore + " at " + sourceHyperHost.getHyperHostName()); + try { + sourceHyperHost.unmountDatastore(mountedDatastore); + } catch (Exception unmountEx) { + s_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."); + } + s_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; + } + + private String getMountedDatastoreName(VmwareHypervisorHost sourceHyperHost, String sourceHostApiVersion, StorageFilerTO filerTo) throws Exception { + String mountedDatastoreName = null; + // If host version is below 5.1 then simultaneous change of VM's datastore and host is not supported. + // So since only the datastore will be changed first, ensure the target datastore is mounted on source host. + if (sourceHostApiVersion.compareTo("5.1") < 0) { + s_logger.debug(String.format("Host: %s version is %s, vMotion without shared storage cannot be done. Check source host has target datastore mounted or can be mounted", sourceHyperHost.getHyperHostName(), sourceHostApiVersion)); + ManagedObjectReference morVolumeDatastoreAtSource = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(sourceHyperHost, filerTo.getUuid()); + String volumeDatastoreName = filerTo.getUuid().replace("-", ""); + String volumeDatastoreHost = filerTo.getHost(); + String volumeDatastorePath = filerTo.getPath(); + int volumeDatastorePort = filerTo.getPort(); + + // If datastore is NFS and target datastore is not already mounted on source host then mount the datastore. + if (filerTo.getType().equals(StoragePoolType.NetworkFilesystem)) { + if (morVolumeDatastoreAtSource == null) { + morVolumeDatastoreAtSource = sourceHyperHost.mountDatastore(false, volumeDatastoreHost, volumeDatastorePort, volumeDatastorePath, volumeDatastoreName, false); + if (morVolumeDatastoreAtSource == null) { + throw new Exception("Unable to mount NFS datastore " + volumeDatastoreHost + ":/" + volumeDatastorePath + " on host: " + sourceHyperHost.getHyperHostName()); + } + mountedDatastoreName = volumeDatastoreName; + s_logger.debug("Mounted NFS datastore " + volumeDatastoreHost + ":/" + volumeDatastorePath + " on host: " + sourceHyperHost.getHyperHostName()); + } + } + + // If datastore is VMFS and target datastore is not mounted or accessible to source host then fail migration. + if (filerTo.getType().equals(StoragePoolType.VMFS)) { + if (morVolumeDatastoreAtSource == null) { + s_logger.warn("Host: " + sourceHyperHost.getHyperHostName() + " version is below 5.1, target VMFS datastore(s) need to be manually mounted on host for successful storage migration."); + throw new Exception("Target VMFS datastore: " + volumeDatastorePath + " is not mounted on host: " + sourceHyperHost.getHyperHostName()); + } + DatastoreMO dsAtSourceMo = new DatastoreMO(getServiceContext(), morVolumeDatastoreAtSource); + String srcHostValue = sourceHyperHost.getMor().getValue(); + if (!dsAtSourceMo.isAccessibleToHost(srcHostValue)) { + s_logger.warn("Host " + sourceHyperHost.getHyperHostName() + " version is below 5.1, target VMFS datastore(s) need to be accessible to host for a successful storage migration."); + throw new Exception("Target VMFS datastore: " + volumeDatastorePath + " is not accessible on host: " + sourceHyperHost.getHyperHostName()); + } + } + } + return mountedDatastoreName; + } + private Answer execute(ValidateVcenterDetailsCommand cmd) { if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource ValidateVcenterDetailsCommand " + _gson.toJson(cmd)); diff --cc server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index bdad40f,ccb91f1..0bd0861 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@@ -223,10 -218,10 +225,12 @@@ public class VolumeApiServiceImpl exten @Inject private SnapshotDao _snapshotDao; @Inject + private SnapshotDataStoreDao _snapshotDataStoreDao; + @Inject private ServiceOfferingDetailsDao _serviceOfferingDetailsDao; @Inject + private ServiceOfferingJoinDao serviceOfferingJoinDao; + @Inject private UserVmDao _userVmDao; @Inject private UserVmDetailsDao userVmDetailsDao;
