mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Use data motion service for PrepareTemplateCmd to move template from
secondary storage to primary.
This commit is contained in:
parent
86913ab4d3
commit
0229c75b50
@ -24,6 +24,7 @@ import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
|||||||
|
|
||||||
|
|
||||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
|
import com.cloud.storage.StoragePool;
|
||||||
|
|
||||||
public interface TemplateService {
|
public interface TemplateService {
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ public interface TemplateService {
|
|||||||
AsyncCallFuture<TemplateApiResult> createTemplateFromSnapshotAsync(SnapshotInfo snapshot, TemplateInfo template, DataStore store);
|
AsyncCallFuture<TemplateApiResult> createTemplateFromSnapshotAsync(SnapshotInfo snapshot, TemplateInfo template, DataStore store);
|
||||||
AsyncCallFuture<TemplateApiResult> createTemplateFromVolumeAsync(VolumeInfo volume, TemplateInfo template, DataStore store);
|
AsyncCallFuture<TemplateApiResult> createTemplateFromVolumeAsync(VolumeInfo volume, TemplateInfo template, DataStore store);
|
||||||
AsyncCallFuture<TemplateApiResult> deleteTemplateAsync(TemplateInfo template);
|
AsyncCallFuture<TemplateApiResult> deleteTemplateAsync(TemplateInfo template);
|
||||||
AsyncCallFuture<TemplateApiResult> copyTemplate(TemplateInfo srcTemplate,
|
AsyncCallFuture<TemplateApiResult> copyTemplate(TemplateInfo srcTemplate, DataStore destStore);
|
||||||
DataStore destStore);
|
AsyncCallFuture<TemplateApiResult> prepareTemplateOnPrimary(TemplateInfo srcTemplate, StoragePool pool );
|
||||||
|
|
||||||
void handleSysTemplateDownload(HypervisorType hostHyper, Long dcId);
|
void handleSysTemplateDownload(HypervisorType hostHyper, Long dcId);
|
||||||
void handleTemplateSync(DataStore store);
|
void handleTemplateSync(DataStore store);
|
||||||
|
|||||||
@ -74,12 +74,14 @@ import com.cloud.exception.ResourceAllocationException;
|
|||||||
import com.cloud.host.HostVO;
|
import com.cloud.host.HostVO;
|
||||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
import com.cloud.storage.StoragePool;
|
import com.cloud.storage.StoragePool;
|
||||||
|
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;
|
||||||
import com.cloud.storage.VMTemplateZoneVO;
|
import com.cloud.storage.VMTemplateZoneVO;
|
||||||
import com.cloud.storage.VolumeVO;
|
import com.cloud.storage.VolumeVO;
|
||||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||||
import com.cloud.storage.dao.VMTemplateDao;
|
import com.cloud.storage.dao.VMTemplateDao;
|
||||||
|
import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||||
import com.cloud.storage.dao.VolumeDao;
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
import com.cloud.storage.download.DownloadMonitor;
|
import com.cloud.storage.download.DownloadMonitor;
|
||||||
@ -87,6 +89,7 @@ import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
|||||||
import com.cloud.user.AccountManager;
|
import com.cloud.user.AccountManager;
|
||||||
import com.cloud.user.ResourceLimitService;
|
import com.cloud.user.ResourceLimitService;
|
||||||
import com.cloud.utils.UriUtils;
|
import com.cloud.utils.UriUtils;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.utils.fsm.NoTransitionException;
|
import com.cloud.utils.fsm.NoTransitionException;
|
||||||
import com.cloud.vm.UserVmVO;
|
import com.cloud.vm.UserVmVO;
|
||||||
import com.cloud.vm.dao.UserVmDao;
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
@ -132,6 +135,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||||||
VolumeDao _volumeDao;
|
VolumeDao _volumeDao;
|
||||||
@Inject
|
@Inject
|
||||||
TemplateDataFactory _templateFactory;
|
TemplateDataFactory _templateFactory;
|
||||||
|
@Inject VMTemplatePoolDao _tmpltPoolDao;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -559,7 +563,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||||||
.setContext(context);
|
.setContext(context);
|
||||||
this._motionSrv.copyAsync(srcTemplate, destTemplate, caller);
|
this._motionSrv.copyAsync(srcTemplate, destTemplate, caller);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
s_logger.debug("Failed to copy volume", e);
|
s_logger.debug("Failed to copy template", e);
|
||||||
res.setResult(e.toString());
|
res.setResult(e.toString());
|
||||||
future.complete(res);
|
future.complete(res);
|
||||||
}
|
}
|
||||||
@ -597,6 +601,74 @@ public class TemplateServiceImpl implements TemplateService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Void prepareTemplateCallBack(AsyncCallbackDispatcher<TemplateServiceImpl, CopyCommandResult> callback,
|
||||||
|
CopyTemplateContext<TemplateApiResult> context) {
|
||||||
|
TemplateInfo srcTemplate = context.getSrcTemplate();
|
||||||
|
TemplateInfo destTemplate = context.getDestTemplate();
|
||||||
|
CopyCommandResult result = callback.getResult();
|
||||||
|
AsyncCallFuture<TemplateApiResult> future = context.getFuture();
|
||||||
|
TemplateApiResult res = new TemplateApiResult(destTemplate);
|
||||||
|
try {
|
||||||
|
if (result.isFailed()) {
|
||||||
|
res.setResult(result.getResult());
|
||||||
|
destTemplate.processEvent(Event.OperationFailed);
|
||||||
|
srcTemplate.processEvent(Event.OperationFailed);
|
||||||
|
// remove entry from template_spool_ref
|
||||||
|
VMTemplateStoragePoolVO destTmpltPool = _tmpltPoolDao.findByPoolTemplate(context.getDestStore().getId(), destTemplate.getId());
|
||||||
|
_vmTemplateStoreDao.remove(destTmpltPool.getId());
|
||||||
|
future.complete(res);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
srcTemplate.processEvent(Event.OperationSuccessed);
|
||||||
|
// update other information in template_spool_ref through templateObject event processing.
|
||||||
|
destTemplate.processEvent(Event.OperationSuccessed);
|
||||||
|
future.complete(res);
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
s_logger.debug("Failed to process prepare template callback", e);
|
||||||
|
res.setResult(e.toString());
|
||||||
|
future.complete(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncCallFuture<TemplateApiResult> prepareTemplateOnPrimary(TemplateInfo srcTemplate, StoragePool pool) {
|
||||||
|
AsyncCallFuture<TemplateApiResult> future = new AsyncCallFuture<TemplateApiResult>();
|
||||||
|
TemplateApiResult res = new TemplateApiResult(srcTemplate);
|
||||||
|
long poolId = pool.getId();
|
||||||
|
long templateId = srcTemplate.getId();
|
||||||
|
try{
|
||||||
|
// create one entry in template_spool_ref
|
||||||
|
VMTemplateStoragePoolVO templateStoragePoolRef = _tmpltPoolDao.findByPoolTemplate(poolId, templateId);
|
||||||
|
if (templateStoragePoolRef == null) {
|
||||||
|
templateStoragePoolRef = new VMTemplateStoragePoolVO(poolId, templateId);
|
||||||
|
templateStoragePoolRef = _tmpltPoolDao.persist(templateStoragePoolRef);
|
||||||
|
}
|
||||||
|
DataStore destStore = (DataStore)pool;
|
||||||
|
TemplateInfo destTemplate = this._templateFactory.getTemplate(templateStoragePoolRef.getTemplateId(), destStore);
|
||||||
|
destTemplate.processEvent(Event.CreateOnlyRequested);
|
||||||
|
srcTemplate.processEvent(Event.CopyingRequested);
|
||||||
|
|
||||||
|
CopyTemplateContext<TemplateApiResult> context = new CopyTemplateContext<TemplateApiResult>(null, future, srcTemplate,
|
||||||
|
destTemplate,
|
||||||
|
destStore
|
||||||
|
);
|
||||||
|
AsyncCallbackDispatcher<TemplateServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
|
||||||
|
caller.setCallback(caller.getTarget().prepareTemplateCallBack(null, null))
|
||||||
|
.setContext(context);
|
||||||
|
this._motionSrv.copyAsync(srcTemplate, destTemplate, caller);
|
||||||
|
} catch (Exception e) {
|
||||||
|
s_logger.debug("Failed to prepare template on storage pool", e);
|
||||||
|
res.setResult(e.toString());
|
||||||
|
future.complete(res);
|
||||||
|
}
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CopyTemplateContext<T> extends AsyncRpcConext<T> {
|
class CopyTemplateContext<T> extends AsyncRpcConext<T> {
|
||||||
final TemplateInfo srcTemplate;
|
final TemplateInfo srcTemplate;
|
||||||
final TemplateInfo destTemplate;
|
final TemplateInfo destTemplate;
|
||||||
|
|||||||
@ -666,7 +666,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
|||||||
long templateId = template.getId();
|
long templateId = template.getId();
|
||||||
long dcId = pool.getDataCenterId();
|
long dcId = pool.getDataCenterId();
|
||||||
VMTemplateStoragePoolVO templateStoragePoolRef = null;
|
VMTemplateStoragePoolVO templateStoragePoolRef = null;
|
||||||
VMTemplateHostVO templateHostRef = null;
|
TemplateDataStoreVO templateStoreRef = null;
|
||||||
long templateStoragePoolRefId;
|
long templateStoragePoolRefId;
|
||||||
String origUrl = null;
|
String origUrl = null;
|
||||||
|
|
||||||
@ -684,113 +684,71 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templateHostRef = findVmTemplateHost(templateId, pool);
|
templateStoreRef = findVmTemplateImageStore(templateId, pool);
|
||||||
|
|
||||||
if (templateHostRef == null || templateHostRef.getDownloadState() != Status.DOWNLOADED) {
|
if (templateStoreRef == null) {
|
||||||
String result = downloadTemplateFromSwiftToSecondaryStorage(dcId, templateId);
|
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
||||||
if (result != null) {
|
return null;
|
||||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
result = _s3Mgr.downloadTemplateFromS3ToSecondaryStorage(dcId,
|
|
||||||
templateId, _primaryStorageDownloadWait);
|
|
||||||
if (result != null) {
|
|
||||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
templateHostRef = findVmTemplateHost(templateId, pool);
|
|
||||||
if (templateHostRef == null || templateHostRef.getDownloadState() != Status.DOWNLOADED) {
|
|
||||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HostVO sh = _hostDao.findById(templateHostRef.getHostId());
|
List<StoragePoolHostVO> vos = _poolHostDao.listByHostStatus(poolId, com.cloud.host.Status.Up);
|
||||||
|
if (vos == null || vos.isEmpty()){
|
||||||
|
throw new CloudRuntimeException("Cannot download " + templateId + " to poolId " + poolId + " since there is no host in the Up state connected to this pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
HostVO sh = _hostDao.findById(templateStoreRef.getHostId());
|
||||||
origUrl = sh.getStorageUrl();
|
origUrl = sh.getStorageUrl();
|
||||||
if (origUrl == null) {
|
if (origUrl == null) {
|
||||||
throw new CloudRuntimeException("Unable to find the orig.url from host " + sh.toString());
|
throw new CloudRuntimeException("Unable to find the orig.url from host " + sh.toString());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (templateStoragePoolRef == null) {
|
if (templateStoragePoolRef == null) {
|
||||||
if (s_logger.isDebugEnabled()) {
|
if (s_logger.isDebugEnabled()) {
|
||||||
s_logger.debug("Downloading template " + templateId + " to pool " + poolId);
|
s_logger.debug("Downloading template " + templateId + " to pool " + poolId);
|
||||||
}
|
}
|
||||||
templateStoragePoolRef = new VMTemplateStoragePoolVO(poolId, templateId);
|
DataStore srcSecStore = this.dataStoreMgr.getDataStore(templateStoreRef.getDataStoreId(), DataStoreRole.Image);
|
||||||
|
TemplateInfo srcTemplate = this.tmplFactory.getTemplate(templateId, srcSecStore);
|
||||||
|
|
||||||
|
AsyncCallFuture<TemplateApiResult> future = this.tmpltSvr.prepareTemplateOnPrimary(srcTemplate, pool);
|
||||||
try {
|
try {
|
||||||
templateStoragePoolRef = _tmpltPoolDao.persist(templateStoragePoolRef);
|
TemplateApiResult result = future.get();
|
||||||
templateStoragePoolRefId = templateStoragePoolRef.getId();
|
if (result.isFailed()) {
|
||||||
|
s_logger.debug("prepare template failed:" + result.getResult());
|
||||||
} catch (Exception e) {
|
return null;
|
||||||
s_logger.debug("Assuming we're in a race condition: " + e.getMessage());
|
|
||||||
templateStoragePoolRef = _tmpltPoolDao.findByPoolTemplate(poolId, templateId);
|
|
||||||
if (templateStoragePoolRef == null) {
|
|
||||||
throw new CloudRuntimeException("Unable to persist a reference for pool " + poolId + " and template " + templateId);
|
|
||||||
}
|
}
|
||||||
templateStoragePoolRefId = templateStoragePoolRef.getId();
|
|
||||||
|
return _tmpltPoolDao.findByPoolTemplate(poolId, templateId);
|
||||||
}
|
}
|
||||||
} else {
|
catch (Exception ex) {
|
||||||
templateStoragePoolRefId = templateStoragePoolRef.getId();
|
s_logger.debug("failed to copy template from image store:" + srcSecStore.getName() + " to primary storage");
|
||||||
}
|
|
||||||
|
|
||||||
List<StoragePoolHostVO> vos = _poolHostDao.listByHostStatus(poolId, com.cloud.host.Status.Up);
|
|
||||||
if (vos == null || vos.isEmpty()){
|
|
||||||
throw new CloudRuntimeException("Cannot download " + templateId + " to poolId " + poolId + " since there is no host in the Up state connected to this pool");
|
|
||||||
}
|
|
||||||
|
|
||||||
templateStoragePoolRef = _tmpltPoolDao.acquireInLockTable(templateStoragePoolRefId, _storagePoolMaxWaitSeconds);
|
|
||||||
if (templateStoragePoolRef == null) {
|
|
||||||
throw new CloudRuntimeException("Unable to acquire lock on VMTemplateStoragePool: " + templateStoragePoolRefId);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (templateStoragePoolRef.getDownloadState() == Status.DOWNLOADED) {
|
|
||||||
return templateStoragePoolRef;
|
|
||||||
}
|
}
|
||||||
String url = origUrl + "/" + templateHostRef.getInstallPath();
|
|
||||||
PrimaryStorageDownloadCommand dcmd = new PrimaryStorageDownloadCommand(template.getUniqueName(), url, template.getFormat(),
|
|
||||||
template.getAccountId(), pool, _primaryStorageDownloadWait);
|
|
||||||
HostVO secondaryStorageHost = _hostDao.findById(templateHostRef.getHostId());
|
|
||||||
assert(secondaryStorageHost != null);
|
|
||||||
dcmd.setSecondaryStorageUrl(secondaryStorageHost.getStorageUrl());
|
|
||||||
|
|
||||||
|
|
||||||
for (int retry = 0; retry < 2; retry ++){
|
|
||||||
Collections.shuffle(vos); // Shuffling to pick a random host in the vm deployment retries
|
|
||||||
StoragePoolHostVO vo = vos.get(0);
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Downloading " + templateId + " via " + vo.getHostId());
|
|
||||||
}
|
|
||||||
dcmd.setLocalPath(vo.getLocalPath());
|
|
||||||
// set 120 min timeout for this command
|
|
||||||
|
|
||||||
PrimaryStorageDownloadAnswer answer = (PrimaryStorageDownloadAnswer)_agentMgr.easySend(
|
|
||||||
_hvGuruMgr.getGuruProcessedCommandTargetHost(vo.getHostId(), dcmd), dcmd);
|
|
||||||
if (answer != null && answer.getResult() ) {
|
|
||||||
templateStoragePoolRef.setDownloadPercent(100);
|
|
||||||
templateStoragePoolRef.setDownloadState(Status.DOWNLOADED);
|
|
||||||
templateStoragePoolRef.setLocalDownloadPath(answer.getInstallPath());
|
|
||||||
templateStoragePoolRef.setInstallPath(answer.getInstallPath());
|
|
||||||
templateStoragePoolRef.setTemplateSize(answer.getTemplateSize());
|
|
||||||
_tmpltPoolDao.update(templateStoragePoolRef.getId(), templateStoragePoolRef);
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Template " + templateId + " is downloaded via " + vo.getHostId());
|
|
||||||
}
|
|
||||||
return templateStoragePoolRef;
|
|
||||||
} else {
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Template " + templateId + " download to pool " + vo.getPoolId() + " failed due to " + (answer!=null?answer.getDetails():"return null")); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
_tmpltPoolDao.releaseFromLockTable(templateStoragePoolRefId);
|
|
||||||
}
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Template " + templateId + " is not found on and can not be downloaded to pool " + poolId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TemplateDataStoreVO findVmTemplateImageStore(long templateId,
|
||||||
|
StoragePool pool) {
|
||||||
|
long dcId = pool.getDataCenterId();
|
||||||
|
|
||||||
|
List<DataStore> secStores = this.dataStoreMgr.getImageStoresByScope(new ZoneScope(dcId));
|
||||||
|
|
||||||
|
if ( secStores == null ){
|
||||||
|
s_logger.error("No image store found to download template " + templateId + " primary storage!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (DataStore store : secStores ){
|
||||||
|
List<TemplateDataStoreVO> templStores = this._tmplStoreDao.listByTemplateStoreDownloadStatus(templateId, store.getId(), VMTemplateStorageResourceAssoc.Status.DOWNLOADED);
|
||||||
|
if (templStores != null && !templStores.isEmpty()) {
|
||||||
|
Collections.shuffle(templStores);
|
||||||
|
return templStores.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user