refactor snapshot

This commit is contained in:
Edison Su 2013-04-29 18:50:46 -07:00
parent 37cbe8890f
commit 2f689171e0
30 changed files with 894 additions and 505 deletions

View File

@ -60,6 +60,9 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity,
CreatedOnPrimary, CreatedOnPrimary,
BackingUp, BackingUp,
BackedUp, BackedUp,
Copying,
Destroying,
Destroyed,//it's a state, user can't see the snapshot from ui, while the snapshot may still exist on the storage
Error; Error;
public String toString() { public String toString() {
@ -76,6 +79,8 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity,
OperationNotPerformed, OperationNotPerformed,
BackupToSecondary, BackupToSecondary,
BackedupToSecondary, BackedupToSecondary,
DestroyRequested,
CopyingRequested,
OperationSucceeded, OperationSucceeded,
OperationFailed OperationFailed
} }

View File

@ -80,6 +80,9 @@ public interface VolumeApiService {
Volume detachVolumeFromVM(DetachVolumeCmd cmmd); Volume detachVolumeFromVM(DetachVolumeCmd cmmd);
Snapshot takeSnapshot(Long volumeId, Long policyId) Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account)
throws ResourceAllocationException; throws ResourceAllocationException;
Snapshot allocSnapshot(Long volumeId, Long policyId)
throws ResourceAllocationException;
} }

View File

@ -152,7 +152,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
@Override @Override
public void create() throws ResourceAllocationException { public void create() throws ResourceAllocationException {
Snapshot snapshot = _snapshotService.allocSnapshot(getVolumeId(), getPolicyId()); Snapshot snapshot = this._volumeService.allocSnapshot(getVolumeId(), getPolicyId());
if (snapshot != null) { if (snapshot != null) {
this.setEntityId(snapshot.getId()); this.setEntityId(snapshot.getId());
this.setEntityUuid(snapshot.getUuid()); this.setEntityUuid(snapshot.getUuid());
@ -164,14 +164,20 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
@Override @Override
public void execute() { public void execute() {
UserContext.current().setEventDetails("Volume Id: "+getVolumeId()); UserContext.current().setEventDetails("Volume Id: "+getVolumeId());
Snapshot snapshot = _snapshotService.createSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId())); Snapshot snapshot;
if (snapshot != null) { try {
SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot); snapshot = _volumeService.takeSnapshot(this.getVolumeId(), this.getPolicyId(), this.getEntityId(), _accountService.getAccount(getEntityOwnerId()));
response.setResponseName(getCommandName()); if (snapshot != null) {
this.setResponseObject(response); SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot);
} else { response.setResponseName(getCommandName());
this.setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot due to an internal error creating snapshot for volume " + volumeId);
}
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot due to an internal error creating snapshot for volume " + volumeId); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot due to an internal error creating snapshot for volume " + volumeId);
} }
} }

View File

@ -3,9 +3,14 @@ package org.apache.cloudstack.engine.subsystem.api.storage;
import com.cloud.storage.Snapshot; import com.cloud.storage.Snapshot;
public interface SnapshotStrategy { public interface SnapshotStrategy {
public SnapshotInfo takeSnapshot(SnapshotInfo snapshot); public SnapshotInfo takeSnapshot(SnapshotInfo snapshot);
public SnapshotInfo backupSnapshot(SnapshotInfo snapshot); public SnapshotInfo backupSnapshot(SnapshotInfo snapshot);
public boolean deleteSnapshot(Long snapshotId); public boolean deleteSnapshot(Long snapshotId);
public boolean canHandle(Snapshot snapshot); /**
* @param snapshot
* @return
*/
boolean canHandle(Snapshot snapshot);
} }

View File

@ -22,4 +22,11 @@ package org.apache.cloudstack.engine.subsystem.api.storage;
public interface StorageCacheManager { public interface StorageCacheManager {
public DataStore getCacheStorage(Scope scope); public DataStore getCacheStorage(Scope scope);
public DataObject createCacheObject(DataObject data, Scope scope); public DataObject createCacheObject(DataObject data, Scope scope);
/** only create cache object in db
* @param data
* @param scope
* @return
*/
DataObject getCacheObject(DataObject data, Scope scope);
DataObject deleteCacheObject(DataObject data);
} }

View File

@ -27,4 +27,5 @@ public interface VolumeInfo extends DataObject, Volume {
public Object getpayload(); public Object getpayload();
public HypervisorType getHypervisorType(); public HypervisorType getHypervisorType();
public Long getLastPoolId(); public Long getLastPoolId();
public String getAttachedVmName();
} }

View File

@ -23,12 +23,12 @@ import com.cloud.agent.api.Command;
public class CopyCommand extends Command implements StorageSubSystemCommand { public class CopyCommand extends Command implements StorageSubSystemCommand {
private DataTO srcTO; private DataTO srcTO;
private DataTO destTO; private DataTO destTO;
private DataTO cacheTO;
public CopyCommand(DataTO srcData, DataTO destData, int timeout) {
public CopyCommand(DataTO srcUri, DataTO destUri, int timeout) {
super(); super();
this.srcTO = srcUri; this.srcTO = srcData;
this.destTO = destUri; this.destTO = destData;
this.setWait(timeout); this.setWait(timeout);
} }
@ -45,4 +45,12 @@ public class CopyCommand extends Command implements StorageSubSystemCommand {
return true; return true;
} }
public DataTO getCacheTO() {
return cacheTO;
}
public void setCacheTO(DataTO cacheTO) {
this.cacheTO = cacheTO;
}
} }

View File

@ -2,21 +2,43 @@ package org.apache.cloudstack.storage.to;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType;
import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.DataTO;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataStoreTO;
public class SnapshotObjectTO implements DataTO { public class SnapshotObjectTO implements DataTO {
private String path; private String path;
private VolumeObjectTO volume;
private String parentSnapshotPath;
private DataStoreTO dataStore;
private String vmName;
private String name;
private long id;
public SnapshotObjectTO() {
}
public SnapshotObjectTO(SnapshotInfo snapshot) {
this.path = snapshot.getPath();
this.setId(snapshot.getId());
this.volume = (VolumeObjectTO)snapshot.getBaseVolume().getTO();
this.setVmName(snapshot.getBaseVolume().getAttachedVmName());
if (snapshot.getParent() != null) {
this.parentSnapshotPath = snapshot.getParent().getPath();
}
this.dataStore = snapshot.getDataStore().getTO();
this.setName(snapshot.getName());
}
@Override @Override
public DataObjectType getObjectType() { public DataObjectType getObjectType() {
// TODO Auto-generated method stub return DataObjectType.SNAPSHOT;
return null;
} }
@Override @Override
public DataStoreTO getDataStore() { public DataStoreTO getDataStore() {
// TODO Auto-generated method stub return this.dataStore;
return null;
} }
@Override @Override
@ -27,4 +49,44 @@ public class SnapshotObjectTO implements DataTO {
public void setPath(String path) { public void setPath(String path) {
this.path = path; this.path = path;
} }
public VolumeObjectTO getVolume() {
return volume;
}
public void setVolume(VolumeObjectTO volume) {
this.volume = volume;
}
public String getParentSnapshotPath() {
return parentSnapshotPath;
}
public void setParentSnapshotPath(String parentSnapshotPath) {
this.parentSnapshotPath = parentSnapshotPath;
}
public String getVmName() {
return vmName;
}
public void setVmName(String vmName) {
this.vmName = vmName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
} }

View File

@ -32,6 +32,7 @@ public class VolumeObjectTO implements DataTO {
private String name; private String name;
private long size; private long size;
private String path; private String path;
private Long volumeId;
public VolumeObjectTO() { public VolumeObjectTO() {
@ -49,6 +50,7 @@ public class VolumeObjectTO implements DataTO {
} }
//this.name = volume.getName(); //this.name = volume.getName();
this.size = volume.getSize(); this.size = volume.getSize();
this.setVolumeId(volume.getId());
} }
public String getUuid() { public String getUuid() {
@ -103,4 +105,12 @@ public class VolumeObjectTO implements DataTO {
this.path = path; this.path = path;
} }
public Long getVolumeId() {
return volumeId;
}
public void setVolumeId(Long volumeId) {
this.volumeId = volumeId;
}
} }

View File

@ -164,10 +164,24 @@ public class StorageCacheManagerImpl implements StorageCacheManager, Manager {
return null; return null;
} }
@Override
public DataObject getCacheObject(DataObject data, Scope scope) {
DataStore cacheStore = this.getCacheStorage(scope);
DataObject objOnCacheStore = cacheStore.create(data);
return objOnCacheStore;
}
protected Void createCacheObjectCallBack(AsyncCallbackDispatcher<StorageCacheManagerImpl, CopyCommandResult> callback, protected Void createCacheObjectCallBack(AsyncCallbackDispatcher<StorageCacheManagerImpl, CopyCommandResult> callback,
CreateCacheObjectContext<CopyCommandResult> context) { CreateCacheObjectContext<CopyCommandResult> context) {
AsyncCallFuture<CopyCommandResult> future = context.future; AsyncCallFuture<CopyCommandResult> future = context.future;
future.complete(callback.getResult()); future.complete(callback.getResult());
return null; return null;
} }
@Override
public DataObject deleteCacheObject(DataObject data) {
// TODO Auto-generated method stub
return null;
}
} }

View File

