add copy volume and create volume from snapshot

This commit is contained in:
Edison Su 2013-02-08 18:22:00 -08:00
parent 621a779446
commit 020be66f9d
9 changed files with 205 additions and 182 deletions

View File

@ -21,6 +21,7 @@ package com.cloud.storage;
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.MigrateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
@ -61,7 +62,7 @@ public interface VolumeApiService {
*/
Volume resizeVolume(ResizeVolumeCmd cmd);
Volume migrateVolume(Long volumeId, Long storagePoolId) throws ConcurrentOperationException;
Volume migrateVolume(MigrateVolumeCmd cmd) throws ConcurrentOperationException;
/**
* Uploads the volume to secondary storage

View File

@ -92,7 +92,7 @@ public class MigrateVolumeCmd extends BaseAsyncCmd {
public void execute(){
Volume result;
try {
result = _volumeService.migrateVolume(getVolumeId(), getStoragePoolId());
result = _volumeService.migrateVolume(this);
if (result != null) {
VolumeResponse response = _responseGenerator.createVolumeResponse(result);
response.setResponseName(getCommandName());

View File

@ -329,6 +329,59 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
return errMsg;
}
protected String copyVolumeBetweenPools(DataObject srcData, DataObject destData) {
VolumeInfo volume = (VolumeInfo)srcData;
VolumeInfo destVolume = (VolumeInfo)destData;
String secondaryStorageURL = this.templateMgr.getSecondaryStorageURL(volume
.getDataCenterId());
StoragePool srcPool = (StoragePool)this.dataStoreMgr.getDataStore(volume
.getPoolId(), DataStoreRole.Primary);
StoragePool destPool = (StoragePool)this.dataStoreMgr.getDataStore(destVolume.getPoolId(), DataStoreRole.Primary);
String value = this.configDao.getValue(Config.CopyVolumeWait.toString());
int _copyvolumewait = NumbersUtil.parseInt(value,
Integer.parseInt(Config.CopyVolumeWait.getDefaultValue()));
CopyVolumeCommand cvCmd = new CopyVolumeCommand(volume.getId(),
volume.getPath(), srcPool, secondaryStorageURL, true,
_copyvolumewait);
CopyVolumeAnswer cvAnswer;
try {
cvAnswer = (CopyVolumeAnswer) this.storagMgr.sendToPool(srcPool, cvCmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException(
"Failed to copy the volume from the source primary storage pool to secondary storage.",
e1);
}
if (cvAnswer == null || !cvAnswer.getResult()) {
throw new CloudRuntimeException(
"Failed to copy the volume from the source primary storage pool to secondary storage.");
}
String secondaryStorageVolumePath = cvAnswer.getVolumePath();
cvCmd = new CopyVolumeCommand(volume.getId(),
secondaryStorageVolumePath, destPool,
secondaryStorageURL, false, _copyvolumewait);
try {
cvAnswer = (CopyVolumeAnswer) this.storagMgr.sendToPool(destPool, cvCmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException(
"Failed to copy the volume from secondary storage to the destination primary storage pool.");
}
if (cvAnswer == null || !cvAnswer.getResult()) {
throw new CloudRuntimeException(
"Failed to copy the volume from secondary storage to the destination primary storage pool.");
}
VolumeVO destVol = this.volDao.findById(destVolume.getId());
destVol.setPath(cvAnswer.getVolumePath());
this.volDao.update(destVol.getId(), destVol);
return null;
}
@Override
public Void copyAsync(DataObject srcData, DataObject destData,
@ -336,7 +389,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
String errMsg = null;
try {
if (destData.getType() == DataObjectType.VOLUME
&& srcData.getType() == DataObjectType.VOLUME) {
&& srcData.getType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Image) {
errMsg = copyVolumeFromImage(srcData, destData);
} else if (destData.getType() == DataObjectType.TEMPLATE
&& srcData.getType() == DataObjectType.TEMPLATE) {
@ -353,6 +406,9 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
} else if (srcData.getType() == DataObjectType.TEMPLATE
&& destData.getType() == DataObjectType.VOLUME) {
errMsg = cloneVolume(srcData, destData);
} else if (destData.getType() == DataObjectType.VOLUME
&& srcData.getType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) {
errMsg = copyVolumeBetweenPools(srcData, destData);
}
} catch (Exception e) {
s_logger.debug("copy failed", e);

View File

@ -45,12 +45,14 @@ import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
import com.cloud.storage.Volume.Type;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.snapshot.SnapshotManager;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.VMInstanceDao;
@ -420,14 +422,105 @@ public class VolumeServiceImpl implements VolumeService {
@Override
public AsyncCallFuture<VolumeApiResult> createVolumeFromSnapshot(
VolumeInfo volume, DataStore store, SnapshotInfo snapshot) {
// TODO Auto-generated method stub
AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
VolumeApiResult result = new VolumeApiResult(volume);
return null;
}
protected VolumeVO duplicateVolumeOnAnotherStorage(Volume volume, StoragePool pool) {
Long lastPoolId = volume.getPoolId();
VolumeVO newVol = new VolumeVO(volume);
newVol.setPoolId(pool.getId());
newVol.setFolder(pool.getPath());
newVol.setPodId(pool.getPodId());
newVol.setPoolId(pool.getId());
newVol.setLastPoolId(lastPoolId);
newVol.setPodId(pool.getPodId());
return this.volDao.persist(newVol);
}
private class CopyVolumeContext<T> extends AsyncRpcConext<T> {
final VolumeInfo srcVolume;
final VolumeInfo destVolume;
final DataStore destStore;
final AsyncCallFuture<VolumeApiResult> future;
/**
* @param callback
*/
public CopyVolumeContext(AsyncCompletionCallback<T> callback, AsyncCallFuture<VolumeApiResult> future, VolumeInfo srcVolume, VolumeInfo destVolume,
DataStore destStore) {
super(callback);
this.srcVolume = srcVolume;
this.destVolume = destVolume;
this.destStore = destStore;
this.future = future;
}
}
@Override
public AsyncCallFuture<VolumeApiResult> copyVolume(VolumeInfo srcVolume,
DataStore destStore) {
// TODO Auto-generated method stub
AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
VolumeApiResult res = new VolumeApiResult(srcVolume);
try {
if (!this.snapshotMgr.canOperateOnVolume(srcVolume)) {
s_logger.debug(
"There are snapshots creating on this volume, can not move this volume");
res.setResult("There are snapshots creating on this volume, can not move this volume");
future.complete(res);
return future;
}
VolumeVO destVol = duplicateVolumeOnAnotherStorage(srcVolume, (StoragePool)destStore);
VolumeInfo destVolume = this.volFactory.getVolume(destVol.getId(), destStore);
destVolume.processEvent(Event.CreateOnlyRequested);
srcVolume.processEvent(Event.CopyingRequested);
CopyVolumeContext<VolumeApiResult> context = new CopyVolumeContext<VolumeApiResult>(null, future, srcVolume,
destVolume,
destStore);
AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().copyVolumeCallBack(null, null))
.setContext(context);
this.motionSrv.copyAsync(srcVolume, destVolume, caller);
} catch (Exception e) {
s_logger.debug("Failed to copy volume", e);
res.setResult(e.toString());
future.complete(res);
}
return future;
}
protected Void copyVolumeCallBack(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback, CopyVolumeContext<VolumeApiResult> context) {
VolumeInfo srcVolume = context.srcVolume;
VolumeInfo destVolume = context.destVolume;
CopyCommandResult result = callback.getResult();
AsyncCallFuture<VolumeApiResult> future = context.future;
VolumeApiResult res = new VolumeApiResult(destVolume);
try {
if (result.isFailed()) {
res.setResult(result.getResult());
destVolume.processEvent(Event.OperationFailed);
srcVolume.processEvent(Event.OperationFailed);
AsyncCallFuture<VolumeApiResult> destroyFuture = this.expungeVolumeAsync(destVolume);
destroyFuture.get();
future.complete(res);
return null;
}
srcVolume.processEvent(Event.OperationSuccessed);
destVolume.processEvent(Event.OperationSuccessed);
AsyncCallFuture<VolumeApiResult> destroyFuture = this.expungeVolumeAsync(srcVolume);
destroyFuture.get();
future.complete(res);
return null;
} catch (Exception e) {
s_logger.debug("Failed to process copy volume callback",e);
res.setResult(e.toString());
future.complete(res);
}
return null;
}

