bug 7976: if the snapshot is empty, still create a snapshot entry which has the same backupSnapshotId even though nothing is backed up to secondary storage

status 7976: resolved fixed
This commit is contained in:
anthony 2011-01-20 18:11:20 -08:00
parent 8739a93f3a
commit cd0f5e7c20
6 changed files with 85 additions and 49 deletions

View File

@ -1080,7 +1080,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
String result = command.execute();
if (result != null) {
s_logger.debug("Failed to backup snaptshot: " + result);
return new BackupSnapshotAnswer(cmd, false, result, null);
return new BackupSnapshotAnswer(cmd, false, result, null, true);
}
/*Delete the snapshot on primary*/
@ -1116,15 +1116,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
result = command.execute();
if (result != null) {
s_logger.debug("Failed to backup snapshot: " + result);
return new BackupSnapshotAnswer(cmd, false, "Failed to backup snapshot: " + result, null);
return new BackupSnapshotAnswer(cmd, false, "Failed to backup snapshot: " + result, null, true);
}
}
} catch (LibvirtException e) {
return new BackupSnapshotAnswer(cmd, false, e.toString(), null);
return new BackupSnapshotAnswer(cmd, false, e.toString(), null, true);
} catch (URISyntaxException e) {
return new BackupSnapshotAnswer(cmd, false, e.toString(), null);
return new BackupSnapshotAnswer(cmd, false, e.toString(), null, true);
}
return new BackupSnapshotAnswer(cmd, true, null, snapshotDestPath + File.separator + snapshotName);
return new BackupSnapshotAnswer(cmd, true, null, snapshotDestPath + File.separator + snapshotName, true);
}
protected DeleteSnapshotBackupAnswer execute(final DeleteSnapshotBackupCommand cmd) {

View File

@ -20,14 +20,16 @@ package com.cloud.agent.api;
public class BackupSnapshotAnswer extends Answer {
private String backupSnapshotName;
private boolean full;
protected BackupSnapshotAnswer() {
}
public BackupSnapshotAnswer(BackupSnapshotCommand cmd, boolean success, String result, String backupSnapshotName) {
public BackupSnapshotAnswer(BackupSnapshotCommand cmd, boolean success, String result, String backupSnapshotName, boolean full) {
super(cmd, success, result);
this.backupSnapshotName = backupSnapshotName;
this.full = full;
}
/**
@ -36,4 +38,8 @@ public class BackupSnapshotAnswer extends Answer {
public String getBackupSnapshotName() {
return backupSnapshotName;
}
public boolean isFull() {
return full;
}
}

View File

@ -5013,10 +5013,12 @@ public abstract class CitrixResourceBase implements ServerResource {
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
String snapshotUuid = cmd.getSnapshotUuid(); // not null: Precondition.
String prevBackupUuid = cmd.getPrevBackupUuid();
String prevSnapshotUuid = cmd.getPrevSnapshotUuid();
// By default assume failure
String details = null;
boolean success = false;
String snapshotBackupUuid = null;
boolean fullbackup = true;
try {
SR primaryStorageSR = getSRByNameLabelandHost(conn, primaryStorageNameLabel);
if (primaryStorageSR == null) {
@ -5025,22 +5027,30 @@ public abstract class CitrixResourceBase implements ServerResource {
URI uri = new URI(secondaryStoragePoolURL);
String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
VDI snapshotVdi = getVDIbyUuid(conn, snapshotUuid);
if (prevBackupUuid == null) {
if ( prevBackupUuid != null ) {
try {
VDI preSnapshotVdi = getVDIbyUuid(conn, prevSnapshotUuid);
if ( snapshotVdi.getParent(conn).getParent(conn) == preSnapshotVdi.getParent(conn) ) {
fullbackup = false;
}
} catch (Exception e) {
}
}
if (fullbackup) {
// the first snapshot is always a full snapshot
String folder = "snapshots/" + accountId + "/" + volumeId;
if( !createSecondaryStorageFolder(conn, secondaryStorageMountPath, folder)) {
details = " Filed to create folder " + folder + " in secondary storage";
s_logger.warn(details);
return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid);
return new BackupSnapshotAnswer(cmd, false, details, null, false);
}
String snapshotMountpoint = secondaryStoragePoolURL + "/" + folder;
SR snapshotSr = null;
try {
snapshotSr = createNfsSRbyURI(conn, new URI(snapshotMountpoint), false);
VDI snapshotVdi = getVDIbyUuid(conn, snapshotUuid);
VDI backedVdi = cloudVDIcopy(conn, snapshotVdi, snapshotSr);
snapshotBackupUuid = backedVdi.getUuid(conn);
success = true;
@ -5050,21 +5060,16 @@ public abstract class CitrixResourceBase implements ServerResource {
}
}
} else {
String primaryStorageSRUuid = primaryStorageSR.getUuid(conn);
Boolean isISCSI = IsISCSI(primaryStorageSR.getType(conn));
snapshotBackupUuid = backupSnapshot(conn, primaryStorageSRUuid, dcId, accountId, volumeId, secondaryStorageMountPath,
snapshotUuid, prevBackupUuid, isISCSI);
success = (snapshotBackupUuid != null);
String primaryStorageSRUuid = primaryStorageSR.getUuid(conn);
Boolean isISCSI = IsISCSI(primaryStorageSR.getType(conn));
snapshotBackupUuid = backupSnapshot(conn, primaryStorageSRUuid, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotUuid, prevBackupUuid, isISCSI);
success = (snapshotBackupUuid != null);
}
if (success) {
details = "Successfully backedUp the snapshotUuid: " + snapshotUuid + " to secondary storage.";
String volumeUuid = cmd.getVolumePath();
destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid);
}
} catch (XenAPIException e) {
details = "BackupSnapshot Failed due to " + e.toString();
s_logger.warn(details, e);
@ -5073,7 +5078,7 @@ public abstract class CitrixResourceBase implements ServerResource {
s_logger.warn(details, e);
}
return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid);
return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid, fullbackup);
}
protected CreateVolumeFromSnapshotAnswer execute(final CreateVolumeFromSnapshotCommand cmd) {
@ -5168,7 +5173,7 @@ public abstract class CitrixResourceBase implements ServerResource {
}
}
}
return new DeleteSnapshotBackupAnswer(cmd, success, details);
return new DeleteSnapshotBackupAnswer(cmd, true, details);
}
protected Answer execute(DeleteSnapshotsDirCommand cmd) {

View File

@ -32,5 +32,6 @@ public interface SnapshotDao extends GenericDao<SnapshotVO, Long> {
long getLastSnapshot(long volumeId, long snapId);
List<SnapshotVO> listByVolumeIdType(long volumeId, String type);
List<SnapshotVO> listByVolumeIdIncludingRemoved(long volumeId);
List<SnapshotVO> listByBackupUuid(long volumeId, String backupUuid);
}

View File

@ -41,6 +41,8 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
private final SearchBuilder<SnapshotVO> VolumeIdSearch;
private final SearchBuilder<SnapshotVO> VolumeIdTypeSearch;
private final SearchBuilder<SnapshotVO> ParentIdSearch;
private final SearchBuilder<SnapshotVO> backupUuidSearch;
@Override
public SnapshotVO findNextSnapshot(long snapshotId) {
@ -49,6 +51,13 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
return findOneIncludingRemovedBy(sc);
}
@Override
public List<SnapshotVO> listByBackupUuid(long volumeId, String backupUuid) {
SearchCriteria<SnapshotVO> sc = backupUuidSearch.create();
sc.setParameters("backupUuid", backupUuid);
return listBy(sc, null);
}
@Override
public List<SnapshotVO> listByVolumeIdType(long volumeId, String type ) {
return listByVolumeIdType(null, volumeId, type);
@ -93,6 +102,10 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
ParentIdSearch = createSearchBuilder();
ParentIdSearch.and("prevSnapshotId", ParentIdSearch.entity().getPrevSnapshotId(), SearchCriteria.Op.EQ);
ParentIdSearch.done();
backupUuidSearch = createSearchBuilder();
backupUuidSearch.and("backupUuid", backupUuidSearch.entity().getBackupSnapshotId(), SearchCriteria.Op.EQ);
backupUuidSearch.done();
}

View File

@ -209,9 +209,6 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
if (storagePoolVO == null) {
throw new InvalidParameterValueException("VolumeId: " + volumeId + " does not have a valid storage pool. Is it destroyed?");
}
if (!isVolumeDirty(volumeId, policyId)) {
throw new CloudRuntimeException("There is no change for volume " + volumeId + " since last snapshot, please use the last snapshot instead.");
}
Long id = null;
@ -242,11 +239,10 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
long preId = _snapshotDao.getLastSnapshot(volumeId, id);
String preSnapshotPath = null;
SnapshotVO preSnapshotVO = null;
if( preId != 0) {
preSnapshotVO = _snapshotDao.findByIdIncludingRemoved(preId);
if (preSnapshotVO != null) {
if (preSnapshotVO != null && preSnapshotVO.getBackupSnapshotId() != null ) {
preSnapshotPath = preSnapshotVO.getPath();
}
}
@ -262,10 +258,11 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
//empty snapshot
s_logger.debug("CreateSnapshot: this is empty snapshot, remove it ");
createdSnapshot = _snapshotDao.findByIdIncludingRemoved(id);
// delete from the snapshots table
_snapshotDao.expunge(id);
throw new CloudRuntimeException("There is no change for volume " + volumeId + " since last snapshot, please use last snapshot instead.");
createdSnapshot.setPath(preSnapshotPath);
createdSnapshot.setBackupSnapshotId(preSnapshotVO.getBackupSnapshotId());
createdSnapshot.setStatus(Snapshot.Status.BackedUp);
createdSnapshot.setPrevSnapshotId(preId);
_snapshotDao.update(id, createdSnapshot);
} else {
long preSnapshotId = 0;
if( preSnapshotVO != null && preSnapshotVO.getBackupSnapshotId() != null) {
@ -292,16 +289,17 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
}
}
createdSnapshot = updateDBOnCreate(id, answer.getSnapshotPath(), preSnapshotId);
// Get the snapshot_schedule table entry for this snapshot and
// policy id.
// Set the snapshotId to retrieve it back later.
if( policyId != Snapshot.MANUAL_POLICY_ID) {
SnapshotScheduleVO snapshotSchedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, true);
assert snapshotSchedule != null;
snapshotSchedule.setSnapshotId(id);
_snapshotScheduleDao.update(snapshotSchedule.getId(), snapshotSchedule);
}
}
// Get the snapshot_schedule table entry for this snapshot and
// policy id.
// Set the snapshotId to retrieve it back later.
if (policyId != Snapshot.MANUAL_POLICY_ID) {
SnapshotScheduleVO snapshotSchedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, true);
assert snapshotSchedule != null;
snapshotSchedule.setSnapshotId(id);
_snapshotScheduleDao.update(snapshotSchedule.getId(), snapshotSchedule);
}
} else {
if (answer != null) {
s_logger.error(answer.getDetails());
@ -430,7 +428,6 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
@Override @DB
public boolean backupSnapshotToSecondaryStorage(SnapshotVO ss) {
Long userId = getSnapshotUserId();
long snapshotId = ss.getId();
SnapshotVO snapshot = _snapshotDao.acquireInLockTable(snapshotId);
if( snapshot == null) {
@ -460,8 +457,10 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
long prevSnapshotId = snapshot.getPrevSnapshotId();
if (prevSnapshotId > 0) {
prevSnapshot = _snapshotDao.findByIdIncludingRemoved(prevSnapshotId);
prevSnapshotUuid = prevSnapshot.getPath();
prevBackupUuid = prevSnapshot.getBackupSnapshotId();
if( prevBackupUuid != null ) {
prevSnapshotUuid = prevSnapshot.getPath();
}
}
boolean isVolumeInactive = _storageMgr.volumeInactive(volume);
@ -505,6 +504,9 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
if (backedUp) {
snapshot.setBackupSnapshotId(backedUpSnapshotUuid);
if( answer.isFull()) {
snapshot.setPrevSnapshotId(0);
}
snapshot.setStatus(Snapshot.Status.BackedUp);
_snapshotDao.update(snapshotId, snapshot);
UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), volume.getDataCenterId(), snapshotId, snapshot.getName(), null, null, volume.getSize());
@ -675,11 +677,17 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
while (lastSnapshot.getRemoved() != null) {
String BackupSnapshotId = lastSnapshot.getBackupSnapshotId();
if (BackupSnapshotId != null) {
if (destroySnapshotBackUp(lastId, policyId)) {
List<SnapshotVO> snaps = _snapshotDao.listByBackupUuid(lastSnapshot.getVolumeId(), BackupSnapshotId);
if ( snaps.size() > 1 ) {
lastSnapshot.setBackupSnapshotId(null);
_snapshotDao.update(lastSnapshot.getId(), lastSnapshot);
} else {
s_logger.debug("Destroying snapshot backup failed " + lastSnapshot);
break;
if (destroySnapshotBackUp(lastId, policyId)) {
} else {
s_logger.debug("Destroying snapshot backup failed " + lastSnapshot);
break;
}
}
}
postDeleteSnapshot(lastId, policyId);
@ -715,17 +723,20 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
Long volumeId = volume.getId();
String backupOfSnapshot = snapshot.getBackupSnapshotId();
if ( backupOfSnapshot == null ) {
return true;
}
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(primaryStoragePoolNameLabel,
secondaryStoragePoolUrl, dcId, accountId, volumeId, backupOfSnapshot, snapshot.getName());
snapshot.setBackupSnapshotId(null);
_snapshotDao.update(snapshotId, snapshot);
details = "Failed to destroy snapshot id:" + snapshotId + " for volume: " + volume.getId();
Answer answer = _storageMgr.sendToHostsOnStoragePool(volume.getPoolId(), cmd, details, _totalRetries,
_pauseInterval, _shouldBeSnapshotCapable, volume.getInstanceId());
if ((answer != null) && answer.getResult()) {
snapshot.setBackupSnapshotId(null);
_snapshotDao.update(snapshotId, snapshot);
// This is not the last snapshot.
success = true;
details = "Successfully deleted snapshot " + snapshotId + " for volumeId: " + volumeId + " and policyId "