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 774950855c4..1b3d914b27a 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -215,6 +215,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; @@ -225,6 +226,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; @@ -291,6 +293,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject private VMTemplateDao _templateDao; @Inject + private VMTemplateZoneDao templateZoneDao; + @Inject private ItWorkDao _workDao; @Inject private UserVmDao _userVmDao; @@ -1031,6 +1035,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 { @@ -1094,6 +1117,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 ad7da2ceace..49c822d25ad 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,17 +37,12 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.event.ActionEvent; -import com.cloud.storage.StorageUtil; - import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; -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; @@ -76,6 +71,9 @@ 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.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; @@ -87,6 +85,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; +import org.jetbrains.annotations.Nullable; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DatadiskTO; @@ -100,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; @@ -121,6 +121,7 @@ import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; +import com.cloud.storage.StorageUtil; import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.Volume; import com.cloud.storage.Volume.Type; @@ -140,11 +141,11 @@ import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.EntityManager; -import com.cloud.utils.db.UUIDManager; 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; @@ -168,9 +169,6 @@ import com.cloud.vm.dao.UserVmCloneSettingDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; -import org.apache.cloudstack.snapshot.SnapshotHelper; -import org.jetbrains.annotations.Nullable; - public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrationService, Configurable { public enum UserVmCloneType { @@ -1710,6 +1708,11 @@ 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 dd6f51d7577..742bb3dda89 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -71,10 +71,14 @@ import com.cloud.storage.ScopeType; import com.cloud.storage.StorageManager; 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; @@ -134,6 +138,10 @@ public class VirtualMachineManagerImplTest { @Mock private DiskOfferingDao diskOfferingDaoMock; + @Mock + VMTemplateDao templateDao; + @Mock + VMTemplateZoneDao templateZoneDao; @Mock private HostDao hostDaoMock; @@ -776,4 +784,58 @@ public class VirtualMachineManagerImplTest { Mockito.doReturn(isOfferingUsingLocal).when(diskOfferingMock).isUseLocalStorage(); virtualMachineManagerImpl.checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstanceMock, diskOfferingMock); } + + @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); + 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); + } }