@ -60,6 +60,8 @@ import com.cloud.agent.api.UpgradeSnapshotCommand;
import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.S3TO;
import com.cloud.agent.api.to.SwiftTO; import com.cloud.agent.api.to.SwiftTO;
import com.cloud.configuration.Config; import com.cloud.configuration.Config;
@ -127,10 +129,6 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
@Inject @Inject
VolumeManager volumeMgr; VolumeManager volumeMgr;
@Inject @Inject
private SwiftManager _swiftMgr;
@Inject
private S3Manager _s3Mgr;
@Inject
StorageCacheManager cacheMgr; StorageCacheManager cacheMgr;
@Override @Override
@ -179,112 +177,55 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
return answer; return answer;
} }
} }
protected Answer copyFromSnapshot(DataObject snapObj, DataObject volObj) { protected DataObject cacheSnapshotChain(SnapshotInfo snapshot) {
SnapshotVO snapshot = this.snapshotDao.findById(snapObj.getId()); DataObject leafData = null;
StoragePool pool = (StoragePool) volObj.getDataStore(); while(snapshot != null) {
String vdiUUID = null; DataObject cacheData = cacheMgr.createCacheObject(snapshot, snapshot.getDataStore().getScope());
Long snapshotId = snapshot.getId(); if (leafData == null) {
Long volumeId = snapshot.getVolumeId(); leafData = cacheData;
Long dcId = snapshot.getDataCenterId();
String secondaryStoragePoolUrl = this.snapshotMgr
.getSecondaryStorageURL(snapshot);
long accountId = snapshot.getAccountId();
String backedUpSnapshotUuid = snapshot.getBackupSnapshotId();
snapshot = snapshotDao.findById(snapshotId);
if (snapshot.getVersion().trim().equals("2.1")) {
VolumeVO volume = this.volDao.findByIdIncludingRemoved(volumeId);
if (volume == null) {
throw new CloudRuntimeException("failed to upgrade snapshot "
+ snapshotId + " due to unable to find orignal volume:"
+ volumeId + ", try it later ");
}
if (volume.getTemplateId() == null) {
snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2");
} else {
VMTemplateVO template = templateDao
.findByIdIncludingRemoved(volume.getTemplateId());
if (template == null) {
throw new CloudRuntimeException(
"failed to upgrade snapshot "
+ snapshotId
+ " due to unalbe to find orignal template :"
+ volume.getTemplateId()
+ ", try it later ");
}
Long templateId = template.getId();
Long tmpltAccountId = template.getAccountId();
if (!snapshotDao.lockInLockTable(snapshotId.toString(), 10)) {
throw new CloudRuntimeException(
"failed to upgrade snapshot "
+ snapshotId
+ " due to this snapshot is being used, try it later ");
}
UpgradeSnapshotCommand cmd = new UpgradeSnapshotCommand(null,
secondaryStoragePoolUrl, dcId, accountId, volumeId,
templateId, tmpltAccountId, null,
snapshot.getBackupSnapshotId(), snapshot.getName(),
"2.1");
Answer answer = null;
try {
answer = this.storageMgr.sendToPool(pool, cmd);
} catch (StorageUnavailableException e) {
} finally {
snapshotDao.unlockFromLockTable(snapshotId.toString());
}
if ((answer != null) && answer.getResult()) {
snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2");
} else {
throw new CloudRuntimeException("Unable to upgrade snapshot from 2.1 to 2.2 for "
+ snapshot.getId());
}
} }
snapshot = snapshot.getParent();
} }
return leafData;
}
protected void deleteSnapshotCacheChain(SnapshotInfo snapshot) {
}
protected Answer copyVolumeFromSnapshot(DataObject snapObj, DataObject volObj) {
SnapshotInfo snapshot = (SnapshotInfo)snapObj;
StoragePool pool = (StoragePool) volObj.getDataStore();
String basicErrMsg = "Failed to create volume from " String basicErrMsg = "Failed to create volume from "
+ snapshot.getName() + " on pool " + pool; + snapshot.getName() + " on pool " + pool;
DataStore store = snapObj.getDataStore();
DataStoreTO storTO = store.getTO();
DataObject srcData = snapObj;
try { try {
if (snapshot.getSwiftId() != null && snapshot.getSwiftId() != 0) { if (!(storTO instanceof NfsTO)) {
snapshotMgr.downloadSnapshotsFromSwift(snapshot); srcData = cacheSnapshotChain(snapshot);
} else if (snapshot.getS3Id() != null && snapshot.getS3Id() != 0) {
snapshotMgr.downloadSnapshotsFromS3(snapshot);
} }
String value = configDao String value = configDao
.getValue(Config.CreateVolumeFromSnapshotWait.toString()); .getValue(Config.CreateVolumeFromSnapshotWait.toString());
int _createVolumeFromSnapshotWait = NumbersUtil.parseInt(value, int _createVolumeFromSnapshotWait = NumbersUtil.parseInt(value,
Integer.parseInt(Config.CreateVolumeFromSnapshotWait Integer.parseInt(Config.CreateVolumeFromSnapshotWait
.getDefaultValue())); .getDefaultValue()));
CreateVolumeFromSnapshotCommand createVolumeFromSnapshotCommand = new CreateVolumeFromSnapshotCommand(
pool, secondaryStoragePoolUrl, dcId, accountId, volumeId, CopyCommand cmd = new CopyCommand(srcData.getTO(), volObj.getTO(), _createVolumeFromSnapshotWait);
backedUpSnapshotUuid, snapshot.getName(),
_createVolumeFromSnapshotWait);
CreateVolumeFromSnapshotAnswer answer; Answer answer = this.storageMgr
if (!snapshotDao.lockInLockTable(snapshotId.toString(), 10)) { .sendToPool(pool, cmd);
throw new CloudRuntimeException("failed to create volume from " return answer;
+ snapshotId
+ " due to this snapshot is being used, try it later ");
}
answer = (CreateVolumeFromSnapshotAnswer) this.storageMgr
.sendToPool(pool, createVolumeFromSnapshotCommand);
if (answer != null && answer.getResult()) {
vdiUUID = answer.getVdi();
VolumeVO vol = this.volDao.findById(volObj.getId());
vol.setPath(vdiUUID);
this.volDao.update(vol.getId(), vol);
return null;
} else {
s_logger.error(basicErrMsg + " due to "
+ ((answer == null) ? "null" : answer.getDetails()));
throw new CloudRuntimeException(basicErrMsg);
}
} catch (StorageUnavailableException e) { } catch (StorageUnavailableException e) {
s_logger.error(basicErrMsg, e); s_logger.error(basicErrMsg, e);
throw new CloudRuntimeException(basicErrMsg); throw new CloudRuntimeException(basicErrMsg);
} finally { } finally {
if (snapshot.getSwiftId() != null) { if (!(storTO instanceof NfsTO)) {
snapshotMgr.deleteSnapshotsDirForVolume( deleteSnapshotCacheChain((SnapshotInfo)srcData);
secondaryStoragePoolUrl, dcId, accountId, volumeId);
} }
} }
} }
@ -328,7 +269,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
answer = copyTemplate(srcData, destData); answer = copyTemplate(srcData, destData);
} else if (srcData.getType() == DataObjectType.SNAPSHOT } else if (srcData.getType() == DataObjectType.SNAPSHOT
&& destData.getType() == DataObjectType.VOLUME) { && destData.getType() == DataObjectType.VOLUME) {
answer = copyFromSnapshot(srcData, destData); answer = copyVolumeFromSnapshot(srcData, destData);
} else if (srcData.getType() == DataObjectType.SNAPSHOT } else if (srcData.getType() == DataObjectType.SNAPSHOT
&& destData.getType() == DataObjectType.TEMPLATE) { && destData.getType() == DataObjectType.TEMPLATE) {
answer = createTemplateFromSnapshot(srcData, destData); answer = createTemplateFromSnapshot(srcData, destData);
@ -359,49 +300,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
@DB @DB
protected Answer createTemplateFromSnapshot(DataObject srcData, protected Answer createTemplateFromSnapshot(DataObject srcData,
DataObject destData) { DataObject destData) {
long snapshotId = srcData.getId();
SnapshotVO snapshot = snapshotDao.findById(snapshotId);
if (snapshot == null) {
throw new CloudRuntimeException("Unable to find Snapshot for Id "
+ srcData.getId());
}
Long zoneId = snapshot.getDataCenterId();
DataStore secStore = destData.getDataStore();
/*
HostVO secondaryStorageHost = this.templateMgr
.getSecondaryStorageHost(zoneId);
*/
String secondaryStorageURL = snapshotMgr
.getSecondaryStorageURL(snapshot);
VMTemplateVO template = this.templateDao.findById(destData.getId());
String name = template.getName();
String backupSnapshotUUID = snapshot.getBackupSnapshotId();
if (backupSnapshotUUID == null) {
throw new CloudRuntimeException(
"Unable to create private template from snapshot "
+ snapshotId
+ " due to there is no backupSnapshotUUID for this snapshot");
}
Long dcId = snapshot.getDataCenterId();
Long accountId = snapshot.getAccountId();
Long volumeId = snapshot.getVolumeId();
String origTemplateInstallPath = null;
List<StoragePoolVO> pools = this.storageMgr
.ListByDataCenterHypervisor(zoneId,
snapshot.getHypervisorType());
if (pools == null || pools.size() == 0) {
throw new CloudRuntimeException(
"Unable to find storage pools in zone " + zoneId);
}
StoragePoolVO poolvo = pools.get(0);
StoragePool pool = (StoragePool) this.dataStoreMgr.getDataStore(
poolvo.getId(), DataStoreRole.Primary);
if (snapshot.getSwiftId() != null && snapshot.getSwiftId() != 0) {
snapshotMgr.downloadSnapshotsFromSwift(snapshot);
}
String value = configDao String value = configDao
.getValue(Config.CreatePrivateTemplateFromSnapshotWait .getValue(Config.CreatePrivateTemplateFromSnapshotWait
.toString()); .toString());
@ -410,172 +309,71 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
.parseInt(Config.CreatePrivateTemplateFromSnapshotWait .parseInt(Config.CreatePrivateTemplateFromSnapshotWait
.getDefaultValue())); .getDefaultValue()));
CreatePrivateTemplateFromSnapshotCommand cmd = new CreatePrivateTemplateFromSnapshotCommand( if (srcData.getDataStore().getRole() != DataStoreRole.ImageCache && destData.getDataStore().getRole() != DataStoreRole.ImageCache) {
pool, secondaryStorageURL, dcId, accountId, SnapshotInfo snapshot = (SnapshotInfo)srcData;
snapshot.getVolumeId(), backupSnapshotUUID, snapshot.getName(), srcData = cacheSnapshotChain(snapshot);
origTemplateInstallPath, template.getId(), name,
_createprivatetemplatefromsnapshotwait);
return sendCommand(cmd, pool, template.getId(), dcId, secStore);
}
@DB
protected Answer sendCommand(Command cmd, StoragePool pool,
long templateId, long zoneId, DataStore secStore) {
CreatePrivateTemplateAnswer answer = null;
try {
answer = (CreatePrivateTemplateAnswer) this.storageMgr.sendToPool(
pool, cmd);
} catch (StorageUnavailableException e) {
throw new CloudRuntimeException(
"Failed to execute CreatePrivateTemplateFromSnapshotCommand",
e);
} }
if (answer == null || !answer.getResult()) { CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _createprivatetemplatefromsnapshotwait);
return answer; EndPoint ep = selector.select(srcData, destData);
} Answer answer = ep.sendMessage(cmd);
VMTemplateVO privateTemplate = templateDao.findById(templateId);
String answerUniqueName = answer.getUniqueName();
if (answerUniqueName != null) {
privateTemplate.setUniqueName(answerUniqueName);
}
ImageFormat format = answer.getImageFormat();
if (format != null) {
privateTemplate.setFormat(format);
} else {
// This never occurs.
// Specify RAW format makes it unusable for snapshots.
privateTemplate.setFormat(ImageFormat.RAW);
}
String checkSum = this.templateMgr
.getChecksum(secStore, answer.getPath());
Transaction txn = Transaction.currentTxn();
txn.start();
privateTemplate.setChecksum(checkSum);
templateDao.update(privateTemplate.getId(), privateTemplate);
// add template zone ref for this template
templateDao.addTemplateToZone(privateTemplate, zoneId);
TemplateDataStoreVO templateHostVO = new TemplateDataStoreVO(secStore.getId(),
privateTemplate.getId());
templateHostVO.setDownloadPercent(100);
templateHostVO.setDownloadState(Status.DOWNLOADED);
templateHostVO.setInstallPath(answer.getPath());
templateHostVO.setLastUpdated(new Date());
templateHostVO.setSize(answer.getVirtualSize());
templateHostVO.setPhysicalSize(answer.getphysicalSize());
templateStoreDao.persist(templateHostVO);
txn.close();
return answer; return answer;
} }
private Answer createTemplateFromVolume(DataObject srcObj,
DataObject destObj) {
long volumeId = srcObj.getId();
VolumeVO volume = this.volDao.findById(volumeId);
if (volume == null) {
throw new CloudRuntimeException("Unable to find volume for Id "
+ volumeId);
}
long accountId = volume.getAccountId();
String vmName = this.volumeMgr.getVmNameOnVolume(volume); private Answer createTemplateFromVolume(DataObject srcData,
Long zoneId = volume.getDataCenterId(); DataObject destData) {
DataStore secStore = destObj.getDataStore();
String secondaryStorageURL = secStore.getUri();
VMTemplateVO template = this.templateDao.findById(destObj.getId());
StoragePool pool = (StoragePool) this.dataStoreMgr.getDataStore(
volume.getPoolId(), DataStoreRole.Primary);
String value = configDao String value = configDao
.getValue(Config.CreatePrivateTemplateFromVolumeWait.toString()); .getValue(Config.CreatePrivateTemplateFromVolumeWait.toString());
int _createprivatetemplatefromvolumewait = NumbersUtil.parseInt(value, int _createprivatetemplatefromvolumewait = NumbersUtil.parseInt(value,
Integer.parseInt(Config.CreatePrivateTemplateFromVolumeWait Integer.parseInt(Config.CreatePrivateTemplateFromVolumeWait
.getDefaultValue())); .getDefaultValue()));
CreatePrivateTemplateFromVolumeCommand cmd = new CreatePrivateTemplateFromVolumeCommand( if (srcData.getDataStore().getRole() != DataStoreRole.ImageCache && destData.getDataStore().getRole() != DataStoreRole.ImageCache) {
pool, secondaryStorageURL, destObj.getId(), accountId, //need to copy it to image cache store
template.getName(), template.getUniqueName(), volume.getPath(), DataObject cacheData = cacheMgr.createCacheObject(srcData, destData.getDataStore().getScope());
vmName, _createprivatetemplatefromvolumewait); CopyCommand cmd = new CopyCommand(cacheData.getTO(), destData.getTO(), _createprivatetemplatefromvolumewait);
EndPoint ep = selector.select(cacheData, destData);
return sendCommand(cmd, pool, template.getId(), zoneId, secStore); Answer answer = ep.sendMessage(cmd);
} return answer;
} else {
private DataStore getSecHost(long volumeId, long dcId) { //handle copy it to/from cache store
Long id = snapshotDao.getSecHostId(volumeId); CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _createprivatetemplatefromvolumewait);
if ( id != null) { EndPoint ep = selector.select(srcData, destData);
return this.dataStoreMgr.getDataStore(id, DataStoreRole.Image); Answer answer = ep.sendMessage(cmd);
return answer;
} }
return this.dataStoreMgr.getImageStore(dcId);
} }
protected Answer copySnapshot(DataObject srcObject, DataObject destObject) { protected Answer copySnapshot(DataObject srcData, DataObject destData) {
SnapshotInfo srcSnapshot = (SnapshotInfo)srcObject; String value = configDao.getValue(Config.BackupSnapshotWait.toString());
VolumeInfo baseVolume = srcSnapshot.getBaseVolume(); int _backupsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue()));
Long dcId = baseVolume.getDataCenterId();
Long accountId = baseVolume.getAccountId();
DataStore secStore = getSecHost(baseVolume.getId(), baseVolume.getDataCenterId()); DataObject cacheData = null;
Long secHostId = secStore.getId(); try {
String secondaryStoragePoolUrl = secStore.getUri(); if (destData.getDataStore().getRole() != DataStoreRole.ImageCache) {
String snapshotUuid = srcSnapshot.getPath(); cacheData = cacheMgr.getCacheObject(srcData, destData.getDataStore().getScope());
// In order to verify that the snapshot is not empty,
// we check if the parent of the snapshot is not the same as the parent of the previous snapshot. CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait);
// We pass the uuid of the previous snapshot to the plugin to verify this. cmd.setCacheTO(cacheData.getTO());
SnapshotVO prevSnapshot = null; EndPoint ep = selector.select(srcData, destData);
String prevSnapshotUuid = null; Answer answer = ep.sendMessage(cmd);
String prevBackupUuid = null; return answer;
} else {
CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait);
SwiftTO swift = _swiftMgr.getSwiftTO(); EndPoint ep = selector.select(srcData, destData);
S3TO s3 = _s3Mgr.getS3TO(); Answer answer = ep.sendMessage(cmd);
return answer;
long prevSnapshotId = srcSnapshot.getPrevSnapshotId(); }
if (prevSnapshotId > 0) { } catch (Exception e) {
prevSnapshot = snapshotDao.findByIdIncludingRemoved(prevSnapshotId); s_logger.debug("copy snasphot failed: " + e.toString());
if ( prevSnapshot.getBackupSnapshotId() != null && swift == null) { if (cacheData != null) {
if (prevSnapshot.getVersion() != null && prevSnapshot.getVersion().equals("2.2")) { cacheMgr.deleteCacheObject(cacheData);
prevBackupUuid = prevSnapshot.getBackupSnapshotId(); }
prevSnapshotUuid = prevSnapshot.getPath(); throw new CloudRuntimeException(e.toString());
} }
} else if ((prevSnapshot.getSwiftId() != null && swift != null)
|| (prevSnapshot.getS3Id() != null && s3 != null)) {
prevBackupUuid = prevSnapshot.getBackupSnapshotId();
prevSnapshotUuid = prevSnapshot.getPath();
}
}
boolean isVolumeInactive = this.volumeMgr.volumeInactive(baseVolume);
String vmName = this.volumeMgr.getVmNameOnVolume(baseVolume);
StoragePool srcPool = (StoragePool)dataStoreMgr.getPrimaryDataStore(baseVolume.getPoolId());
String value = configDao.getValue(Config.BackupSnapshotWait.toString());
int _backupsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue()));
BackupSnapshotCommand backupSnapshotCommand = new BackupSnapshotCommand(secondaryStoragePoolUrl, dcId, accountId, baseVolume.getId(), srcSnapshot.getId(), secHostId, baseVolume.getPath(), srcPool, snapshotUuid,
srcSnapshot.getName(), prevSnapshotUuid, prevBackupUuid, isVolumeInactive, vmName, _backupsnapshotwait);
if ( swift != null ) {
backupSnapshotCommand.setSwift(swift);
} else if (s3 != null) {
backupSnapshotCommand.setS3(s3);
}
BackupSnapshotAnswer answer = (BackupSnapshotAnswer) this.snapshotMgr.sendToPool(baseVolume, backupSnapshotCommand);
if (answer != null && answer.getResult()) {
SnapshotVO snapshotVO = this.snapshotDao.findById(srcSnapshot.getId());
snapshotVO.setBackupSnapshotId(answer.getBackupSnapshotName());
// persist an entry in snapshot_store_ref
SnapshotDataStoreVO snapshotStore = new SnapshotDataStoreVO(secStore.getId(), snapshotVO.getId());
this._snapshotStoreDao.persist(snapshotStore);
if (answer.isFull()) {
snapshotVO.setPrevSnapshotId(0L);
}
this.snapshotDao.update(srcSnapshot.getId(), snapshotVO);
}
return answer;
} }
} }

