mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
add copy volume and create volume from snapshot
This commit is contained in:
parent
621a779446
commit
020be66f9d
@ -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.AttachVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
|
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.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.ResizeVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
|
||||||
|
|
||||||
@ -61,7 +62,7 @@ public interface VolumeApiService {
|
|||||||
*/
|
*/
|
||||||
Volume resizeVolume(ResizeVolumeCmd cmd);
|
Volume resizeVolume(ResizeVolumeCmd cmd);
|
||||||
|
|
||||||
Volume migrateVolume(Long volumeId, Long storagePoolId) throws ConcurrentOperationException;
|
Volume migrateVolume(MigrateVolumeCmd cmd) throws ConcurrentOperationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads the volume to secondary storage
|
* Uploads the volume to secondary storage
|
||||||
|
|||||||
@ -92,7 +92,7 @@ public class MigrateVolumeCmd extends BaseAsyncCmd {
|
|||||||
public void execute(){
|
public void execute(){
|
||||||
Volume result;
|
Volume result;
|
||||||
try {
|
try {
|
||||||
result = _volumeService.migrateVolume(getVolumeId(), getStoragePoolId());
|
result = _volumeService.migrateVolume(this);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
VolumeResponse response = _responseGenerator.createVolumeResponse(result);
|
VolumeResponse response = _responseGenerator.createVolumeResponse(result);
|
||||||
response.setResponseName(getCommandName());
|
response.setResponseName(getCommandName());
|
||||||
|
|||||||
@ -330,13 +330,66 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
|
|||||||
return errMsg;
|
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
|
@Override
|
||||||
public Void copyAsync(DataObject srcData, DataObject destData,
|
public Void copyAsync(DataObject srcData, DataObject destData,
|
||||||
AsyncCompletionCallback<CopyCommandResult> callback) {
|
AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||||
String errMsg = null;
|
String errMsg = null;
|
||||||
try {
|
try {
|
||||||
if (destData.getType() == DataObjectType.VOLUME
|
if (destData.getType() == DataObjectType.VOLUME
|
||||||
&& srcData.getType() == DataObjectType.VOLUME) {
|
&& srcData.getType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Image) {
|
||||||
errMsg = copyVolumeFromImage(srcData, destData);
|
errMsg = copyVolumeFromImage(srcData, destData);
|
||||||
} else if (destData.getType() == DataObjectType.TEMPLATE
|
} else if (destData.getType() == DataObjectType.TEMPLATE
|
||||||
&& srcData.getType() == DataObjectType.TEMPLATE) {
|
&& srcData.getType() == DataObjectType.TEMPLATE) {
|
||||||
@ -353,6 +406,9 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
|
|||||||
} else if (srcData.getType() == DataObjectType.TEMPLATE
|
} else if (srcData.getType() == DataObjectType.TEMPLATE
|
||||||
&& destData.getType() == DataObjectType.VOLUME) {
|
&& destData.getType() == DataObjectType.VOLUME) {
|
||||||
errMsg = cloneVolume(srcData, destData);
|
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) {
|
} catch (Exception e) {
|
||||||
s_logger.debug("copy failed", e);
|
s_logger.debug("copy failed", e);
|
||||||
|
|||||||
@ -45,12 +45,14 @@ import org.apache.log4j.Logger;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.cloud.exception.ConcurrentOperationException;
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
|
import com.cloud.storage.StoragePool;
|
||||||
import com.cloud.storage.Volume;
|
import com.cloud.storage.Volume;
|
||||||
import com.cloud.storage.Volume.Type;
|
import com.cloud.storage.Volume.Type;
|
||||||
import com.cloud.storage.VolumeVO;
|
import com.cloud.storage.VolumeVO;
|
||||||
import com.cloud.storage.dao.VolumeDao;
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
import com.cloud.storage.snapshot.SnapshotManager;
|
import com.cloud.storage.snapshot.SnapshotManager;
|
||||||
import com.cloud.utils.db.DB;
|
import com.cloud.utils.db.DB;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
import com.cloud.vm.dao.VMInstanceDao;
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
|
|
||||||
@ -420,14 +422,105 @@ public class VolumeServiceImpl implements VolumeService {
|
|||||||
@Override
|
@Override
|
||||||
public AsyncCallFuture<VolumeApiResult> createVolumeFromSnapshot(
|
public AsyncCallFuture<VolumeApiResult> createVolumeFromSnapshot(
|
||||||
VolumeInfo volume, DataStore store, SnapshotInfo snapshot) {
|
VolumeInfo volume, DataStore store, SnapshotInfo snapshot) {
|
||||||
// TODO Auto-generated method stub
|
AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
|
||||||
|
VolumeApiResult result = new VolumeApiResult(volume);
|
||||||
return null;
|
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
|
@Override
|
||||||
public AsyncCallFuture<VolumeApiResult> copyVolume(VolumeInfo srcVolume,
|
public AsyncCallFuture<VolumeApiResult> copyVolume(VolumeInfo srcVolume,
|
||||||
DataStore destStore) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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.AttachVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
|
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.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.ResizeVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||||
@ -76,12 +77,11 @@ public interface VolumeManager extends VolumeApiService {
|
|||||||
|
|
||||||
void cleanupVolumes(long vmId) throws ConcurrentOperationException;
|
void cleanupVolumes(long vmId) throws ConcurrentOperationException;
|
||||||
|
|
||||||
Volume migrateVolume(Long volumeId, Long storagePoolId)
|
Volume migrateVolume(MigrateVolumeCmd cmd);
|
||||||
throws ConcurrentOperationException;
|
|
||||||
|
|
||||||
boolean StorageMigration(
|
boolean storageMigration(
|
||||||
VirtualMachineProfile<? extends VirtualMachine> vm,
|
VirtualMachineProfile<? extends VirtualMachine> vm,
|
||||||
StoragePool destPool) throws ConcurrentOperationException;
|
StoragePool destPool);
|
||||||
|
|
||||||
void prepareForMigration(
|
void prepareForMigration(
|
||||||
VirtualMachineProfile<? extends VirtualMachine> vm,
|
VirtualMachineProfile<? extends VirtualMachine> vm,
|
||||||
|
|||||||
@ -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.AttachVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
|
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.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.ResizeVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||||
@ -352,10 +353,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
|
|||||||
"Failed to find a storage pool with enough capacity to move the volume to.");
|
"Failed to find a storage pool with enough capacity to move the volume to.");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Volume> vols = new ArrayList<Volume>();
|
Volume newVol = migrateVolume(volume, destPool);
|
||||||
vols.add(volume);
|
return this.volFactory.getVolume(newVol.getId());
|
||||||
migrateVolumes(vols, destPool);
|
|
||||||
return this.volFactory.getVolume(volume.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1012,10 +1011,15 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
|
|||||||
@DB
|
@DB
|
||||||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_RESIZE, eventDescription = "resizing volume", async = true)
|
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_RESIZE, eventDescription = "resizing volume", async = true)
|
||||||
public VolumeVO resizeVolume(ResizeVolumeCmd cmd) {
|
public VolumeVO resizeVolume(ResizeVolumeCmd cmd) {
|
||||||
VolumeVO volume = _volsDao.findById(cmd.getEntityId());
|
|
||||||
Long newSize = null;
|
Long newSize = null;
|
||||||
boolean shrinkOk = cmd.getShrinkOk();
|
boolean shrinkOk = cmd.getShrinkOk();
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
|
||||||
|
VolumeVO volume = _volsDao.findById(cmd.getEntityId());
|
||||||
|
if (volume == null) {
|
||||||
|
throw new InvalidParameterValueException("No such volume");
|
||||||
|
}
|
||||||
|
|
||||||
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume
|
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume
|
||||||
.getDiskOfferingId());
|
.getDiskOfferingId());
|
||||||
DiskOfferingVO newDiskOffering = null;
|
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");
|
"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) {
|
if (volume.getState() != Volume.State.Ready) {
|
||||||
throw new InvalidParameterValueException(
|
throw new InvalidParameterValueException(
|
||||||
@ -1995,8 +1996,10 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
|
|||||||
|
|
||||||
@DB
|
@DB
|
||||||
@Override
|
@Override
|
||||||
public Volume migrateVolume(Long volumeId, Long storagePoolId)
|
public Volume migrateVolume(MigrateVolumeCmd cmd) {
|
||||||
throws ConcurrentOperationException {
|
Long volumeId = cmd.getVolumeId();
|
||||||
|
Long storagePoolId = cmd.getStoragePoolId();
|
||||||
|
|
||||||
VolumeVO vol = _volsDao.findById(volumeId);
|
VolumeVO vol = _volsDao.findById(volumeId);
|
||||||
if (vol == null) {
|
if (vol == null) {
|
||||||
throw new InvalidParameterValueException(
|
throw new InvalidParameterValueException(
|
||||||
@ -2025,171 +2028,36 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
|
|||||||
"Migration of volume from local storage pool is not supported");
|
"Migration of volume from local storage pool is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Volume> vols = new ArrayList<Volume>();
|
Volume newVol = migrateVolume(vol, destPool);
|
||||||
vols.add(vol);
|
return newVol;
|
||||||
|
|
||||||
migrateVolumes(vols, destPool);
|
|
||||||
return vol;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@DB
|
@DB
|
||||||
public boolean migrateVolumes(List<Volume> volumes, StoragePool destPool)
|
protected Volume migrateVolume(Volume volume, StoragePool destPool) {
|
||||||
throws ConcurrentOperationException {
|
VolumeInfo vol = this.volFactory.getVolume(volume.getId());
|
||||||
Transaction txn = Transaction.currentTxn();
|
AsyncCallFuture<VolumeApiResult> future = this.volService.copyVolume(vol, (DataStore)destPool);
|
||||||
txn.start();
|
|
||||||
|
|
||||||
boolean transitResult = false;
|
|
||||||
long checkPointTaskId = -1;
|
|
||||||
try {
|
try {
|
||||||
List<Long> volIds = new ArrayList<Long>();
|
VolumeApiResult result = future.get();
|
||||||
for (Volume volume : volumes) {
|
if (result.isFailed()) {
|
||||||
if (!_snapshotMgr.canOperateOnVolume((VolumeVO) volume)) {
|
s_logger.debug("migrate volume failed:" + result.getResult());
|
||||||
throw new CloudRuntimeException(
|
return null;
|
||||||
"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();
|
|
||||||
}
|
}
|
||||||
|
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
|
@Override
|
||||||
public boolean StorageMigration(
|
public boolean storageMigration(
|
||||||
VirtualMachineProfile<? extends VirtualMachine> vm,
|
VirtualMachineProfile<? extends VirtualMachine> vm,
|
||||||
StoragePool destPool) throws ConcurrentOperationException {
|
StoragePool destPool) {
|
||||||
List<VolumeVO> vols = _volsDao.findUsableVolumesForInstance(vm.getId());
|
List<VolumeVO> vols = _volsDao.findUsableVolumesForInstance(vm.getId());
|
||||||
List<Volume> volumesNeedToMigrate = new ArrayList<Volume>();
|
List<Volume> volumesNeedToMigrate = new ArrayList<Volume>();
|
||||||
|
|
||||||
@ -2215,7 +2083,13 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return migrateVolumes(volumesNeedToMigrate, destPool);
|
for (Volume vol : volumesNeedToMigrate) {
|
||||||
|
Volume result = migrateVolume(vol, destPool);
|
||||||
|
if (result == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -2452,9 +2326,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
|
|||||||
vol = task.volume;
|
vol = task.volume;
|
||||||
} else if (task.type == VolumeTaskType.MIGRATE) {
|
} else if (task.type == VolumeTaskType.MIGRATE) {
|
||||||
pool = (StoragePool)dataStoreMgr.getDataStore(task.pool.getId(), DataStoreRole.Primary);
|
pool = (StoragePool)dataStoreMgr.getDataStore(task.pool.getId(), DataStoreRole.Primary);
|
||||||
List<Volume> volumes = new ArrayList<Volume>();
|
migrateVolume(task.volume, pool);
|
||||||
volumes.add(task.volume);
|
|
||||||
migrateVolumes(volumes, pool);
|
|
||||||
vol = task.volume;
|
vol = task.volume;
|
||||||
} else if (task.type == VolumeTaskType.RECREATE) {
|
} else if (task.type == VolumeTaskType.RECREATE) {
|
||||||
Pair<VolumeVO, DataStore> result = recreateVolume(task.volume, vm, dest);
|
Pair<VolumeVO, DataStore> result = recreateVolume(task.volume, vm, dest);
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import com.cloud.exception.ResourceAllocationException;
|
|||||||
import com.cloud.host.HostVO;
|
import com.cloud.host.HostVO;
|
||||||
import com.cloud.storage.SnapshotPolicyVO;
|
import com.cloud.storage.SnapshotPolicyVO;
|
||||||
import com.cloud.storage.SnapshotVO;
|
import com.cloud.storage.SnapshotVO;
|
||||||
|
import com.cloud.storage.Volume;
|
||||||
import com.cloud.storage.VolumeVO;
|
import com.cloud.storage.VolumeVO;
|
||||||
import com.cloud.utils.db.Filter;
|
import com.cloud.utils.db.Filter;
|
||||||
|
|
||||||
@ -138,5 +139,5 @@ public interface SnapshotManager {
|
|||||||
|
|
||||||
void deleteSnapshotsDirForVolume(String secondaryStoragePoolUrl, Long dcId, Long accountId, Long volumeId);
|
void deleteSnapshotsDirForVolume(String secondaryStoragePoolUrl, Long dcId, Long accountId, Long volumeId);
|
||||||
|
|
||||||
boolean canOperateOnVolume(VolumeVO volume);
|
boolean canOperateOnVolume(Volume volume);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1578,7 +1578,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canOperateOnVolume(VolumeVO volume) {
|
public boolean canOperateOnVolume(Volume volume) {
|
||||||
List<SnapshotVO> snapshots = _snapshotDao.listByStatus(volume.getId(), Snapshot.State.Creating,
|
List<SnapshotVO> snapshots = _snapshotDao.listByStatus(volume.getId(), Snapshot.State.Creating,
|
||||||
Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp);
|
Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp);
|
||||||
if (snapshots.size() > 0) {
|
if (snapshots.size() > 0) {
|
||||||
|
|||||||
@ -1226,7 +1226,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||||||
VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
|
VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
|
||||||
boolean migrationResult = false;
|
boolean migrationResult = false;
|
||||||
try {
|
try {
|
||||||
migrationResult = this.volumeMgr.StorageMigration(profile, destPool);
|
migrationResult = this.volumeMgr.storageMigration(profile, destPool);
|
||||||
|
|
||||||
if (migrationResult) {
|
if (migrationResult) {
|
||||||
//if the vm is migrated to different pod in basic mode, need to reallocate ip
|
//if the vm is migrated to different pod in basic mode, need to reallocate ip
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user