This is an automated email from the ASF dual-hosted git repository. dahn pushed a commit to branch 4.20 in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.20 by this push: new ac6b1b382cf Migrate public templates that have URLs on data migration across secondary storages (#10364) ac6b1b382cf is described below commit ac6b1b382cfd552a1d5311e8c2172c563e39a631 Author: Fabricio Duarte <fabricio.duarte...@gmail.com> AuthorDate: Tue Apr 15 08:48:45 2025 -0300 Migrate public templates that have URLs on data migration across secondary storages (#10364) Co-authored-by: Fabricio Duarte <fabricio.dua...@scclouds.com.br> --- .../storage/VMTemplateStorageResourceAssoc.java | 3 + .../engine/orchestration/DataMigrationUtility.java | 32 ++++- .../orchestration/DataMigrationUtilityTest.java | 88 +++++++++++++ .../storage/image/SecondaryStorageServiceImpl.java | 91 +++++++++++--- .../image/SecondaryStorageServiceImplTest.java | 138 +++++++++++++++++++++ .../cloud/api/query/dao/TemplateJoinDaoImpl.java | 7 -- .../storage/download/DownloadActiveState.java | 2 +- .../cloud/storage/download/DownloadListener.java | 1 + 8 files changed, 336 insertions(+), 26 deletions(-) diff --git a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java index f43d5331222..db702a61f2b 100644 --- a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java +++ b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java @@ -17,6 +17,7 @@ package com.cloud.storage; import java.util.Date; +import java.util.List; import org.apache.cloudstack.api.InternalIdentity; @@ -25,6 +26,8 @@ public interface VMTemplateStorageResourceAssoc extends InternalIdentity { UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED } + List<Status> PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS); + String getInstallPath(); long getTemplateId(); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java index c260f48dcf8..9609ba7751d 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java @@ -208,9 +208,7 @@ public class DataMigrationUtility { List<TemplateInfo> files = new LinkedList<>(); for (TemplateDataStoreVO template : templates) { VMTemplateVO templateVO = templateDao.findById(template.getTemplateId()); - if (template.getState() == ObjectInDataStoreStateMachine.State.Ready && templateVO != null && - (!templateVO.isPublicTemplate() || (templateVO.isPublicTemplate() && templateVO.getUrl() == null)) && - templateVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator && templateVO.getParentTemplateId() == null) { + if (shouldMigrateTemplate(template, templateVO)) { files.add(templateFactory.getTemplate(template.getTemplateId(), srcDataStore)); } } @@ -231,6 +229,34 @@ public class DataMigrationUtility { return getAllReadyTemplates(srcDataStore, childTemplates, templates); } + /** + * Returns whether a template should be migrated. A template should be migrated if: + * <ol> + * <li>its state is ready, and</li> + * <li>its hypervisor type is not simulator, and</li> + * <li>it is not a child template.</li> + * </ol> + */ + protected boolean shouldMigrateTemplate(TemplateDataStoreVO template, VMTemplateVO templateVO) { + if (template.getState() != State.Ready) { + logger.debug("Template [{}] should not be migrated as it is not ready.", template); + return false; + } + + if (templateVO.getHypervisorType() == Hypervisor.HypervisorType.Simulator) { + logger.debug("Template [{}] should not be migrated as its hypervisor type is simulator.", template); + return false; + } + + if (templateVO.getParentTemplateId() != null) { + logger.debug("Template [{}] should not be migrated as it has a parent template.", template); + return false; + } + + logger.debug("Template [{}] should be migrated.", template); + return true; + } + /** Returns parent snapshots and snapshots that do not have any children; snapshotChains comprises of the snapshot chain info * for each parent snapshot and the cumulative size of the chain - this is done to ensure that all the snapshots in a chain * are migrated to the same datastore diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtilityTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtilityTest.java new file mode 100644 index 00000000000..acd98e1cbff --- /dev/null +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtilityTest.java @@ -0,0 +1,88 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.engine.orchestration; + +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.VMTemplateVO; +import junit.framework.TestCase; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class DataMigrationUtilityTest extends TestCase { + + @Spy + private DataMigrationUtility dataMigrationUtility; + + @Mock + private VMTemplateVO templateVoMock; + + @Mock + private TemplateDataStoreVO templateDataStoreVoMock; + + private void prepareForShouldMigrateTemplateTests() { + Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready); + Mockito.when(templateVoMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + Mockito.when(templateVoMock.getParentTemplateId()).thenReturn(null); + } + + @Test + public void shouldMigrateTemplateTestReturnsFalseWhenTemplateIsNotReady() { + prepareForShouldMigrateTemplateTests(); + Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Migrating); + + boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock); + + Assert.assertFalse(result); + } + + @Test + public void shouldMigrateTemplateTestReturnsFalseWhenHypervisorTypeIsSimulator() { + prepareForShouldMigrateTemplateTests(); + Mockito.when(templateVoMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.Simulator); + + boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock); + + Assert.assertFalse(result); + } + + @Test + public void shouldMigrateTemplateTestReturnsFalseWhenTemplateHasParentTemplate() { + prepareForShouldMigrateTemplateTests(); + Mockito.when(templateVoMock.getParentTemplateId()).thenReturn(1L); + + boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock); + + Assert.assertFalse(result); + } + + @Test + public void shouldMigrateTemplateTestReturnsTrueWhenTemplateIsReadyAndDoesNotHaveParentTemplateAndHypervisorTypeIsNotSimulator() { + prepareForShouldMigrateTemplateTests(); + + boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock); + + Assert.assertTrue(result); + } +} diff --git a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/SecondaryStorageServiceImpl.java b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/SecondaryStorageServiceImpl.java index 730b003fcb0..502bbaf9e49 100644 --- a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/SecondaryStorageServiceImpl.java +++ b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/SecondaryStorageServiceImpl.java @@ -24,6 +24,9 @@ import java.util.concurrent.ExecutionException; import javax.inject.Inject; +import com.cloud.storage.VMTemplateStorageResourceAssoc; +import com.cloud.storage.download.DownloadListener; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; @@ -118,26 +121,21 @@ public class SecondaryStorageServiceImpl implements SecondaryStorageService { } } else if (srcDataObject instanceof TemplateInfo && templateChain != null && templateChain.containsKey(srcDataObject)) { for (TemplateInfo templateInfo : templateChain.get(srcDataObject).first()) { + if (templateIsOnDestination(templateInfo, destDatastore)) { + res.setResult("Template already exists on destination."); + res.setSuccess(true); + logger.debug("Deleting template {} from source data store [{}].", srcDataObject.getTO().toString(), + srcDataObject.getDataStore().getTO().toString()); + srcDataObject.getDataStore().delete(srcDataObject); + future.complete(res); + continue; + } destDataObject = destDatastore.create(templateInfo); templateInfo.processEvent(ObjectInDataStoreStateMachine.Event.MigrateDataRequested); destDataObject.processEvent(ObjectInDataStoreStateMachine.Event.MigrateDataRequested); migrateJob(future, templateInfo, destDataObject, destDatastore); } - } - else { - // Check if template in destination store, if yes, do not proceed - if (srcDataObject instanceof TemplateInfo) { - logger.debug("Checking if template present at destination"); - TemplateDataStoreVO templateStoreVO = templateStoreDao.findByStoreTemplate(destDatastore.getId(), srcDataObject.getId()); - if (templateStoreVO != null) { - String msg = "Template already exists in destination store"; - logger.debug(msg); - res.setResult(msg); - res.setSuccess(true); - future.complete(res); - return future; - } - } + } else { destDataObject = destDatastore.create(srcDataObject); srcDataObject.processEvent(ObjectInDataStoreStateMachine.Event.MigrateDataRequested); destDataObject.processEvent(ObjectInDataStoreStateMachine.Event.MigrateDataRequested); @@ -160,6 +158,69 @@ public class SecondaryStorageServiceImpl implements SecondaryStorageService { return future; } + /** + * Returns a boolean indicating whether a template is ready on the provided data store. If the template is being downloaded, + * waits until the download finishes. + * @param srcDataObject the template. + * @param destDatastore the data store. + */ + protected boolean templateIsOnDestination(DataObject srcDataObject, DataStore destDatastore) { + if (!(srcDataObject instanceof TemplateInfo)) { + return false; + } + + String templateAsString = srcDataObject.getTO().toString(); + String destDatastoreAsString = destDatastore.getTO().toString(); + TemplateDataStoreVO templateStoreVO; + + long timer = getTemplateDownloadTimeout(); + long msToSleep = 10000L; + int previousDownloadPercentage = -1; + + while (true) { + templateStoreVO = templateStoreDao.findByStoreTemplate(destDatastore.getId(), srcDataObject.getId()); + if (templateStoreVO == null) { + logger.debug("{} is not present at destination [{}].", templateAsString, destDatastoreAsString); + return false; + } + VMTemplateStorageResourceAssoc.Status downloadState = templateStoreVO.getDownloadState(); + if (downloadState == null || !VMTemplateStorageResourceAssoc.PENDING_DOWNLOAD_STATES.contains(downloadState)) { + break; + } + if (previousDownloadPercentage == templateStoreVO.getDownloadPercent()) { + timer -= msToSleep; + } else { + timer = getTemplateDownloadTimeout(); + } + if (timer <= 0) { + throw new CloudRuntimeException(String.format("Timeout while waiting for %s to be downloaded to image store [%s]. " + + "The download percentage has not changed for %d milliseconds.", templateAsString, destDatastoreAsString, getTemplateDownloadTimeout())); + } + waitForTemplateDownload(msToSleep, templateAsString, destDatastoreAsString); + } + + if (templateStoreVO.getState() == ObjectInDataStoreStateMachine.State.Ready) { + logger.debug("{} already exists on destination [{}].", templateAsString, destDatastoreAsString); + return true; + } + return false; + } + + protected long getTemplateDownloadTimeout() { + return DownloadListener.DOWNLOAD_TIMEOUT; + } + + protected void waitForTemplateDownload(long msToSleep, String templateAsString, String destDatastoreAsString) { + logger.debug("{} is being downloaded to destination [{}]; we will verify in {} milliseconds if the download has finished.", + templateAsString, destDatastoreAsString, msToSleep); + try { + Thread.sleep(msToSleep); + } catch (InterruptedException e) { + logger.warn("[ignored] interrupted while waiting for template {} download to finish before trying to migrate it to data store [{}].", + templateAsString, destDatastoreAsString); + } + } + protected void migrateJob(AsyncCallFuture<DataObjectResult> future, DataObject srcDataObject, DataObject destDataObject, DataStore destDatastore) throws ExecutionException, InterruptedException { MigrateDataContext<DataObjectResult> context = new MigrateDataContext<DataObjectResult>(null, future, srcDataObject, destDataObject, destDatastore); AsyncCallbackDispatcher<SecondaryStorageServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this); diff --git a/engine/storage/image/src/test/java/org/apache/cloudstack/storage/image/SecondaryStorageServiceImplTest.java b/engine/storage/image/src/test/java/org/apache/cloudstack/storage/image/SecondaryStorageServiceImplTest.java new file mode 100644 index 00000000000..740194ea253 --- /dev/null +++ b/engine/storage/image/src/test/java/org/apache/cloudstack/storage/image/SecondaryStorageServiceImplTest.java @@ -0,0 +1,138 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.storage.image; + +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.storage.VMTemplateStorageResourceAssoc; +import com.cloud.utils.exception.CloudRuntimeException; +import junit.framework.TestCase; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SecondaryStorageServiceImplTest extends TestCase { + + @Spy + @InjectMocks + private SecondaryStorageServiceImpl secondaryStorageService; + + @Mock + TemplateDataStoreDao templateDataStoreDaoMock; + + @Mock + TemplateDataStoreVO templateDataStoreVoMock; + + @Mock + TemplateInfo templateInfoMock; + + @Mock + TemplateObjectTO templateObjectToMock; + + @Mock + DataStore dataStoreMock; + + @Mock + DataStoreTO dataStoreToMock; + + private void prepareForTemplateIsOnDestinationTests() { + long dataStoreId = 1; + long templateId = 2; + + Mockito.when(dataStoreMock.getId()).thenReturn(dataStoreId); + Mockito.when(dataStoreMock.getTO()).thenReturn(dataStoreToMock); + Mockito.when(templateInfoMock.getId()).thenReturn(templateId); + Mockito.when(templateInfoMock.getTO()).thenReturn(templateObjectToMock); + Mockito.doReturn(templateDataStoreVoMock).when(templateDataStoreDaoMock).findByStoreTemplate(dataStoreId, templateId); + Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready); + } + + @Test + public void templateIsOnDestinationTestReturnsFalseWhenTemplateStoreRefDoesNotExist() { + prepareForTemplateIsOnDestinationTests(); + Mockito.doReturn(null).when(templateDataStoreDaoMock).findByStoreTemplate(Mockito.anyLong(), Mockito.anyLong()); + + boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock); + + Assert.assertFalse(result); + } + + @Test + public void templateIsOnDestinationTestReturnsTrueWhenTemplateIsReady() { + prepareForTemplateIsOnDestinationTests(); + + boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock); + + Assert.assertTrue(result); + } + + @Test + public void templateIsOnDestinationTestReturnsFalseWhenTemplateIsNotReady() { + prepareForTemplateIsOnDestinationTests(); + Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Creating); + + boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock); + + Assert.assertFalse(result); + } + + @Test + public void templateIsOnDestinationTestReturnsTrueIfTemplateIsDownloadedSuccessfully() { + prepareForTemplateIsOnDestinationTests(); + Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS); + Mockito.doAnswer(I -> Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOADED)).when(secondaryStorageService).waitForTemplateDownload(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString()); + + boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock); + + Assert.assertTrue(result); + } + + @Test + public void templateIsOnDestinationTestReturnsFalseIfTemplateIsNotDownloadedSuccessfully() { + prepareForTemplateIsOnDestinationTests(); + Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS); + Mockito.doAnswer(I -> { + Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR); + Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Failed); + return "mocked download fail"; + }).when(secondaryStorageService).waitForTemplateDownload(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString()); + + boolean result = secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock); + + Assert.assertFalse(result); + } + + @Test(expected = CloudRuntimeException.class) + public void templateIsOnDestinationTestThrowsExceptionIfDownloadTimesOut() { + prepareForTemplateIsOnDestinationTests(); + Mockito.when(templateDataStoreVoMock.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS); + Mockito.doReturn(0L).when(secondaryStorageService).getTemplateDownloadTimeout(); + + secondaryStorageService.templateIsOnDestination(templateInfoMock, dataStoreMock); + } +} diff --git a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java index 0bdf5040c82..ac24f14b781 100644 --- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java @@ -151,11 +151,6 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa activeTmpltSearch.and("store_id", activeTmpltSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); activeTmpltSearch.and("type", activeTmpltSearch.entity().getTemplateType(), SearchCriteria.Op.EQ); activeTmpltSearch.and("templateState", activeTmpltSearch.entity().getTemplateState(), SearchCriteria.Op.EQ); - activeTmpltSearch.and().op("public", activeTmpltSearch.entity().isPublicTemplate(), SearchCriteria.Op.EQ); - activeTmpltSearch.or().op("publicNoUrl", activeTmpltSearch.entity().isPublicTemplate(), SearchCriteria.Op.EQ); - activeTmpltSearch.and("url", activeTmpltSearch.entity().getUrl(), SearchCriteria.Op.NULL); - activeTmpltSearch.cp(); - activeTmpltSearch.cp(); activeTmpltSearch.done(); publicTmpltSearch = createSearchBuilder(); @@ -687,8 +682,6 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa sc.setParameters("store_id", storeId); sc.setParameters("type", TemplateType.USER); sc.setParameters("templateState", VirtualMachineTemplate.State.Active); - sc.setParameters("public", Boolean.FALSE); - sc.setParameters("publicNoUrl",Boolean.TRUE); return searchIncludingRemoved(sc, null, null, false); } diff --git a/server/src/main/java/com/cloud/storage/download/DownloadActiveState.java b/server/src/main/java/com/cloud/storage/download/DownloadActiveState.java index 989e0b55f94..889ffbc0b1c 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadActiveState.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadActiveState.java @@ -76,7 +76,7 @@ public abstract class DownloadActiveState extends DownloadState { getDownloadListener().log("handleTimeout, updateMs=" + updateMs + ", curr state= " + getName(), Level.TRACE); } String newState = getName(); - if (updateMs > 5 * DownloadListener.STATUS_POLL_INTERVAL) { + if (updateMs > DownloadListener.DOWNLOAD_TIMEOUT) { newState = Status.DOWNLOAD_ERROR.toString(); getDownloadListener().log("timeout: transitioning to download error state, currstate=" + getName(), Level.DEBUG); } else if (updateMs > 3 * DownloadListener.STATUS_POLL_INTERVAL) { diff --git a/server/src/main/java/com/cloud/storage/download/DownloadListener.java b/server/src/main/java/com/cloud/storage/download/DownloadListener.java index e06a31d96b5..42b0e394db4 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadListener.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadListener.java @@ -100,6 +100,7 @@ public class DownloadListener implements Listener { protected Logger logger = LogManager.getLogger(getClass()); public static final int SMALL_DELAY = 100; public static final long STATUS_POLL_INTERVAL = 10000L; + public static final long DOWNLOAD_TIMEOUT = 5 * STATUS_POLL_INTERVAL; public static final String DOWNLOADED = Status.DOWNLOADED.toString(); public static final String NOT_DOWNLOADED = Status.NOT_DOWNLOADED.toString();