View File

@ -30,6 +30,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat; import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.command.CreateObjectAnswer;
import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
@ -234,6 +235,13 @@ public class SnapshotObject implements SnapshotInfo {
SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CreateObjectAnswer) answer).getData(); SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CreateObjectAnswer) answer).getData();
snapshotStore.setInstallPath(snapshotTO.getPath()); snapshotStore.setInstallPath(snapshotTO.getPath());
this.snapshotStore.update(snapshotStore.getId(), snapshotStore); this.snapshotStore.update(snapshotStore.getId(), snapshotStore);
} else if (answer instanceof CopyCmdAnswer) {
SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CopyCmdAnswer) answer).getNewData();
snapshotStore.setInstallPath(snapshotTO.getPath());
if (snapshotTO.getParentSnapshotPath() == null) {
snapshotStore.setParentSnapshotId(0L);
}
this.snapshotStore.update(snapshotStore.getId(), snapshotStore);
} else { } else {
throw new CloudRuntimeException("Unknown answer: " + answer.getClass()); throw new CloudRuntimeException("Unknown answer: " + answer.getClass());
} }

View File

@ -399,7 +399,7 @@ public class SnapshotServiceImpl implements SnapshotService {
@Override @Override
public boolean deleteSnapshot(SnapshotInfo snapInfo) { public boolean deleteSnapshot(SnapshotInfo snapInfo) {
return true;
} }

View File

@ -44,6 +44,12 @@ SnapshotStateMachineManager {
stateMachine.addTransition(Snapshot.State.CreatedOnPrimary, Event.BackupToSecondary, Snapshot.State.BackingUp); stateMachine.addTransition(Snapshot.State.CreatedOnPrimary, Event.BackupToSecondary, Snapshot.State.BackingUp);
stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationSucceeded, Snapshot.State.BackedUp); stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationSucceeded, Snapshot.State.BackedUp);
stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationFailed, Snapshot.State.CreatedOnPrimary); stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationFailed, Snapshot.State.CreatedOnPrimary);
stateMachine.addTransition(Snapshot.State.BackedUp, Event.DestroyRequested, Snapshot.State.Destroying);
stateMachine.addTransition(Snapshot.State.BackedUp, Event.CopyingRequested, Snapshot.State.Copying);
stateMachine.addTransition(Snapshot.State.Copying, Event.OperationSucceeded, Snapshot.State.BackedUp);
stateMachine.addTransition(Snapshot.State.Copying, Event.OperationFailed, Snapshot.State.BackedUp);
stateMachine.addTransition(Snapshot.State.Destroying, Event.OperationSucceeded, Snapshot.State.Destroyed);
stateMachine.addTransition(Snapshot.State.Destroying, Event.OperationFailed, Snapshot.State.Error);
stateMachine.registerListener(new SnapshotStateListener()); stateMachine.registerListener(new SnapshotStateListener());
} }

