engine-orchestration,vmware: hypervisor migration during start vm migration (#7444)

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2023-06-20 20:43:08 +05:30 committed by GitHub
parent 658daef715
commit 3748f32bc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 31 deletions

View File

@ -264,6 +264,8 @@ public interface VirtualMachineManager extends Manager {
Pair<Long, Long> findClusterAndHostIdForVm(long vmId); Pair<Long, Long> findClusterAndHostIdForVm(long vmId);
Pair<Long, Long> findClusterAndHostIdForVm(VirtualMachine vm, boolean skipCurrentHostForStartingVm);
/** /**
* Obtains statistics for a list of VMs; CPU and network utilization * Obtains statistics for a list of VMs; CPU and network utilization
* @param hostId ID of the host * @param hostId ID of the host

View File

@ -1054,6 +1054,26 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} }
} }
protected void checkAndAttemptMigrateVmAcrossCluster(final VMInstanceVO vm, final Long destinationClusterId, final Map<Volume, StoragePool> volumePoolMap) {
if (!HypervisorType.VMware.equals(vm.getHypervisorType()) || vm.getLastHostId() == null) {
return;
}
Host lastHost = _hostDao.findById(vm.getLastHostId());
if (destinationClusterId.equals(lastHost.getClusterId())) {
return;
}
if (volumePoolMap.values().stream().noneMatch(s -> destinationClusterId.equals(s.getClusterId()))) {
return;
}
Answer[] answer = attemptHypervisorMigration(vm, volumePoolMap, lastHost.getId());
if (answer == null) {
s_logger.warn("Hypervisor inter-cluster migration during VM start failed");
return;
}
// Other network related updates will be done using caller
markVolumesInPool(vm, answer);
}
@Override @Override
public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner) public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner)
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
@ -1227,6 +1247,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
resetVmNicsDeviceId(vm.getId()); resetVmNicsDeviceId(vm.getId());
_networkMgr.prepare(vmProfile, dest, ctx); _networkMgr.prepare(vmProfile, dest, ctx);
if (vm.getHypervisorType() != HypervisorType.BareMetal) { if (vm.getHypervisorType() != HypervisorType.BareMetal) {
checkAndAttemptMigrateVmAcrossCluster(vm, cluster_id, dest.getStorageForDisks());
volumeMgr.prepare(vmProfile, dest); volumeMgr.prepare(vmProfile, dest);
} }
@ -2355,7 +2376,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
try { try {
return _agentMgr.send(hostId, commandsContainer); return _agentMgr.send(hostId, commandsContainer);
} catch (AgentUnavailableException | OperationTimedoutException e) { } catch (AgentUnavailableException | OperationTimedoutException e) {
throw new CloudRuntimeException(String.format("Failed to migrate VM: %s", vm.getUuid()),e); s_logger.warn(String.format("Hypervisor migration failed for the VM: %s", vm), e);
} }
} }
return null; return null;
@ -5712,8 +5733,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
return new Pair<>(clusterId, hostId); return new Pair<>(clusterId, hostId);
} }
private Pair<Long, Long> findClusterAndHostIdForVm(VirtualMachine vm) { @Override
Long hostId = vm.getHostId(); public Pair<Long, Long> findClusterAndHostIdForVm(VirtualMachine vm, boolean skipCurrentHostForStartingVm) {
Long hostId = null;
if (!skipCurrentHostForStartingVm || !State.Starting.equals(vm.getState())) {
hostId = vm.getHostId();
}
Long clusterId = null; Long clusterId = null;
if(hostId == null) { if(hostId == null) {
hostId = vm.getLastHostId(); hostId = vm.getLastHostId();
@ -5731,6 +5756,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
return new Pair<>(clusterId, hostId); return new Pair<>(clusterId, hostId);
} }
private Pair<Long, Long> findClusterAndHostIdForVm(VirtualMachine vm) {
return findClusterAndHostIdForVm(vm, false);
}
@Override @Override
public Pair<Long, Long> findClusterAndHostIdForVm(long vmId) { public Pair<Long, Long> findClusterAndHostIdForVm(long vmId) {
VMInstanceVO vm = _vmDao.findById(vmId); VMInstanceVO vm = _vmDao.findById(vmId);

View File

@ -838,4 +838,34 @@ public class VirtualMachineManagerImplTest {
Mockito.when(templateZoneDao.findByZoneTemplate(dcId, templateId)).thenReturn(Mockito.mock(VMTemplateZoneVO.class)); Mockito.when(templateZoneDao.findByZoneTemplate(dcId, templateId)).thenReturn(Mockito.mock(VMTemplateZoneVO.class));
virtualMachineManagerImpl.checkIfTemplateNeededForCreatingVmVolumes(vm); virtualMachineManagerImpl.checkIfTemplateNeededForCreatingVmVolumes(vm);
} }
@Test
public void checkAndAttemptMigrateVmAcrossClusterNonValid() {
// Below scenarios shouldn't result in VM migration
VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
Mockito.when(vm.getHypervisorType()).thenReturn(HypervisorType.KVM);
virtualMachineManagerImpl.checkAndAttemptMigrateVmAcrossCluster(vm, 1L, new HashMap<>());
Mockito.when(vm.getHypervisorType()).thenReturn(HypervisorType.VMware);
Mockito.when(vm.getLastHostId()).thenReturn(null);
virtualMachineManagerImpl.checkAndAttemptMigrateVmAcrossCluster(vm, 1L, new HashMap<>());
Long destinationClusterId = 10L;
Mockito.when(vm.getLastHostId()).thenReturn(1L);
HostVO hostVO = Mockito.mock(HostVO.class);
Mockito.when(hostVO.getClusterId()).thenReturn(destinationClusterId);
Mockito.when(hostDaoMock.findById(1L)).thenReturn(hostVO);
virtualMachineManagerImpl.checkAndAttemptMigrateVmAcrossCluster(vm, destinationClusterId, new HashMap<>());
destinationClusterId = 20L;
Map<Volume, StoragePool> map = new HashMap<>();
StoragePool pool1 = Mockito.mock(StoragePool.class);
Mockito.when(pool1.getClusterId()).thenReturn(10L);
map.put(Mockito.mock(Volume.class), pool1);
StoragePool pool2 = Mockito.mock(StoragePool.class);
Mockito.when(pool2.getClusterId()).thenReturn(null);
map.put(Mockito.mock(Volume.class), pool2);
virtualMachineManagerImpl.checkAndAttemptMigrateVmAcrossCluster(vm, destinationClusterId, map);
}
} }

View File

@ -27,9 +27,6 @@ import java.util.UUID;
import javax.inject.Inject; import javax.inject.Inject;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.Backup;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
@ -112,7 +109,9 @@ import com.cloud.storage.DataStoreRole;
import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOSVO; import com.cloud.storage.GuestOSVO;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateStorageResourceAssoc;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
@ -120,6 +119,7 @@ import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO; import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDao;
@ -1149,10 +1149,10 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
@Override @Override
public List<Command> finalizeMigrate(VirtualMachine vm, Map<Volume, StoragePool> volumeToPool) { public List<Command> finalizeMigrate(VirtualMachine vm, Map<Volume, StoragePool> volumeToPool) {
List<Command> commands = new ArrayList<Command>(); List<Command> commands = new ArrayList<>();
// OfflineVmwareMigration: specialised migration command // OfflineVmwareMigration: specialised migration command
List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerTo = new ArrayList<Pair<VolumeTO, StorageFilerTO>>(); List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerTo = new ArrayList<>();
Long poolClusterId = null; Long poolClusterId = null;
StoragePool targetLocalPoolForVM = null; StoragePool targetLocalPoolForVM = null;
for (Map.Entry<Volume, StoragePool> entry : volumeToPool.entrySet()) { for (Map.Entry<Volume, StoragePool> entry : volumeToPool.entrySet()) {
@ -1166,10 +1166,10 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
if (volume.getVolumeType().equals(Volume.Type.ROOT) && pool.isLocal()) { if (volume.getVolumeType().equals(Volume.Type.ROOT) && pool.isLocal()) {
targetLocalPoolForVM = pool; targetLocalPoolForVM = pool;
} }
volumeToFilerTo.add(new Pair<VolumeTO, StorageFilerTO>(volumeTo, filerTo)); volumeToFilerTo.add(new Pair<>(volumeTo, filerTo));
} }
final Long destClusterId = poolClusterId; final Long destClusterId = poolClusterId;
final Long srcClusterId = vmManager.findClusterAndHostIdForVm(vm.getId()).first(); final Long srcClusterId = vmManager.findClusterAndHostIdForVm(vm, true).first();
final boolean isInterClusterMigration = isInterClusterMigration(destClusterId, srcClusterId); final boolean isInterClusterMigration = isInterClusterMigration(destClusterId, srcClusterId);
String targetHostGuid = getTargetHostGuid(targetLocalPoolForVM, destClusterId, isInterClusterMigration); String targetHostGuid = getTargetHostGuid(targetLocalPoolForVM, destClusterId, isInterClusterMigration);

View File

@ -16,20 +16,13 @@
// under the License. // under the License.
package com.cloud.hypervisor.guru; package com.cloud.hypervisor.guru;
import com.cloud.agent.api.Command; import static org.junit.Assert.assertEquals;
import com.cloud.agent.api.MigrateVmToPoolCommand;
import com.cloud.dc.ClusterDetailsDao; import java.util.ArrayList;
import com.cloud.host.HostVO; import java.util.HashMap;
import com.cloud.host.dao.HostDao; import java.util.List;
import com.cloud.storage.Storage.ProvisioningType; import java.util.Map;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.Pair;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.junit.Assert; import org.junit.Assert;
@ -46,12 +39,20 @@ import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.springframework.test.context.support.AnnotationConfigContextLoader;
import static org.junit.Assert.assertEquals; import com.cloud.agent.api.Command;
import com.cloud.agent.api.MigrateVmToPoolCommand;
import java.util.ArrayList; import com.cloud.dc.ClusterDetailsDao;
import java.util.HashMap; import com.cloud.host.HostVO;
import java.util.List; import com.cloud.host.dao.HostDao;
import java.util.Map; import com.cloud.storage.Storage.ProvisioningType;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.Pair;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({VMwareGuru.class}) @PrepareForTest({VMwareGuru.class})
@ -105,7 +106,7 @@ public class VMwareGuruTest {
Mockito.when(localStorage.isLocal()).thenReturn(true); Mockito.when(localStorage.isLocal()).thenReturn(true);
Pair<Long, Long> clusterAndHost = new Pair<>(1L, 1L); Pair<Long, Long> clusterAndHost = new Pair<>(1L, 1L);
Mockito.when(vmManager.findClusterAndHostIdForVm(1L)).thenReturn(clusterAndHost); Mockito.when(vmManager.findClusterAndHostIdForVm(vm, true)).thenReturn(clusterAndHost);
List<StoragePoolHostVO> storagePoolHostVOS = new ArrayList<>(); List<StoragePoolHostVO> storagePoolHostVOS = new ArrayList<>();
storagePoolHostVOS.add(storagePoolHostVO); storagePoolHostVOS.add(storagePoolHostVO);