diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 1e87657efce..f6abf571fbb 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -17,6 +17,8 @@ package com.cloud.vm; +import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS; + import java.net.URI; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -204,6 +206,7 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VMTemplateZoneVO; import com.cloud.storage.Volume; import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeApiService; @@ -213,6 +216,7 @@ import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; @@ -254,8 +258,6 @@ import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; import com.google.common.base.Strings; -import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS; - public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable { private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class); @@ -282,6 +284,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject private VMTemplateDao _templateDao; @Inject + private VMTemplateZoneDao templateZoneDao; + @Inject private ItWorkDao _workDao; @Inject private UserVmDao _userVmDao; @@ -1019,6 +1023,25 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } + protected void checkIfTemplateNeededForCreatingVmVolumes(VMInstanceVO vm) { + final List existingRootVolumes = _volsDao.findReadyRootVolumesByInstance(vm.getId()); + if (CollectionUtils.isNotEmpty(existingRootVolumes)) { + return; + } + final VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); + if (template == null) { + String msg = "Template for the VM instance can not be found, VM instance configuration needs to be updated"; + s_logger.error(String.format("%s. Template ID: %d seems to be removed", msg, vm.getTemplateId())); + throw new CloudRuntimeException(msg); + } + final VMTemplateZoneVO templateZoneVO = templateZoneDao.findByZoneTemplate(vm.getDataCenterId(), template.getId()); + if (templateZoneVO == null) { + String msg = "Template for the VM instance can not be found in the zone ID: %s, VM instance configuration needs to be updated"; + s_logger.error(String.format("%s. %s", msg, template)); + throw new CloudRuntimeException(msg); + } + } + @Override public void orchestrateStart(final String vmUuid, final Map params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { @@ -1083,6 +1106,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac boolean reuseVolume = true; final DataCenterDeployment originalPlan = plan; + checkIfTemplateNeededForCreatingVmVolumes(vm); + int retry = StartRetry.value(); while (retry-- != 0) { s_logger.debug("VM start attempt #" + (StartRetry.value() - retry)); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index c212228e256..83d1839b80c 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -37,21 +37,11 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.event.ActionEvent; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.EntityManager; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.db.TransactionCallback; -import com.cloud.utils.db.TransactionCallbackNoReturn; -import com.cloud.utils.db.TransactionStatus; -import com.cloud.utils.db.UUIDManager; import org.apache.cloudstack.api.ApiCommandResourceType; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.secret.dao.PassphraseDao; -import org.apache.cloudstack.secret.PassphraseVO; import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd; import org.apache.cloudstack.api.command.admin.volume.MigrateVolumeCmdByAdmin; import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; @@ -83,6 +73,8 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; +import org.apache.cloudstack.secret.PassphraseVO; +import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; @@ -107,6 +99,7 @@ import com.cloud.dc.Pod; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; import com.cloud.exception.ConcurrentOperationException; @@ -148,6 +141,13 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.db.UUIDManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; @@ -1606,6 +1606,12 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati future = volService.createVolumeAsync(volume, destPool); } else { + final VirtualMachineTemplate template = _entityMgr.findById(VirtualMachineTemplate.class, templateId); + if (template == null) { + s_logger.error(String.format("Failed to find template: %d for %s", templateId, volume)); + throw new CloudRuntimeException(String.format("Failed to find template for volume ID: %s", volume.getUuid())); + + } TemplateInfo templ = tmplFactory.getReadyTemplateOnImageStore(templateId, dest.getDataCenter().getId()); PrimaryDataStore primaryDataStore = (PrimaryDataStore)destPool; diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java index 548d00f67d3..f47854015d3 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -32,9 +32,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import com.cloud.agent.api.routing.NetworkElementCommand; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.host.Host; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -54,10 +51,13 @@ import com.cloud.agent.AgentManager; import com.cloud.agent.api.Command; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner; import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorGuru; @@ -67,10 +67,14 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.ScopeType; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VMTemplateZoneVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.StoragePoolHostDao; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine.State; @@ -124,6 +128,10 @@ public class VirtualMachineManagerImplTest { @Mock private DiskOfferingDao diskOfferingDaoMock; + @Mock + VMTemplateDao templateDao; + @Mock + VMTemplateZoneDao templateZoneDao; @Before public void setup() { @@ -727,4 +735,59 @@ public class VirtualMachineManagerImplTest { Mockito.doReturn(isOfferingUsingLocal).when(serviceOfferingMock).isUseLocalStorage(); virtualMachineManagerImpl.checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstanceMock, serviceOfferingMock); } + + @Test + public void checkIfTemplateNeededForCreatingVmVolumesExistingRootVolumes() { + long vmId = 1L; + VMInstanceVO vm = Mockito.mock(VMInstanceVO.class); + Mockito.when(vm.getId()).thenReturn(vmId); + Mockito.when(volumeDaoMock.findReadyRootVolumesByInstance(vmId)).thenReturn(List.of(Mockito.mock(VolumeVO.class))); + virtualMachineManagerImpl.checkIfTemplateNeededForCreatingVmVolumes(vm); + } + + @Test(expected = CloudRuntimeException.class) + public void checkIfTemplateNeededForCreatingVmVolumesMissingTemplate() { + long vmId = 1L; + long templateId = 1L; + VMInstanceVO vm = Mockito.mock(VMInstanceVO.class); + Mockito.when(vm.getId()).thenReturn(vmId); + Mockito.when(vm.getTemplateId()).thenReturn(templateId); + Mockito.when(volumeDaoMock.findReadyRootVolumesByInstance(vmId)).thenReturn(null); + Mockito.when(templateDao.findById(templateId)).thenReturn(null); + virtualMachineManagerImpl.checkIfTemplateNeededForCreatingVmVolumes(vm); + } + + @Test(expected = CloudRuntimeException.class) + public void checkIfTemplateNeededForCreatingVmVolumesMissingZoneTemplate() { + long vmId = 1L; + long templateId = 1L; + long dcId = 1L; + VMInstanceVO vm = Mockito.mock(VMInstanceVO.class); + Mockito.when(vm.getId()).thenReturn(vmId); + Mockito.when(vm.getTemplateId()).thenReturn(templateId); + Mockito.when(vm.getDataCenterId()).thenReturn(dcId); + Mockito.when(volumeDaoMock.findReadyRootVolumesByInstance(vmId)).thenReturn(null); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + Mockito.when(vm.getId()).thenReturn(templateId); + Mockito.when(templateDao.findById(templateId)).thenReturn(template); + Mockito.when(templateZoneDao.findByZoneTemplate(dcId, templateId)).thenReturn(null); + virtualMachineManagerImpl.checkIfTemplateNeededForCreatingVmVolumes(vm); + } + + @Test + public void checkIfTemplateNeededForCreatingVmVolumesTemplateAvailable() { + long vmId = 1L; + long templateId = 1L; + long dcId = 1L; + VMInstanceVO vm = Mockito.mock(VMInstanceVO.class); + Mockito.when(vm.getId()).thenReturn(vmId); + Mockito.when(vm.getTemplateId()).thenReturn(templateId); + Mockito.when(vm.getDataCenterId()).thenReturn(dcId); + Mockito.when(volumeDaoMock.findReadyRootVolumesByInstance(vmId)).thenReturn(new ArrayList<>()); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + Mockito.when(template.getId()).thenReturn(templateId); + Mockito.when(templateDao.findById(templateId)).thenReturn(template); + Mockito.when(templateZoneDao.findByZoneTemplate(dcId, templateId)).thenReturn(Mockito.mock(VMTemplateZoneVO.class)); + virtualMachineManagerImpl.checkIfTemplateNeededForCreatingVmVolumes(vm); + } }