Move data store specific extract volume logic from VolumeManager to data

store driver.
This commit is contained in:
Min Chen 2013-06-04 13:54:14 -07:00
parent ef03d5a122
commit e92cd6d632
5 changed files with 79 additions and 182 deletions

View File

@ -97,5 +97,5 @@ public interface VolumeApiService {
* @throws PermissionDeniedException
*
*/
Long extractVolume(ExtractVolumeCmd cmd);
String extractVolume(ExtractVolumeCmd cmd);
}

View File

@ -126,33 +126,26 @@ public class ExtractVolumeCmd extends BaseAsyncCmd {
@Override
public void execute(){
// try {
UserContext.current().setEventDetails("Volume Id: "+getId());
Long uploadId = _volumeService.extractVolume(this);
if (uploadId != null){
Upload uploadInfo = _entityMgr.findById(Upload.class, uploadId);
ExtractResponse response = new ExtractResponse();
response.setResponseName(getCommandName());
response.setObjectName("volume");
Volume vol = _entityMgr.findById(Volume.class, id);
response.setId(vol.getUuid());
response.setName(vol.getName());
DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId);
response.setZoneId(zone.getUuid());
response.setZoneName(zone.getName());
response.setMode(mode);
response.setUploadId(uploadInfo.getUuid());
response.setState(uploadInfo.getUploadState().toString());
Account account = _entityMgr.findById(Account.class, getEntityOwnerId());
response.setAccountId(account.getUuid());
response.setUrl(uploadInfo.getUploadUrl());
this.setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to extract volume");
}
// } catch (URISyntaxException ex) {
// s_logger.info(ex);
// throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage());
// }
UserContext.current().setEventDetails("Volume Id: " + getId());
String uploadUrl = _volumeService.extractVolume(this);
if (uploadUrl != null) {
ExtractResponse response = new ExtractResponse();
response.setResponseName(getCommandName());
response.setObjectName("volume");
Volume vol = _entityMgr.findById(Volume.class, id);
response.setId(vol.getUuid());
response.setName(vol.getName());
DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId);
response.setZoneId(zone.getUuid());
response.setZoneName(zone.getName());
response.setMode(mode);
response.setState(Upload.Status.DOWNLOAD_URL_CREATED.toString());
Account account = _entityMgr.findById(Account.class, getEntityOwnerId());
response.setAccountId(account.getUuid());
response.setUrl(uploadUrl);
this.setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to extract volume");
}
}
}

View File

@ -1605,8 +1605,8 @@ public class ApiResponseHelper implements ResponseGenerator {
response.setZoneName(zone.getName());
}
response.setMode(mode);
response.setUrl(url);
response.setState(Upload.Status.DOWNLOAD_URL_CREATED.toString());
response.setUrl(url);
response.setState(Upload.Status.DOWNLOAD_URL_CREATED.toString());
Account account = ApiDBUtils.findAccountById(accountId);
response.setAccountId(account.getUuid());

View File