View File

@ -9,9 +9,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy;
public abstract class SnapshotStrategyBase implements SnapshotStrategy { public abstract class SnapshotStrategyBase implements SnapshotStrategy {
@Inject @Inject
SnapshotService snapshotSvr; SnapshotService snapshotSvr;
//the default strategy is:
//create snapshot,
//backup, then delete snapshot on primary storage
@Override @Override
public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) { public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) {
return snapshotSvr.takeSnapshot(snapshot).getSnashot(); return snapshotSvr.takeSnapshot(snapshot).getSnashot();

View File

@ -27,6 +27,7 @@ import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Snapshot; import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO; import com.cloud.storage.SnapshotVO;
import com.cloud.storage.VolumeVO; import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.storage.snapshot.SnapshotManager;
import com.cloud.utils.NumbersUtil; import com.cloud.utils.NumbersUtil;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
@ -48,6 +49,8 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
@Inject @Inject
ConfigurationDao configDao; ConfigurationDao configDao;
@Inject @Inject
SnapshotDao snapshotDao;
@Inject
SnapshotDataFactory snapshotDataFactory; SnapshotDataFactory snapshotDataFactory;
@Override @Override
@ -114,82 +117,72 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
snapshot.addPayload(fullBackup); snapshot.addPayload(fullBackup);
return this.snapshotSvr.backupSnapshot(snapshot); return this.snapshotSvr.backupSnapshot(snapshot);
} }
protected void deleteSnapshotChain(SnapshotInfo snapshot) {
while(snapshot != null) {
SnapshotInfo child = snapshot.getChild();
SnapshotInfo parent = snapshot.getParent();
if (child == null) {
if (!parent.getPath().equalsIgnoreCase(snapshot.getPath())) {
this.snapshotSvr.deleteSnapshot(snapshot);
snapshot = parent;
continue;
}
break;
} else {
break;
}
}
}
@Override @Override
public boolean deleteSnapshot(SnapshotInfo snapshot) { public boolean deleteSnapshot(Long snapshotId) {
Long snapshotId = snapshot.getId(); SnapshotVO snapshotVO = snapshotDao.findById(snapshotId);
SnapshotObject snapObj = (SnapshotObject)snapshot; if (snapshotVO.getState() == Snapshot.State.Destroyed) {
return true;
if (!Snapshot.State.BackedUp.equals(snapshot.getState()) || !Snapshot) { }
if (!Snapshot.State.BackedUp.equals(snapshotVO.getState())) {
throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is not in BackedUp Status"); throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is not in BackedUp Status");
} }
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("Calling deleteSnapshot for snapshotId: " + snapshotId); s_logger.debug("Calling deleteSnapshot for snapshotId: " + snapshotId);
} }
SnapshotVO lastSnapshot = null;
if (snapshot.getPrevSnapshotId() != null) {
List<SnapshotVO> snaps = _snapshotDao.listByBackupUuid(snapshot.getVolumeId(), snapshot.getBackupSnapshotId()); //firt mark the snapshot as destroyed, so that ui can't see it, but we may not destroy the snapshot on the storage, as other snaphosts may depend on it.
if (snaps != null && snaps.size() > 1) { SnapshotInfo snapshotOnPrimary = this.snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
snapshot.setBackupSnapshotId(null); SnapshotObject obj = (SnapshotObject)snapshotOnPrimary;
SnapshotVO snapshotVO = this._snapshotDao.findById(snapshotId); try {
_snapshotDao.update(snapshot.getId(), snapshotVO); obj.processEvent(Snapshot.Event.DestroyRequested);
} } catch (NoTransitionException e) {
} s_logger.debug("Failed to destroy snapshot: " + e.toString());
return false;
}
try {
if (snapshotOnPrimary != null) {
deleteSnapshotChain(snapshotOnPrimary);
}
_snapshotDao.remove(snapshotId); SnapshotInfo snapshotOnImage = this.snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Image);
if (snapshotOnImage != null) {
long lastId = snapshotId; deleteSnapshotChain(snapshotOnImage);
boolean destroy = false; }
while (true) {
lastSnapshot = _snapshotDao.findNextSnapshot(lastId); obj.processEvent(Snapshot.Event.OperationSucceeded);
if (lastSnapshot == null) { } catch (Exception e) {
// if all snapshots after this snapshot in this chain are removed, remove those snapshots. s_logger.debug("Failed to delete snapshot: " + e.toString());
destroy = true; try {
break; obj.processEvent(Snapshot.Event.OperationFailed);
} } catch (NoTransitionException e1) {
if (lastSnapshot.getRemoved() == null) { s_logger.debug("Failed to change snapshot state: " + e.toString());
// if there is one child not removed, then can not remove back up snapshot. }
break;
}
lastId = lastSnapshot.getId();
}
if (destroy) {
lastSnapshot = _snapshotDao.findByIdIncludingRemoved(lastId);
while (lastSnapshot.getRemoved() != null) {
String BackupSnapshotId = lastSnapshot.getBackupSnapshotId();
if (BackupSnapshotId != null) {
List<SnapshotVO> snaps = _snapshotDao.listByBackupUuid(lastSnapshot.getVolumeId(), BackupSnapshotId);
if (snaps != null && snaps.size() > 1) {
lastSnapshot.setBackupSnapshotId(null);
_snapshotDao.update(lastSnapshot.getId(), lastSnapshot);
} else {
if (destroySnapshotBackUp(lastSnapshot)) {
} else {
s_logger.debug("Destroying snapshot backup failed " + lastSnapshot);
break;
}
}
}
lastId = lastSnapshot.getPrevSnapshotId();
if (lastId == 0) {
break;
}
lastSnapshot = _snapshotDao.findByIdIncludingRemoved(lastId);
}
} }
return true; return true;
} }
@Override
public boolean canHandle(SnapshotInfo snapshot) {
if (snapshot.getHypervisorType() == HypervisorType.XenServer) {
return true;
} else {
return false;
}
}
@Override @Override
public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) { public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) {
@ -197,4 +190,13 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
//TODO: add async //TODO: add async
return this.backupSnapshot(snapshot); return this.backupSnapshot(snapshot);
} }
@Override
public boolean canHandle(Snapshot snapshot) {
if (snapshot.getHypervisorType() == HypervisorType.XenServer) {
return true;
} else {
return false;
}
}
} }

View File

@ -84,33 +84,17 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager {
public ObjectInDataStoreManagerImpl() { public ObjectInDataStoreManagerImpl() {
stateMachines = new StateMachine2<State, Event, DataObjectInStore>(); stateMachines = new StateMachine2<State, Event, DataObjectInStore>();
stateMachines.addTransition(State.Allocated, Event.CreateRequested, stateMachines.addTransition(State.Allocated, Event.CreateOnlyRequested,
State.Creating); State.Creating);
stateMachines.addTransition(State.Creating, Event.OperationSuccessed,
State.Created);
stateMachines.addTransition(State.Creating, Event.OperationFailed, stateMachines.addTransition(State.Creating, Event.OperationFailed,
State.Failed); State.Allocated);
stateMachines.addTransition(State.Failed, Event.CreateRequested, stateMachines.addTransition(State.Creating, Event.OperationSuccessed,
State.Creating); State.Ready);
stateMachines.addTransition(State.Ready, Event.DestroyRequested, stateMachines.addTransition(State.Ready, Event.CopyingRequested,
State.Destroying);
stateMachines.addTransition(State.Destroying, Event.OperationSuccessed,
State.Destroyed);
stateMachines.addTransition(State.Destroying, Event.OperationFailed,
State.Destroying);
stateMachines.addTransition(State.Destroying, Event.DestroyRequested,
State.Destroying);
stateMachines.addTransition(State.Created, Event.CopyingRequested,
State.Copying); State.Copying);
stateMachines.addTransition(State.Copying, Event.OperationFailed,
State.Created);
stateMachines.addTransition(State.Copying, Event.OperationSuccessed, stateMachines.addTransition(State.Copying, Event.OperationSuccessed,
State.Ready); State.Ready);
stateMachines.addTransition(State.Allocated, Event.CreateOnlyRequested, stateMachines.addTransition(State.Copying, Event.OperationFailed,
State.Creating2);
stateMachines.addTransition(State.Creating2, Event.OperationFailed,
State.Allocated);
stateMachines.addTransition(State.Creating2, Event.OperationSuccessed,
State.Ready); State.Ready);
} }

View File

@ -149,4 +149,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
sc.setParameters("store_role", role); sc.setParameters("store_role", role);
return findOneBy(sc); return findOneBy(sc);
} }
@Override
public SnapshotDataStoreVO findByStoreSnapshot(long storeId, long snapshotId, boolean lock) {
// TODO Auto-generated method stub
return null;
}
} }