View File

@ -21,6 +21,7 @@ package com.cloud.storage;
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.MigrateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
@ -76,12 +77,11 @@ public interface VolumeManager extends VolumeApiService {
void cleanupVolumes(long vmId) throws ConcurrentOperationException;
Volume migrateVolume(Long volumeId, Long storagePoolId)
throws ConcurrentOperationException;
Volume migrateVolume(MigrateVolumeCmd cmd);
boolean StorageMigration(
boolean storageMigration(
VirtualMachineProfile<? extends VirtualMachine> vm,
StoragePool destPool) throws ConcurrentOperationException;
StoragePool destPool);
void prepareForMigration(
VirtualMachineProfile<? extends VirtualMachine> vm,

View File

@ -39,6 +39,7 @@ import org.apache.cloudstack.api.BaseCmd;
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.MigrateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
@ -351,11 +352,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
throw new CloudRuntimeException(
"Failed to find a storage pool with enough capacity to move the volume to.");
}
List<Volume> vols = new ArrayList<Volume>();
vols.add(volume);
migrateVolumes(vols, destPool);
return this.volFactory.getVolume(volume.getId());
Volume newVol = migrateVolume(volume, destPool);
return this.volFactory.getVolume(newVol.getId());
}
/*
@ -1012,10 +1011,15 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
@DB
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_RESIZE, eventDescription = "resizing volume", async = true)
public VolumeVO resizeVolume(ResizeVolumeCmd cmd) {
VolumeVO volume = _volsDao.findById(cmd.getEntityId());
Long newSize = null;
boolean shrinkOk = cmd.getShrinkOk();
boolean success = false;
VolumeVO volume = _volsDao.findById(cmd.getEntityId());
if (volume == null) {
throw new InvalidParameterValueException("No such volume");
}
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume
.getDiskOfferingId());
DiskOfferingVO newDiskOffering = null;
@ -1039,9 +1043,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
"Cloudstack currently only supports volumes marked as KVM or XenServer hypervisor for resize");
}
if (volume == null) {
throw new InvalidParameterValueException("No such volume");
}
if (volume.getState() != Volume.State.Ready) {
throw new InvalidParameterValueException(
@ -1995,8 +1996,10 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
@DB
@Override
public Volume migrateVolume(Long volumeId, Long storagePoolId)
throws ConcurrentOperationException {
public Volume migrateVolume(MigrateVolumeCmd cmd) {
Long volumeId = cmd.getVolumeId();
Long storagePoolId = cmd.getStoragePoolId();
VolumeVO vol = _volsDao.findById(volumeId);
if (vol == null) {
throw new InvalidParameterValueException(
@ -2025,171 +2028,36 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
"Migration of volume from local storage pool is not supported");
}
List<Volume> vols = new ArrayList<Volume>();
vols.add(vol);
migrateVolumes(vols, destPool);
return vol;
Volume newVol = migrateVolume(vol, destPool);
return newVol;
}
@DB
public boolean migrateVolumes(List<Volume> volumes, StoragePool destPool)
throws ConcurrentOperationException {
Transaction txn = Transaction.currentTxn();
txn.start();
boolean transitResult = false;
long checkPointTaskId = -1;
protected Volume migrateVolume(Volume volume, StoragePool destPool) {
VolumeInfo vol = this.volFactory.getVolume(volume.getId());
AsyncCallFuture<VolumeApiResult> future = this.volService.copyVolume(vol, (DataStore)destPool);
try {
List<Long> volIds = new ArrayList<Long>();
for (Volume volume : volumes) {
if (!_snapshotMgr.canOperateOnVolume((VolumeVO) volume)) {
throw new CloudRuntimeException(
"There are snapshots creating on this volume, can not move this volume");
}
try {
if (!stateTransitTo(volume, Volume.Event.MigrationRequested)) {
throw new ConcurrentOperationException(
"Failed to transit volume state");
}
} catch (NoTransitionException e) {
s_logger.debug("Failed to set state into migrate: "
+ e.toString());
throw new CloudRuntimeException(
"Failed to set state into migrate: " + e.toString());
}
volIds.add(volume.getId());
}
transitResult = true;
} finally {
if (!transitResult) {
txn.rollback();
} else {
txn.commit();
VolumeApiResult result = future.get();
if (result.isFailed()) {
s_logger.debug("migrate volume failed:" + result.getResult());
return null;
}
return result.getVolume();
} catch (InterruptedException e) {
s_logger.debug("migrate volume failed", e);
return null;
} catch (ExecutionException e) {
s_logger.debug("migrate volume failed", e);
return null;
}
// At this stage, nobody can modify volumes. Send the copyvolume command
List<Pair<StoragePool, DestroyCommand>> destroyCmds = new ArrayList<Pair<StoragePool, DestroyCommand>>();
List<CopyVolumeAnswer> answers = new ArrayList<CopyVolumeAnswer>();
try {
for (Volume volume : volumes) {
String secondaryStorageURL = this._tmpltMgr.getSecondaryStorageURL(volume
.getDataCenterId());
StoragePool srcPool = (StoragePool)this.dataStoreMgr.getDataStore(volume
.getPoolId(), DataStoreRole.Primary);
CopyVolumeCommand cvCmd = new CopyVolumeCommand(volume.getId(),
volume.getPath(), srcPool, secondaryStorageURL, true,
_copyvolumewait);
CopyVolumeAnswer cvAnswer;
try {
cvAnswer = (CopyVolumeAnswer) this.storageMgr.sendToPool(srcPool, cvCmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException(
"Failed to copy the volume from the source primary storage pool to secondary storage.",
e1);
}
if (cvAnswer == null || !cvAnswer.getResult()) {
throw new CloudRuntimeException(
"Failed to copy the volume from the source primary storage pool to secondary storage.");
}
String secondaryStorageVolumePath = cvAnswer.getVolumePath();
// Copy the volume from secondary storage to the destination
// storage
// pool
cvCmd = new CopyVolumeCommand(volume.getId(),
secondaryStorageVolumePath, destPool,
secondaryStorageURL, false, _copyvolumewait);
try {
cvAnswer = (CopyVolumeAnswer) this.storageMgr.sendToPool(destPool, cvCmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException(
"Failed to copy the volume from secondary storage to the destination primary storage pool.");
}
if (cvAnswer == null || !cvAnswer.getResult()) {
throw new CloudRuntimeException(
"Failed to copy the volume from secondary storage to the destination primary storage pool.");
}
answers.add(cvAnswer);
destroyCmds.add(new Pair<StoragePool, DestroyCommand>(
srcPool, new DestroyCommand(srcPool, volume, null)));
}
} finally {
if (answers.size() != volumes.size()) {
// this means one of copying volume failed
for (Volume volume : volumes) {
try {
stateTransitTo(volume, Volume.Event.OperationFailed);
} catch (NoTransitionException e) {
s_logger.debug("Failed to change volume state: "
+ e.toString());
}
}
} else {
// Need a transaction, make sure all the volumes get migrated to
// new storage pool
txn = Transaction.currentTxn();
txn.start();
transitResult = false;
try {
for (int i = 0; i < volumes.size(); i++) {
CopyVolumeAnswer answer = answers.get(i);
VolumeVO volume = (VolumeVO) volumes.get(i);
Long oldPoolId = volume.getPoolId();
volume.setPath(answer.getVolumePath());
volume.setFolder(destPool.getPath());
volume.setPodId(destPool.getPodId());
volume.setPoolId(destPool.getId());
volume.setLastPoolId(oldPoolId);
volume.setPodId(destPool.getPodId());
try {
stateTransitTo(volume,
Volume.Event.OperationSucceeded);
} catch (NoTransitionException e) {
s_logger.debug("Failed to change volume state: "
+ e.toString());
throw new CloudRuntimeException(
"Failed to change volume state: "
+ e.toString());
}
}
transitResult = true;
} finally {
if (!transitResult) {
txn.rollback();
} else {
txn.commit();
}
}
}
}
// all the volumes get migrated to new storage pool, need to delete the
// copy on old storage pool
for (Pair<StoragePool, DestroyCommand> cmd : destroyCmds) {
try {
Answer cvAnswer = this.storageMgr.sendToPool(cmd.first(), cmd.second());
} catch (StorageUnavailableException e) {
s_logger.debug("Unable to delete the old copy on storage pool: "
+ e.toString());
}
}
return true;
}
@Override
public boolean StorageMigration(
public boolean storageMigration(
VirtualMachineProfile<? extends VirtualMachine> vm,
StoragePool destPool) throws ConcurrentOperationException {
StoragePool destPool) {
List<VolumeVO> vols = _volsDao.findUsableVolumesForInstance(vm.getId());
List<Volume> volumesNeedToMigrate = new ArrayList<Volume>();
@ -2215,7 +2083,13 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
return true;
}
return migrateVolumes(volumesNeedToMigrate, destPool);
for (Volume vol : volumesNeedToMigrate) {
Volume result = migrateVolume(vol, destPool);
if (result == null) {
return false;
}
}
return true;
}
@Override
@ -2452,9 +2326,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
vol = task.volume;
} else if (task.type == VolumeTaskType.MIGRATE) {
pool = (StoragePool)dataStoreMgr.getDataStore(task.pool.getId(), DataStoreRole.Primary);
List<Volume> volumes = new ArrayList<Volume>();
volumes.add(task.volume);
migrateVolumes(volumes, pool);
migrateVolume(task.volume, pool);
vol = task.volume;
} else if (task.type == VolumeTaskType.RECREATE) {
Pair<VolumeVO, DataStore> result = recreateVolume(task.volume, vm, dest);

View File

@ -22,6 +22,7 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.host.HostVO;
import com.cloud.storage.SnapshotPolicyVO;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.utils.db.Filter;
@ -138,5 +139,5 @@ public interface SnapshotManager {
void deleteSnapshotsDirForVolume(String secondaryStoragePoolUrl, Long dcId, Long accountId, Long volumeId);
boolean canOperateOnVolume(VolumeVO volume);
boolean canOperateOnVolume(Volume volume);
}

View File

@ -1578,7 +1578,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
}
@Override
public boolean canOperateOnVolume(VolumeVO volume) {
public boolean canOperateOnVolume(Volume volume) {
List<SnapshotVO> snapshots = _snapshotDao.listByStatus(volume.getId(), Snapshot.State.Creating,
Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp);
if (snapshots.size() > 0) {

View File

@ -1226,7 +1226,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
boolean migrationResult = false;
try {
migrationResult = this.volumeMgr.StorageMigration(profile, destPool);
migrationResult = this.volumeMgr.storageMigration(profile, destPool);
if (migrationResult) {
//if the vm is migrated to different pod in basic mode, need to reallocate ip