@ -18,13 +18,11 @@
*/
package com.cloud.storage;
import java.net.URISyntaxException;
import java.util.Map;
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
@ -35,8 +33,6 @@ import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.deploy.DeployDestination;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientStorageCapacityException;
import com.cloud.exception.InternalErrorException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.host.Host;
@ -54,6 +50,7 @@ public interface VolumeManager extends VolumeApiService {
Long destPoolClusterId, HypervisorType dataDiskHyperType)
throws ConcurrentOperationException;
@Override
VolumeVO uploadVolume(UploadVolumeCmd cmd)
throws ResourceAllocationException;
@ -65,28 +62,35 @@ public interface VolumeManager extends VolumeApiService {
String getVmNameOnVolume(Volume volume);
@Override
VolumeVO allocVolume(CreateVolumeCmd cmd)
throws ResourceAllocationException;
@Override
VolumeVO createVolume(CreateVolumeCmd cmd);
@Override
VolumeVO resizeVolume(ResizeVolumeCmd cmd)
throws ResourceAllocationException;
@Override
boolean deleteVolume(long volumeId, Account caller)
throws ConcurrentOperationException;
void destroyVolume(VolumeVO volume);
DiskProfile allocateRawVolume(Type type, String name, DiskOfferingVO offering, Long size, VMInstanceVO vm, Account owner);
@Override
Volume attachVolumeToVM(AttachVolumeCmd command);
@Override
Volume detachVolumeFromVM(DetachVolumeCmd cmmd);
void release(VirtualMachineProfile<? extends VMInstanceVO> profile);
void cleanupVolumes(long vmId) throws ConcurrentOperationException;
@Override
Volume migrateVolume(MigrateVolumeCmd cmd);
<T extends VMInstanceVO> void migrateVolumes(T vm, VirtualMachineTO vmTo, Host srcHost, Host destHost,

View File

@ -43,10 +43,8 @@ import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.cloud.storage.dao.*;
import org.apache.cloudstack.api.command.user.volume.*;
import org.apache.cloudstack.api.response.ExtractResponse;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
@ -72,23 +70,20 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
import org.apache.commons.lang.StringUtils;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.storage.CreateVolumeOVAAnswer;
import com.cloud.agent.api.storage.CreateVolumeOVACommand;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.S3TO;
import com.cloud.agent.api.to.SwiftTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.alert.AlertManager;
import com.cloud.api.ApiDBUtils;
import com.cloud.async.AsyncJobExecutor;
import com.cloud.async.AsyncJobManager;
import com.cloud.async.AsyncJobResult;
import com.cloud.async.AsyncJobVO;
import com.cloud.async.BaseAsyncJobExecutor;
import com.cloud.capacity.CapacityManager;
@ -119,7 +114,6 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.exception.UnsupportedServiceException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
@ -134,7 +128,6 @@ import com.cloud.server.ManagementServer;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Upload.Status;
import com.cloud.storage.Volume.Type;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.SnapshotDao;
@ -166,7 +159,6 @@ import com.cloud.uservm.UserVm;
import com.cloud.utils.EnumUtils;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.S3Utils;
import com.cloud.utils.UriUtils;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
@ -2615,11 +2607,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
@Override
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_EXTRACT, eventDescription = "extracting volume", async = true)
public Long extractVolume(ExtractVolumeCmd cmd) {
public String extractVolume(ExtractVolumeCmd cmd) {
Long volumeId = cmd.getId();
String url = cmd.getUrl();
Long zoneId = cmd.getZoneId();
AsyncJobVO job = null; // FIXME: cmd.getJob();
String mode = cmd.getMode();
Account account = UserContext.current().getCaller();
@ -2654,18 +2644,16 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
throw ex;
}
if (volume.getVolumeType() != Volume.Type.DATADISK) { // Datadisk dont
// have any
// template
// dependence.
if (volume.getVolumeType() != Volume.Type.DATADISK) {
// Datadisk dont have any template dependence.
VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId());
if (template != null) { // For ISO based volumes template = null and
// we allow extraction of all ISO based
// volumes
boolean isExtractable = template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM;
if (!isExtractable && account != null && account.getType() != Account.ACCOUNT_TYPE_ADMIN) { // Global
// admins are always allowed to extract
if (!isExtractable && account != null && account.getType() != Account.ACCOUNT_TYPE_ADMIN) {
// Global admins are always allowed to extract
PermissionDeniedException ex = new PermissionDeniedException("The volume with specified volumeId is not allowed to be extracted");
ex.addProxyObject(volume, volumeId, "volumeId");
throw ex;
@ -2680,139 +2668,51 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
extractMode = mode.equals(Upload.Mode.FTP_UPLOAD.toString()) ? Upload.Mode.FTP_UPLOAD : Upload.Mode.HTTP_DOWNLOAD;
}
long accountId = volume.getAccountId();
// Clean up code to remove all those previous uploadVO and uploadMonitor code. Previous code is trying to fake an async operation purely in
// db table with uploadVO and async_job entry, but internal implementation is actually synchronous.
StoragePool srcPool = (StoragePool) this.dataStoreMgr.getPrimaryDataStore(volume.getPoolId());
DataStore secStore = this.dataStoreMgr.getImageStore(zoneId);
ImageStoreEntity secStore = (ImageStoreEntity) this.dataStoreMgr.getImageStore(zoneId);
String secondaryStorageURL = secStore.getUri();
List<UploadVO> extractURLList = _uploadDao.listByTypeUploadStatus(volumeId, Upload.Type.VOLUME, UploadVO.Status.DOWNLOAD_URL_CREATED);
if (extractMode == Upload.Mode.HTTP_DOWNLOAD && extractURLList.size() > 0) {
return extractURLList.get(0).getId(); // If download url already
// Note: volss
// exists then return
} else {
UploadVO uploadJob = _uploadMonitor.createNewUploadEntry(secStore.getId(), volumeId, UploadVO.Status.COPY_IN_PROGRESS,
Upload.Type.VOLUME, url, extractMode);
s_logger.debug("Extract Mode - " + uploadJob.getMode());
uploadJob = _uploadDao.createForUpdate(uploadJob.getId());
// Update the async Job
ExtractResponse resultObj = new ExtractResponse(ApiDBUtils.findVolumeById(volumeId).getUuid(), volume.getName(), ApiDBUtils
.findAccountById(accountId).getUuid(), UploadVO.Status.COPY_IN_PROGRESS.toString(), uploadJob.getUuid());
resultObj.setResponseName(cmd.getCommandName());
AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor();
if (asyncExecutor != null) {
job = asyncExecutor.getJob();
_asyncMgr.updateAsyncJobAttachment(job.getId(), Upload.Type.VOLUME.toString(), volumeId);
_asyncMgr.updateAsyncJobStatus(job.getId(), AsyncJobResult.STATUS_IN_PROGRESS, resultObj);
}
//TODO: AncientDataMotionStrategy.copyObject should use different timeout parent for different objects
String value = this._configDao.getValue(Config.CopyVolumeWait.toString());
int copyvolumewait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CopyVolumeWait.getDefaultValue()));
// Copy volume from primary to secondary storage
VolumeInfo srcVol = this.volFactory.getVolume(volume.getId());
AsyncCallFuture<VolumeApiResult> cvAnswer = this.volService.copyVolume(srcVol, secStore);
// Check if you got a valid answer.
VolumeApiResult cvResult = null;
try {
cvResult = cvAnswer.get();
} catch (InterruptedException e1) {
s_logger.debug("failed copy volume", e1);
throw new CloudRuntimeException("Failed to copy volume" , e1);
} catch (ExecutionException e1) {
s_logger.debug("failed copy volume", e1);
throw new CloudRuntimeException("Failed to copy volume" , e1);
}
if (cvResult == null || cvResult.isFailed()) {
String errorString = "Failed to copy the volume from the source primary storage pool to secondary storage.";
// Update the async job.
resultObj.setResultString(errorString);
resultObj.setUploadStatus(UploadVO.Status.COPY_ERROR.toString());
if (asyncExecutor != null) {
_asyncMgr.completeAsyncJob(job.getId(), AsyncJobResult.STATUS_FAILED, 0, resultObj);
}
// Update the DB that volume couldn't be copied
uploadJob.setUploadState(UploadVO.Status.COPY_ERROR);
uploadJob.setErrorString(errorString);
uploadJob.setLastUpdated(new Date());
_uploadDao.update(uploadJob.getId(), uploadJob);
throw new CloudRuntimeException(errorString);
}
VolumeInfo vol = cvResult.getVolume();
String volumeLocalPath = vol.getPath();
String volumeName = StringUtils.substringBeforeLast(StringUtils.substringAfterLast(volumeLocalPath, "/"), ".");
// volss, handle the ova special case;
if (getFormatForPool(srcPool) == "ova") {
//TODO: need to handle this for S3 as secondary storage
CreateVolumeOVACommand cvOVACmd = new CreateVolumeOVACommand(secondaryStorageURL, volumeLocalPath, volumeName, srcPool,
copyvolumewait);
CreateVolumeOVAAnswer OVAanswer = null;
try {
cvOVACmd.setContextParam("hypervisor", HypervisorType.VMware.toString());
OVAanswer = (CreateVolumeOVAAnswer) storageMgr.sendToPool(srcPool, cvOVACmd); // Fang:
// for
// extract
// volume,
// create
// the
// ova
// file
// here;
} catch (StorageUnavailableException e) {
s_logger.debug("Storage unavailable");
}
}
// Update the DB that volume is copied and volumePath
uploadJob.setUploadState(UploadVO.Status.COPY_COMPLETE);
uploadJob.setLastUpdated(new Date());
uploadJob.setInstallPath(volumeLocalPath);
_uploadDao.update(uploadJob.getId(), uploadJob);
DataStoreTO volStore = secStore.getTO();
if (volStore instanceof SwiftTO) {
throw new UnsupportedServiceException("ExtractVolume is not yet supported for Swift image store provider");
}
if (volStore instanceof S3TO) {
// for S3, no need to do anything, just return volume url for
// extract template. but we need to set object acl as public_read to
// make the url accessible
S3TO s3 = (S3TO) volStore;
String key = vol.getPath();
try {
S3Utils.setObjectAcl(s3, s3.getBucketName(), key, CannedAccessControlList.PublicRead);
} catch (Exception ex) {
s_logger.error("Failed to set ACL on S3 object " + key + " to PUBLIC_READ", ex);
throw new CloudRuntimeException("Failed to set ACL on S3 object " + key + " to PUBLIC_READ");
}
// construct the url from s3
StringBuffer s3url = new StringBuffer();
s3url.append(s3.isHttps() ? "https://" : "http://");
s3url.append(s3.getEndPoint());
s3url.append("/");
s3url.append(s3.getBucketName());
s3url.append("/");
s3url.append(key);
UploadVO vo = _uploadDao.createForUpdate();
vo.setLastUpdated(new Date());
vo.setUploadUrl(s3url.toString());
vo.setUploadState(Status.DOWNLOAD_URL_CREATED);
_uploadDao.update(uploadJob.getId(), vo);
} else {
// create a URL.
_uploadMonitor.createVolumeDownloadURL(volumeId, volumeLocalPath, Upload.Type.VOLUME, zoneId, uploadJob.getId(), volume.getFormat());
}
return uploadJob.getId();
String value = this._configDao.getValue(Config.CopyVolumeWait.toString());
int copyvolumewait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CopyVolumeWait.getDefaultValue()));
// Copy volume from primary to secondary storage
VolumeInfo srcVol = this.volFactory.getVolume(volume.getId());
AsyncCallFuture<VolumeApiResult> cvAnswer = this.volService.copyVolume(srcVol, secStore);
// Check if you got a valid answer.
VolumeApiResult cvResult = null;
try {
cvResult = cvAnswer.get();
} catch (InterruptedException e1) {
s_logger.debug("failed copy volume", e1);
throw new CloudRuntimeException("Failed to copy volume", e1);
} catch (ExecutionException e1) {
s_logger.debug("failed copy volume", e1);
throw new CloudRuntimeException("Failed to copy volume", e1);
}
if (cvResult == null || cvResult.isFailed()) {
String errorString = "Failed to copy the volume from the source primary storage pool to secondary storage.";
throw new CloudRuntimeException(errorString);
}
VolumeInfo vol = cvResult.getVolume();
String volumeLocalPath = vol.getPath();
String volumeName = StringUtils.substringBeforeLast(StringUtils.substringAfterLast(volumeLocalPath, "/"), ".");
// volss, handle the ova special case;
if (getFormatForPool(srcPool) == "ova") {
// TODO: need to handle this for S3 as secondary storage
CreateVolumeOVACommand cvOVACmd = new CreateVolumeOVACommand(secondaryStorageURL, volumeLocalPath, volumeName, srcPool, copyvolumewait);
CreateVolumeOVAAnswer OVAanswer = null;
try {
cvOVACmd.setContextParam("hypervisor", HypervisorType.VMware.toString());
// for extract volume, create the ova file here;
OVAanswer = (CreateVolumeOVAAnswer) storageMgr.sendToPool(srcPool, cvOVACmd);
} catch (StorageUnavailableException e) {
s_logger.debug("Storage unavailable");
}
}
return secStore.createEntityExtractUrl(vol.getPath(), vol.getFormat());
}
private String getFormatForPool(StoragePool pool) {