View File

@ -87,12 +87,6 @@ public class SnapshotEntityImpl implements SnapshotEntity {
return 0; return 0;
} }
@Override
public String getPath() {
// TODO Auto-generated method stub
return null;
}
@Override @Override
public String getName() { public String getName() {
// TODO Auto-generated method stub // TODO Auto-generated method stub

View File

@ -46,6 +46,8 @@ import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.fsm.StateMachine2;
import com.cloud.utils.storage.encoding.EncodingType; import com.cloud.utils.storage.encoding.EncodingType;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;
public class VolumeObject implements VolumeInfo { public class VolumeObject implements VolumeInfo {
private static final Logger s_logger = Logger.getLogger(VolumeObject.class); private static final Logger s_logger = Logger.getLogger(VolumeObject.class);
@ -58,6 +60,8 @@ public class VolumeObject implements VolumeInfo {
VolumeDataStoreDao volumeStoreDao; VolumeDataStoreDao volumeStoreDao;
@Inject @Inject
ObjectInDataStoreManager ojbectInStoreMgr; ObjectInDataStoreManager ojbectInStoreMgr;
@Inject
VMInstanceDao vmInstanceDao;
private Object payload; private Object payload;
public VolumeObject() { public VolumeObject() {
@ -74,6 +78,19 @@ public class VolumeObject implements VolumeInfo {
vo.configure(dataStore, volumeVO); vo.configure(dataStore, volumeVO);
return vo; return vo;
} }
public String getAttachedVmName() {
Long vmId = this.volumeVO.getInstanceId();
if (vmId != null) {
VMInstanceVO vm = vmInstanceDao.findById(vmId);
if (vm == null) {
return null;
}
return vm.getInstanceName();
}
return null;
}
@Override @Override
public String getUuid() { public String getUuid() {

View File

@ -408,15 +408,17 @@ public class VolumeServiceImpl implements VolumeService {
private final AsyncCallFuture<VolumeApiResult> future; private final AsyncCallFuture<VolumeApiResult> future;
private final DataStore primaryStore; private final DataStore primaryStore;
private final DataObject templateOnStore; private final DataObject templateOnStore;
private final SnapshotInfo snapshot;
public CreateVolumeFromBaseImageContext(AsyncCompletionCallback<T> callback, VolumeObject vo, public CreateVolumeFromBaseImageContext(AsyncCompletionCallback<T> callback, VolumeObject vo,
DataStore primaryStore, DataStore primaryStore,
DataObject templateOnStore, DataObject templateOnStore,
AsyncCallFuture<VolumeApiResult> future) { AsyncCallFuture<VolumeApiResult> future, SnapshotInfo snapshot) {
super(callback); super(callback);
this.vo = vo; this.vo = vo;
this.future = future; this.future = future;
this.primaryStore = primaryStore; this.primaryStore = primaryStore;
this.templateOnStore = templateOnStore; this.templateOnStore = templateOnStore;
this.snapshot = snapshot;
} }
@ -428,7 +430,7 @@ public class VolumeServiceImpl implements VolumeService {
@DB @DB
protected void createVolumeFromBaseImageAsync(VolumeInfo volume, DataObject templateOnPrimaryStore, PrimaryDataStore pd, AsyncCallFuture<VolumeApiResult> future) { protected void createVolumeFromBaseImageAsync(VolumeInfo volume, DataObject templateOnPrimaryStore, PrimaryDataStore pd, AsyncCallFuture<VolumeApiResult> future) {
VolumeObject vo = (VolumeObject)volume; VolumeObject vo = (VolumeObject)volume;
CreateVolumeFromBaseImageContext<VolumeApiResult> context = new CreateVolumeFromBaseImageContext<VolumeApiResult>(null, vo, pd, templateOnPrimaryStore, future); CreateVolumeFromBaseImageContext<VolumeApiResult> context = new CreateVolumeFromBaseImageContext<VolumeApiResult>(null, vo, pd, templateOnPrimaryStore, future, null);
AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().createVolumeFromBaseImageCallBack(null, null)) caller.setCallback(caller.getTarget().createVolumeFromBaseImageCallBack(null, null))
.setContext(context); .setContext(context);
@ -497,8 +499,9 @@ public class VolumeServiceImpl implements VolumeService {
try { try {
DataObject volumeOnStore = store.create(volume); DataObject volumeOnStore = store.create(volume);
volume.processEvent(Event.CreateOnlyRequested); volume.processEvent(Event.CreateOnlyRequested);
snapshot.processEvent(Event.CopyingRequested);
CreateVolumeFromBaseImageContext<VolumeApiResult> context = new CreateVolumeFromBaseImageContext<VolumeApiResult>(null, CreateVolumeFromBaseImageContext<VolumeApiResult> context = new CreateVolumeFromBaseImageContext<VolumeApiResult>(null,
(VolumeObject)volume, store, volumeOnStore, future); (VolumeObject)volume, store, volumeOnStore, future, snapshot);
AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().createVolumeFromSnapshotCallback(null, null)) caller.setCallback(caller.getTarget().createVolumeFromSnapshotCallback(null, null))
.setContext(context); .setContext(context);
@ -517,6 +520,7 @@ public class VolumeServiceImpl implements VolumeService {
CreateVolumeFromBaseImageContext<VolumeApiResult> context) { CreateVolumeFromBaseImageContext<VolumeApiResult> context) {
CopyCommandResult result = callback.getResult(); CopyCommandResult result = callback.getResult();
VolumeInfo volume = context.vo; VolumeInfo volume = context.vo;
SnapshotInfo snapshot = context.snapshot;
VolumeApiResult apiResult = new VolumeApiResult(volume); VolumeApiResult apiResult = new VolumeApiResult(volume);
Event event = null; Event event = null;
if (result.isFailed()) { if (result.isFailed()) {
@ -528,6 +532,7 @@ public class VolumeServiceImpl implements VolumeService {
try { try {
volume.processEvent(event); volume.processEvent(event);
snapshot.processEvent(event);
} catch (Exception e) { } catch (Exception e) {
s_logger.debug("create volume from snapshot failed", e); s_logger.debug("create volume from snapshot failed", e);
apiResult.setResult(e.toString()); apiResult.setResult(e.toString());

View File

@ -3620,7 +3620,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return false; return false;
} }
private void swiftBackupSnapshot(Connection conn, SwiftTO swift, String srUuid, String snapshotUuid, String container, Boolean isISCSI, int wait) { public void swiftBackupSnapshot(Connection conn, SwiftTO swift, String srUuid, String snapshotUuid, String container, Boolean isISCSI, int wait) {
String lfilename; String lfilename;
String ldir; String ldir;
if ( isISCSI ) { if ( isISCSI ) {

View File

@ -18,12 +18,20 @@
*/ */
package com.cloud.hypervisor.xen.resource; package com.cloud.hypervisor.xen.resource;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -43,6 +51,7 @@ import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol; import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol;
import org.apache.cloudstack.storage.to.ImageStoreTO; import org.apache.cloudstack.storage.to.ImageStoreTO;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
@ -54,18 +63,25 @@ import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.XmlRpcException;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.BackupSnapshotAnswer;
import com.cloud.agent.api.Command; import com.cloud.agent.api.Command;
import com.cloud.agent.api.ManageSnapshotAnswer;
import com.cloud.agent.api.ManageSnapshotCommand;
import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CreateAnswer; import com.cloud.agent.api.storage.CreateAnswer;
import com.cloud.agent.api.storage.DeleteVolumeCommand; import com.cloud.agent.api.storage.DeleteVolumeCommand;
import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.NfsTO;
import com.cloud.agent.api.to.S3TO;
import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.api.to.SwiftTO;
import com.cloud.agent.api.to.VolumeTO; import com.cloud.agent.api.to.VolumeTO;
import com.cloud.exception.InternalErrorException; import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.xen.resource.CitrixResourceBase.SRType; import com.cloud.hypervisor.xen.resource.CitrixResourceBase.SRType;
import com.cloud.storage.DataStoreRole; import com.cloud.storage.DataStoreRole;
import com.cloud.utils.S3Utils;
import com.cloud.utils.StringUtils;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.storage.encoding.DecodedDataObject; import com.cloud.utils.storage.encoding.DecodedDataObject;
import com.cloud.utils.storage.encoding.DecodedDataStore; import com.cloud.utils.storage.encoding.DecodedDataStore;
@ -80,8 +96,6 @@ import com.xensource.xenapi.Types.BadServerResponse;
import com.xensource.xenapi.Types.XenAPIException; import com.xensource.xenapi.Types.XenAPIException;
import com.xensource.xenapi.VDI; import com.xensource.xenapi.VDI;
import edu.emory.mathcs.backport.java.util.Arrays;
public class XenServerStorageResource { public class XenServerStorageResource {
private static final Logger s_logger = Logger.getLogger(XenServerStorageResource.class); private static final Logger s_logger = Logger.getLogger(XenServerStorageResource.class);
protected CitrixResourceBase hypervisorResource; protected CitrixResourceBase hypervisorResource;
@ -146,12 +160,62 @@ public class XenServerStorageResource {
return new CreateObjectAnswer(cmd, templateUrl, size);*/ return new CreateObjectAnswer(cmd, templateUrl, size);*/
return null; return null;
} }
protected CreateObjectAnswer createSnapshot(SnapshotObjectTO snapshotTO) {
Connection conn = hypervisorResource.getConnection();
long snapshotId = snapshotTO.getId();
String snapshotName = snapshotTO.getName();
String details = "create snapshot operation Failed for snapshotId: " + snapshotId;
String snapshotUUID = null;
try {
String volumeUUID = snapshotTO.getVolume().getPath();
VDI volume = VDI.getByUuid(conn, volumeUUID);
VDI snapshot = volume.snapshot(conn, new HashMap<String, String>());
if (snapshotName != null) {
snapshot.setNameLabel(conn, snapshotName);
}
snapshotUUID = snapshot.getUuid(conn);
String preSnapshotUUID = snapshotTO.getPath();
//check if it is a empty snapshot
if( preSnapshotUUID != null) {
SR sr = volume.getSR(conn);
String srUUID = sr.getUuid(conn);
String type = sr.getType(conn);
Boolean isISCSI = IsISCSI(type);
String snapshotParentUUID = getVhdParent(conn, srUUID, snapshotUUID, isISCSI);
String preSnapshotParentUUID = getVhdParent(conn, srUUID, preSnapshotUUID, isISCSI);
if( snapshotParentUUID != null && snapshotParentUUID.equals(preSnapshotParentUUID)) {
// this is empty snapshot, remove it
snapshot.destroy(conn);
snapshotUUID = preSnapshotUUID;
}
}
SnapshotObjectTO newSnapshot = new SnapshotObjectTO();
newSnapshot.setPath(snapshotUUID);
return new CreateObjectAnswer(newSnapshot);
} catch (XenAPIException e) {
details += ", reason: " + e.toString();
s_logger.warn(details, e);
} catch (Exception e) {
details += ", reason: " + e.toString();
s_logger.warn(details, e);
}
return new CreateObjectAnswer(details);
}
protected CreateObjectAnswer execute(CreateObjectCommand cmd) { protected CreateObjectAnswer execute(CreateObjectCommand cmd) {
DataTO data = cmd.getData(); DataTO data = cmd.getData();
try { try {
if (data.getObjectType() == DataObjectType.VOLUME) { if (data.getObjectType() == DataObjectType.VOLUME) {
return createVolume(data); return createVolume(data);
} } else if (data.getObjectType() == DataObjectType.SNAPSHOT) {
return createSnapshot((SnapshotObjectTO)data);
}
return new CreateObjectAnswer("not supported type"); return new CreateObjectAnswer("not supported type");
} catch (Exception e) { } catch (Exception e) {
s_logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString()); s_logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString());
@ -821,6 +885,330 @@ public class XenServerStorageResource {
return new CopyCmdAnswer("unsupported protocol"); return new CopyCmdAnswer("unsupported protocol");
} }
boolean swiftUpload(Connection conn, SwiftTO swift, String container, String ldir, String lfilename, Boolean isISCSI, int wait) {
String result = null;
try {
result = hypervisorResource.callHostPluginAsync(conn, "swiftxen", "swift", wait,
"op", "upload", "url", swift.getUrl(), "account", swift.getAccount(),
"username", swift.getUserName(), "key", swift.getKey(), "container", container,
"ldir", ldir, "lfilename", lfilename, "isISCSI", isISCSI.toString());
if( result != null && result.equals("true")) {
return true;
}
} catch (Exception e) {
s_logger.warn("swift upload failed due to " + e.toString(), e);
}
return false;
}
protected String deleteSnapshotBackup(Connection conn, String path, String secondaryStorageMountPath, String backupUUID) {
// If anybody modifies the formatting below again, I'll skin them
String result = hypervisorResource.callHostPlugin(conn, "vmopsSnapshot", "deleteSnapshotBackup", "backupUUID", backupUUID, "path", path, "secondaryStorageMountPath", secondaryStorageMountPath);
return result;
}
public void swiftBackupSnapshot(Connection conn, SwiftTO swift, String srUuid, String snapshotUuid, String container, Boolean isISCSI, int wait) {
String lfilename;
String ldir;
if ( isISCSI ) {
ldir = "/dev/VG_XenStorage-" + srUuid;
lfilename = "VHD-" + snapshotUuid;
} else {
ldir = "/var/run/sr-mount/" + srUuid;
lfilename = snapshotUuid + ".vhd";
}
swiftUpload(conn, swift, container, ldir, lfilename, isISCSI, wait);
}
private static List<String> serializeProperties(final Object object,
final Class<?> propertySet) {
assert object != null;
assert propertySet != null;
assert propertySet.isAssignableFrom(object.getClass());
try {
final BeanInfo beanInfo = Introspector.getBeanInfo(propertySet);
final PropertyDescriptor[] descriptors = beanInfo
.getPropertyDescriptors();
final List<String> serializedProperties = new ArrayList<String>();
for (final PropertyDescriptor descriptor : descriptors) {
serializedProperties.add(descriptor.getName());
final Object value = descriptor.getReadMethod().invoke(object);
serializedProperties.add(value != null ? value.toString()
: "null");
}
return Collections.unmodifiableList(serializedProperties);
} catch (IntrospectionException e) {
s_logger.warn(
"Ignored IntrospectionException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (IllegalArgumentException e) {
s_logger.warn(
"Ignored IllegalArgumentException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (IllegalAccessException e) {
s_logger.warn(
"Ignored IllegalAccessException when serializing class "
+ object.getClass().getCanonicalName(), e);
} catch (InvocationTargetException e) {
s_logger.warn(
"Ignored InvocationTargetException when serializing class "
+ object.getClass().getCanonicalName(), e);
}
return Collections.emptyList();
}
private boolean backupSnapshotToS3(final Connection connection,
final S3TO s3, final String srUuid, final String snapshotUuid,
final Boolean iSCSIFlag, final int wait) {
final String filename = iSCSIFlag ? "VHD-" + snapshotUuid
: snapshotUuid + ".vhd";
final String dir = (iSCSIFlag ? "/dev/VG_XenStorage-"
: "/var/run/sr-mount/") + srUuid;
final String key = StringUtils.join("/", "snapshots", snapshotUuid);
try {
final List<String> parameters = new ArrayList<String>(
serializeProperties(s3, S3Utils.ClientOptions.class));
parameters.addAll(Arrays.asList("operation", "put", "directory",
dir, "filename", filename, "iSCSIFlag",
iSCSIFlag.toString(), "key", key));
final String result = hypervisorResource.callHostPluginAsync(connection, "s3xen",
"s3", wait,
parameters.toArray(new String[parameters.size()]));
if (result != null && result.equals("true")) {
return true;
}
} catch (Exception e) {
s_logger.error(String.format(
"S3 upload failed of snapshot %1$s due to %2$s.",
snapshotUuid, e.toString()), e);
}
return false;
}
protected String backupSnapshot(Connection conn, String primaryStorageSRUuid, String path, String secondaryStorageMountPath, String snapshotUuid, String prevBackupUuid, Boolean isISCSI, int wait) {
String backupSnapshotUuid = null;
if (prevBackupUuid == null) {
prevBackupUuid = "";
}
// Each argument is put in a separate line for readability.
// Using more lines does not harm the environment.
String backupUuid = UUID.randomUUID().toString();
String results = hypervisorResource.callHostPluginAsync(conn, "vmopsSnapshot", "backupSnapshot", wait,
"primaryStorageSRUuid", primaryStorageSRUuid, "path", path, "secondaryStorageMountPath", secondaryStorageMountPath,
"snapshotUuid", snapshotUuid, "prevBackupUuid", prevBackupUuid, "backupUuid", backupUuid, "isISCSI", isISCSI.toString());
String errMsg = null;
if (results == null || results.isEmpty()) {
errMsg = "Could not copy backupUuid: " + backupSnapshotUuid
+ " from primary storage " + primaryStorageSRUuid + " to secondary storage "
+ secondaryStorageMountPath + " due to null";
} else {
String[] tmp = results.split("#");
String status = tmp[0];
backupSnapshotUuid = tmp[1];
// status == "1" if and only if backupSnapshotUuid != null
// So we don't rely on status value but return backupSnapshotUuid as an
// indicator of success.
if (status != null && status.equalsIgnoreCase("1") && backupSnapshotUuid != null) {
s_logger.debug("Successfully copied backupUuid: " + backupSnapshotUuid
+ " to secondary storage");
return backupSnapshotUuid;
} else {
errMsg = "Could not copy backupUuid: " + backupSnapshotUuid
+ " from primary storage " + primaryStorageSRUuid + " to secondary storage "
+ secondaryStorageMountPath + " due to " + tmp[1];
}
}
String source = backupUuid + ".vhd";
hypervisorResource.killCopyProcess(conn, source);
s_logger.warn(errMsg);
return null;
}
private boolean destroySnapshotOnPrimaryStorageExceptThis(Connection conn, String volumeUuid, String avoidSnapshotUuid){
try {
VDI volume = getVDIbyUuid(conn, volumeUuid);
if (volume == null) {
throw new InternalErrorException("Could not destroy snapshot on volume " + volumeUuid + " due to can not find it");
}
Set<VDI> snapshots = volume.getSnapshots(conn);
for( VDI snapshot : snapshots ) {
try {
if(! snapshot.getUuid(conn).equals(avoidSnapshotUuid)) {
snapshot.destroy(conn);
}
} catch (Exception e) {
String msg = "Destroying snapshot: " + snapshot+ " on primary storage failed due to " + e.toString();
s_logger.warn(msg, e);
}
}
s_logger.debug("Successfully destroyed snapshot on volume: " + volumeUuid + " execept this current snapshot "+ avoidSnapshotUuid );
return true;
} catch (XenAPIException e) {
String msg = "Destroying snapshot on volume: " + volumeUuid + " execept this current snapshot "+ avoidSnapshotUuid + " failed due to " + e.toString();
s_logger.error(msg, e);
} catch (Exception e) {
String msg = "Destroying snapshot on volume: " + volumeUuid + " execept this current snapshot "+ avoidSnapshotUuid + " failed due to " + e.toString();
s_logger.warn(msg, e);
}
return false;
}
protected Answer backupSnasphot(DataTO srcData, DataTO destData, DataTO cacheData, int wait) {
Connection conn = hypervisorResource.getConnection();
PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)srcData.getDataStore();
String primaryStorageNameLabel = primaryStore.getUuid();
String secondaryStorageUrl = null;
NfsTO cacheStore = null;
String destPath = null;
if (cacheData != null) {
cacheStore = (NfsTO)cacheData.getDataStore();
secondaryStorageUrl = cacheStore.getUrl();
destPath = cacheData.getPath();
} else {
cacheStore = (NfsTO)destData.getDataStore();
secondaryStorageUrl = cacheStore.getUrl();
destPath = destData.getPath();
}
SnapshotObjectTO snapshotTO = (SnapshotObjectTO)srcData;
SnapshotObjectTO snapshotOnImage = (SnapshotObjectTO)destData;
String snapshotUuid = snapshotTO.getPath();
String prevBackupUuid = snapshotOnImage.getParentSnapshotPath();
String prevSnapshotUuid = snapshotTO.getParentSnapshotPath();
// By default assume failure
String details = null;
String snapshotBackupUuid = null;
boolean fullbackup = true;
try {
SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel);
if (primaryStorageSR == null) {
throw new InternalErrorException("Could not backup snapshot because the primary Storage SR could not be created from the name label: " + primaryStorageNameLabel);
}
String psUuid = primaryStorageSR.getUuid(conn);
Boolean isISCSI = IsISCSI(primaryStorageSR.getType(conn));
VDI snapshotVdi = getVDIbyUuid(conn, snapshotUuid);
String snapshotPaUuid = null;
if ( prevBackupUuid != null ) {
try {
snapshotPaUuid = getVhdParent(conn, psUuid, snapshotUuid, isISCSI);
if( snapshotPaUuid != null ) {
String snashotPaPaPaUuid = getVhdParent(conn, psUuid, snapshotPaUuid, isISCSI);
String prevSnashotPaUuid = getVhdParent(conn, psUuid, prevSnapshotUuid, isISCSI);
if (snashotPaPaPaUuid != null && prevSnashotPaUuid!= null && prevSnashotPaUuid.equals(snashotPaPaPaUuid)) {
fullbackup = false;
}
}
} catch (Exception e) {
}
}
URI uri = new URI(secondaryStorageUrl);
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
DataStoreTO destStore = destData.getDataStore();
String folder = destPath;
if (fullbackup) {
// the first snapshot is always a full snapshot
if( !hypervisorResource.createSecondaryStorageFolder(conn, secondaryStorageMountPath, folder)) {
details = " Filed to create folder " + folder + " in secondary storage";
s_logger.warn(details);
return new CopyCmdAnswer(details);
}
String snapshotMountpoint = secondaryStorageUrl + "/" + folder;
SR snapshotSr = null;
try {
snapshotSr = hypervisorResource.createNfsSRbyURI(conn, new URI(snapshotMountpoint), false);
VDI backedVdi = hypervisorResource.cloudVDIcopy(conn, snapshotVdi, snapshotSr, wait);
snapshotBackupUuid = backedVdi.getUuid(conn);
if( destStore instanceof SwiftTO) {
try {
hypervisorResource.swiftBackupSnapshot(conn, (SwiftTO)destStore, snapshotSr.getUuid(conn), snapshotBackupUuid, "S-" + snapshotTO.getVolume().getVolumeId().toString(), false, wait);
snapshotBackupUuid = snapshotBackupUuid + ".vhd";
} finally {
deleteSnapshotBackup(conn, folder, secondaryStorageMountPath, snapshotBackupUuid);
}
} else if (destStore instanceof S3TO) {
try {
backupSnapshotToS3(conn, (S3TO)destStore, snapshotSr.getUuid(conn), snapshotBackupUuid, isISCSI, wait);
snapshotBackupUuid = snapshotBackupUuid + ".vhd";
} finally {
deleteSnapshotBackup(conn, folder, secondaryStorageMountPath, snapshotBackupUuid);
}
}
} finally {
if( snapshotSr != null) {
hypervisorResource.removeSR(conn, snapshotSr);
}
}
} else {
String primaryStorageSRUuid = primaryStorageSR.getUuid(conn);
if( destStore instanceof SwiftTO ) {
swiftBackupSnapshot(conn, (SwiftTO)destStore, primaryStorageSRUuid, snapshotPaUuid, "S-" + snapshotTO.getVolume().getVolumeId().toString(), isISCSI, wait);
if ( isISCSI ) {
snapshotBackupUuid = "VHD-" + snapshotPaUuid;
} else {
snapshotBackupUuid = snapshotPaUuid + ".vhd";
}
} else if (destStore instanceof S3TO ) {
backupSnapshotToS3(conn, (S3TO)destStore, primaryStorageSRUuid, snapshotPaUuid, isISCSI, wait);
} else {
snapshotBackupUuid = backupSnapshot(conn, primaryStorageSRUuid, folder + File.separator + UUID.nameUUIDFromBytes(secondaryStorageMountPath.getBytes())
, secondaryStorageMountPath, snapshotUuid, prevBackupUuid, isISCSI, wait);
}
}
String volumeUuid = snapshotTO.getVolume().getPath();
destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid);
SnapshotObjectTO newSnapshot = new SnapshotObjectTO();
newSnapshot.setPath(snapshotBackupUuid);
if (fullbackup) {
newSnapshot.setParentSnapshotPath(null);
} else {
newSnapshot.setParentSnapshotPath(prevBackupUuid);
}
return new CopyCmdAnswer(newSnapshot);
} catch (XenAPIException e) {
details = "BackupSnapshot Failed due to " + e.toString();
s_logger.warn(details, e);
} catch (Exception e) {
details = "BackupSnapshot Failed due to " + e.getMessage();
s_logger.warn(details, e);
}
return new CopyCmdAnswer(details);
}
protected Answer execute(CopyCommand cmd) { protected Answer execute(CopyCommand cmd) {
DataTO srcData = cmd.getSrcTO(); DataTO srcData = cmd.getSrcTO();
DataTO destData = cmd.getDestTO(); DataTO destData = cmd.getDestTO();
@ -838,6 +1226,9 @@ public class XenServerStorageResource {
return copyVolumeFromImageCacheToPrimary(srcData, destData, cmd.getWait()); return copyVolumeFromImageCacheToPrimary(srcData, destData, cmd.getWait());
} else if (srcData.getObjectType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) { } else if (srcData.getObjectType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) {
return copyVolumeFromPrimaryToSecondary(srcData, destData, cmd.getWait()); return copyVolumeFromPrimaryToSecondary(srcData, destData, cmd.getWait());
} else if (srcData.getObjectType() == DataObjectType.SNAPSHOT && srcData.getDataStore().getRole() == DataStoreRole.Primary) {
DataTO cacheData = cmd.getCacheTO();
return backupSnasphot(srcData, destData, cacheData, cmd.getWait());
} }
return new Answer(cmd, false, "not implemented yet"); return new Answer(cmd, false, "not implemented yet");

View File

@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.command.CreateObjectCommand;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.cloudstack.storage.volume.VolumeObject;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -193,24 +194,17 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
AsyncCompletionCallback<CreateCmdResult> callback) { AsyncCompletionCallback<CreateCmdResult> callback) {
CreateCmdResult result = null; CreateCmdResult result = null;
try { try {
VolumeInfo volume = snapshot.getBaseVolume(); DataTO snapshotTO = snapshot.getTO();
String vmName = this.volumeMgr.getVmNameOnVolume(volume);
SnapshotVO preSnapshotVO = this.snapshotMgr.getParentSnapshot(volume, snapshot); CreateObjectCommand cmd = new CreateObjectCommand(snapshotTO);
String parentSnapshotPath = null; Answer answer = storageMgr.sendToPool((StoragePool)snapshot.getDataStore(), null, cmd);
if (preSnapshotVO != null) {
parentSnapshotPath = preSnapshotVO.getPath(); result = new CreateCmdResult(null, answer);
if (answer != null && !answer.getResult()) {
result.setResult(answer.getDetails());
} }
StoragePool srcPool = (StoragePool)volume.getDataStore();
ManageSnapshotCommand cmd = new ManageSnapshotCommand(snapshot.getId(), volume.getPath(), srcPool, parentSnapshotPath, snapshot.getName(), vmName); callback.complete(result);
ManageSnapshotAnswer answer = (ManageSnapshotAnswer) this.snapshotMgr.sendToPool(volume, cmd);
if ((answer != null) && answer.getResult()) {
result = new CreateCmdResult(answer.getSnapshotPath(), null);
} else {
result = new CreateCmdResult(null, null);
}
} catch (Exception e) { } catch (Exception e) {
s_logger.debug("Failed to take snapshot: " + snapshot.getId(), e); s_logger.debug("Failed to take snapshot: " + snapshot.getId(), e);
result = new CreateCmdResult(null, null); result = new CreateCmdResult(null, null);

View File

@ -1,7 +1,11 @@
package com.cloud.storage; package com.cloud.storage;
import com.cloud.user.Account;
public class CreateSnapshotPayload { public class CreateSnapshotPayload {
private Long snapshotPolicyId; private Long snapshotPolicyId;
private Long snapshotId;
private Account account;
public Long getSnapshotPolicyId() { public Long getSnapshotPolicyId() {
return snapshotPolicyId; return snapshotPolicyId;
@ -11,4 +15,20 @@ public class CreateSnapshotPayload {
this.snapshotPolicyId = snapshotPolicyId; this.snapshotPolicyId = snapshotPolicyId;
} }
public Long getSnapshotId() {
return snapshotId;
}
public void setSnapshotId(Long snapshotId) {
this.snapshotId = snapshotId;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
} }

View File

@ -133,6 +133,7 @@ import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.download.DownloadMonitor; import com.cloud.storage.download.DownloadMonitor;
import com.cloud.storage.s3.S3Manager; import com.cloud.storage.s3.S3Manager;
import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.storage.secondary.SecondaryStorageVmManager;
import com.cloud.storage.snapshot.SnapshotApiService;
import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.storage.snapshot.SnapshotManager;
import com.cloud.storage.snapshot.SnapshotScheduler; import com.cloud.storage.snapshot.SnapshotScheduler;
import com.cloud.tags.dao.ResourceTagDao; import com.cloud.tags.dao.ResourceTagDao;
@ -307,6 +308,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
TemplateDataFactory tmplFactory; TemplateDataFactory tmplFactory;
@Inject @Inject
SnapshotDataFactory snapshotFactory; SnapshotDataFactory snapshotFactory;
@Inject
SnapshotApiService snapshotMgr;
private int _copyvolumewait; private int _copyvolumewait;
@Inject @Inject
protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
@ -526,7 +529,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
VolumeInfo vol = this.volFactory.getVolume(volume.getId()); VolumeInfo vol = this.volFactory.getVolume(volume.getId());
DataStore store = this.dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); DataStore store = this.dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
SnapshotInfo snapInfo = this.snapshotFactory.getSnapshot(snapshot.getId()); SnapshotInfo snapInfo = this.snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image);
AsyncCallFuture<VolumeApiResult> future = this.volService.createVolumeFromSnapshot(vol, store, snapInfo); AsyncCallFuture<VolumeApiResult> future = this.volService.createVolumeFromSnapshot(vol, store, snapInfo);
try { try {
VolumeApiResult result = future.get(); VolumeApiResult result = future.get();
@ -2470,8 +2473,26 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
@Override @Override
public Snapshot takeSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException { public Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account) throws ResourceAllocationException {
Account caller = UserContext.current().getCaller(); VolumeInfo volume = this.volFactory.getVolume(volumeId);
if (volume == null) {
throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist");
}
if (volume.getState() != Volume.State.Ready) {
throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot.");
}
CreateSnapshotPayload payload = new CreateSnapshotPayload();
payload.setSnapshotId(snapshotId);
payload.setSnapshotPolicyId(policyId);
payload.setAccount(account);
return this.volService.takeSnapshot(volume);
}
@Override
public Snapshot allocSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException {
Account caller = UserContext.current().getCaller();
VolumeInfo volume = this.volFactory.getVolume(volumeId); VolumeInfo volume = this.volFactory.getVolume(volumeId);
if (volume == null) { if (volume == null) {
@ -2502,10 +2523,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
throw new InvalidParameterValueException("VolumeId: " + volumeId + " please attach this volume to a VM before create snapshot for it"); throw new InvalidParameterValueException("VolumeId: " + volumeId + " please attach this volume to a VM before create snapshot for it");
} }
CreateSnapshotPayload payload = new CreateSnapshotPayload(); return this.snapshotMgr.allocSnapshot(volumeId, policyId);
payload.setSnapshotPolicyId(policyId);
return this.volService.takeSnapshot(volume);
} }
} }

View File

@ -151,7 +151,7 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
VolumeIdVersionSearch.and("volumeId", VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); VolumeIdVersionSearch.and("volumeId", VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.and("version", VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ); VolumeIdVersionSearch.and("version", VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.done(); VolumeIdVersionSearch.done();
/*
ParentIdSearch = createSearchBuilder(); ParentIdSearch = createSearchBuilder();
ParentIdSearch.and("prevSnapshotId", ParentIdSearch.entity().getPrevSnapshotId(), SearchCriteria.Op.EQ); ParentIdSearch.and("prevSnapshotId", ParentIdSearch.entity().getPrevSnapshotId(), SearchCriteria.Op.EQ);
ParentIdSearch.done(); ParentIdSearch.done();
@ -159,7 +159,7 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
backupUuidSearch = createSearchBuilder(); backupUuidSearch = createSearchBuilder();
backupUuidSearch.and("backupUuid", backupUuidSearch.entity().getBackupSnapshotId(), SearchCriteria.Op.EQ); backupUuidSearch.and("backupUuid", backupUuidSearch.entity().getBackupSnapshotId(), SearchCriteria.Op.EQ);
backupUuidSearch.done(); backupUuidSearch.done();
*/
AccountIdSearch = createSearchBuilder(); AccountIdSearch = createSearchBuilder();
AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
AccountIdSearch.done(); AccountIdSearch.done();

View File

@ -56,10 +56,6 @@ public interface SnapshotManager {
* The account which is to be deleted. * The account which is to be deleted.
*/ */
boolean deleteSnapshotDirsForAccount(long accountId); boolean deleteSnapshotDirsForAccount(long accountId);
void downloadSnapshotsFromSwift(SnapshotVO ss);
void downloadSnapshotsFromS3(SnapshotVO snapshot);
String getSecondaryStorageURL(SnapshotVO snapshot); String getSecondaryStorageURL(SnapshotVO snapshot);

View File

@ -349,8 +349,10 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
return this.snapshotSrv.backupSnapshot(snapshot); return this.snapshotSrv.backupSnapshot(snapshot);
} }
/*
@Override @Override
public void downloadSnapshotsFromSwift(SnapshotVO ss) { public void downloadSnapshotsFromSwift(SnapshotVO ss) {
long volumeId = ss.getVolumeId(); long volumeId = ss.getVolumeId();
VolumeVO volume = _volsDao.findById(volumeId); VolumeVO volume = _volsDao.findById(volumeId);
Long dcId = volume.getDataCenterId(); Long dcId = volume.getDataCenterId();
@ -432,7 +434,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
e); e);
} }
} }*/
@Override @Override
public SnapshotVO getParentSnapshot(VolumeInfo volume) { public SnapshotVO getParentSnapshot(VolumeInfo volume) {
@ -974,72 +976,44 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
} }
@Override @Override
public SnapshotInfo takeSnapshot(VolumeInfo volume) throws ResourceAllocationException { public SnapshotInfo takeSnapshot(VolumeInfo volume) throws ResourceAllocationException {
Account caller = UserContext.current().getCaller(); CreateSnapshotPayload payload = (CreateSnapshotPayload)volume.getpayload();
Long snapshotId = payload.getSnapshotId();
supportedByHypervisor(volume); Account snapshotOwner = payload.getAccount();
CreateSnapshotPayload snapInfo = (CreateSnapshotPayload)volume.getpayload(); SnapshotInfo snapshot = this.snapshotFactory.getSnapshot(snapshotId, volume.getDataStore());
Long policyId = snapInfo.getSnapshotPolicyId();
// Verify permissions
_accountMgr.checkAccess(caller, null, true, volume);
Type snapshotType = getSnapshotType(policyId);
Account owner = _accountMgr.getAccount(volume.getAccountId());
try{
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot);
if (backup) {
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.secondary_storage, new Long(volume.getSize()));
} else {
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, new Long(volume.getSize()));
}
} catch (ResourceAllocationException e) {
if (snapshotType != Type.MANUAL){
String msg = "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots";
s_logger.warn(msg);
_alertMgr.sendAlert(AlertManager.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg,
"Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots; please use updateResourceLimit to increase the limit");
}
throw e;
}
// Determine the name for this snapshot
// Snapshot Name: VMInstancename + volumeName + timeString
String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT);
VMInstanceVO vmInstance = _vmDao.findById(volume.getInstanceId());
String vmDisplayName = "detached";
if (vmInstance != null) {
vmDisplayName = vmInstance.getHostName();
}
String snapshotName = vmDisplayName + "_" + volume.getName() + "_" + timeString;
HypervisorType hypervisorType = volume.getHypervisorType();
SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName,
(short) snapshotType.ordinal(), snapshotType.name(), volume.getSize(), hypervisorType);
SnapshotVO snapshot = _snapshotDao.persist(snapshotVO);
if (snapshot == null) {
throw new CloudRuntimeException("Failed to create snapshot for volume: " + volume.getId());
}
if (backup) {
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage,
new Long(volume.getSize()));
} else {
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage,
new Long(volume.getSize()));
}
SnapshotInfo snap = this.snapshotFactory.getSnapshot(snapshot.getId(), volume.getDataStore());
boolean processed = false; boolean processed = false;
for (SnapshotStrategy strategy : snapshotStrategies) {
if (strategy.canHandle(snap)) { try {
processed = true; for (SnapshotStrategy strategy : snapshotStrategies) {
snap = strategy.takeSnapshot(snap); if (strategy.canHandle(snapshot)) {
break; processed = true;
} snapshot = strategy.takeSnapshot(snapshot);
break;
}
}
if (!processed) {
throw new CloudRuntimeException("Can't find snapshot strategy to deal with snapshot:" + snapshotId);
}
postCreateSnapshot(volume.getId(), snapshotId, payload.getSnapshotPolicyId());
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(),
snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null,
volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid());
_resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot);
} catch(Exception e) {
s_logger.debug("Failed to create snapshot", e);
if (backup) {
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.secondary_storage,
new Long(volume.getSize()));
} else {
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.primary_storage,
new Long(volume.getSize()));
}
throw new CloudRuntimeException("Failed to create snapshot", e);
} }
if (!processed) { return snapshot;
throw new CloudRuntimeException("Can't find snapshot strategy to deal with snapshot:" + snapshot.getId());
}
return snap;
} }
@Override @Override
@ -1128,4 +1102,61 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
} }
return true; return true;
} }
@Override
public Snapshot allocSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException {
Account caller = UserContext.current().getCaller();
VolumeInfo volume = this.volFactory.getVolume(volumeId);
supportedByHypervisor(volume);
// Verify permissions
_accountMgr.checkAccess(caller, null, true, volume);
Type snapshotType = getSnapshotType(policyId);
Account owner = _accountMgr.getAccount(volume.getAccountId());
try{
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot);
if (backup) {
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.secondary_storage, new Long(volume.getSize()));
} else {
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, new Long(volume.getSize()));
}
} catch (ResourceAllocationException e) {
if (snapshotType != Type.MANUAL){
String msg = "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots";
s_logger.warn(msg);
_alertMgr.sendAlert(AlertManager.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg,
"Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots; please use updateResourceLimit to increase the limit");
}
throw e;
}
// Determine the name for this snapshot
// Snapshot Name: VMInstancename + volumeName + timeString
String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT);
VMInstanceVO vmInstance = _vmDao.findById(volume.getInstanceId());
String vmDisplayName = "detached";
if (vmInstance != null) {
vmDisplayName = vmInstance.getHostName();
}
String snapshotName = vmDisplayName + "_" + volume.getName() + "_" + timeString;
HypervisorType hypervisorType = volume.getHypervisorType();
SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName,
(short) snapshotType.ordinal(), snapshotType.name(), volume.getSize(), hypervisorType);
SnapshotVO snapshot = _snapshotDao.persist(snapshotVO);
if (snapshot == null) {
throw new CloudRuntimeException("Failed to create snapshot for volume: " + volume.getId());
}
if (backup) {
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage,
new Long(volume.getSize()));
} else {
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage,
new Long(volume.getSize()));
}
return snapshot;
}
} }

View File

@ -1355,7 +1355,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
} }
AsyncCallFuture<TemplateApiResult> future = null; AsyncCallFuture<TemplateApiResult> future = null;
if (snapshotId != null) { if (snapshotId != null) {
SnapshotInfo snapInfo = this._snapshotFactory.getSnapshot(snapshotId); SnapshotInfo snapInfo = this._snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image);
future = this._tmpltSvr.createTemplateFromSnapshotAsync(snapInfo, tmplInfo, store.get(0)); future = this._tmpltSvr.createTemplateFromSnapshotAsync(snapInfo, tmplInfo, store.get(0));
} else if (volumeId != null) { } else if (volumeId != null) {
volume = _volumeDao.findById(volumeId); volume = _volumeDao.findById(volumeId);