CLOUDSTACK-8097: Failed to create volume snapshot after vm live migration across clusters.

This commit is contained in:
Sanjay Tripathi 2014-12-19 14:26:48 +05:30
parent 3090e4a030
commit 0c4128e024
6 changed files with 112 additions and 22 deletions

View File

@ -58,6 +58,7 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import org.apache.cloudstack.storage.command.CommandResult;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
@ -96,6 +97,7 @@ import com.cloud.storage.VMTemplateStorageResourceAssoc;
import com.cloud.storage.Volume;
import com.cloud.storage.Volume.Type;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.template.TemplateManager;
@ -142,6 +144,10 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
@Inject
protected VolumeDao _volumeDao;
@Inject
protected SnapshotDao _snapshotDao;
@Inject
protected SnapshotDataStoreDao _snapshotDataStoreDao;
@Inject
protected ResourceLimitService _resourceLimitMgr;
@Inject
VolumeDetailsDao _volDetailDao;
@ -919,8 +925,14 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
try {
VolumeApiResult result = future.get();
if (result.isFailed()) {
s_logger.error("migrate volume failed:" + result.getResult());
throw new StorageUnavailableException("migrate volume failed: " + result.getResult(), destPool.getId());
s_logger.error("Migrate volume failed:" + result.getResult());
throw new StorageUnavailableException("Migrate volume failed: " + result.getResult(), destPool.getId());
} else {
// update the volumeId for snapshots on secondary
if (!_snapshotDao.listByVolumeId(vol.getId()).isEmpty()) {
_snapshotDao.updateVolumeIds(vol.getId(), result.getVolume().getId());
_snapshotDataStoreDao.updateVolumeIds(vol.getId(), result.getVolume().getId());
}
}
return result.getVolume();
} catch (InterruptedException e) {

View File

@ -57,4 +57,6 @@ public interface SnapshotDao extends GenericDao<SnapshotVO, Long>, StateDao<Snap
List<SnapshotVO> listAllByStatus(Snapshot.State... status);
void updateVolumeIds(long oldVolId, long newVolId);
}

View File

@ -41,6 +41,7 @@ import com.cloud.utils.db.DB;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.GenericSearchBuilder;
import com.cloud.utils.db.UpdateBuilder;
import com.cloud.utils.db.JoinBuilder.JoinType;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
@ -326,4 +327,13 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
return true;
}
@Override
public void updateVolumeIds(long oldVolId, long newVolId) {
SearchCriteria<SnapshotVO> sc = VolumeIdSearch.create();
sc.setParameters("volumeId", oldVolId);
SnapshotVO snapshot = createForUpdate();
snapshot.setVolumeId(newVolId);
UpdateBuilder ub = getUpdateBuilder(snapshot);
update(ub, sc, null);
}
}

View File

@ -56,4 +56,8 @@ public interface SnapshotDataStoreDao extends GenericDao<SnapshotDataStoreVO, Lo
void updateStoreRoleToCache(long storeId);
SnapshotDataStoreVO findLatestSnapshotForVolume(Long volumeId, DataStoreRole role);
SnapshotDataStoreVO findOldestSnapshotForVolume(Long volumeId, DataStoreRole role);
void updateVolumeIds(long oldVolId, long newVolId);
}

View File

@ -46,7 +46,9 @@ import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.snapshot.SnapshotManager;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.db.DB;
@ -68,6 +70,8 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
@Inject
SnapshotDao snapshotDao;
@Inject
VolumeDao volumeDao;
@Inject
SnapshotDataFactory snapshotDataFactory;
@Override
@ -105,31 +109,44 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
// determine full snapshot backup or not
boolean fullBackup = true;
SnapshotDataStoreVO parentSnapshotOnBackupStore = snapshotStoreDao.findLatestSnapshotForVolume(snapshot.getVolumeId(), DataStoreRole.Image);
HypervisorType hypervisorType = snapshot.getBaseVolume().getHypervisorType();
if (parentSnapshotOnBackupStore != null && hypervisorType == Hypervisor.HypervisorType.XenServer) { // CS does incremental backup only for XenServer
int _deltaSnapshotMax = NumbersUtil.parseInt(configDao.getValue("snapshot.delta.max"),
SnapshotManager.DELTAMAX);
int deltaSnap = _deltaSnapshotMax;
int i;
for (i = 1; i < deltaSnap; i++) {
Long prevBackupId = parentSnapshotOnBackupStore.getParentSnapshotId();
if (prevBackupId == 0) {
break;
}
parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(prevBackupId, DataStoreRole.Image);
if (parentSnapshotOnBackupStore == null) {
break;
}
}
// In case of volume migration from one pool to other pool, CS should take full snapshot to avoid any issues with delta chain,
// to check if this is a migrated volume, compare the current pool id of volume and store_id of oldest snapshot on primary for this volume.
// Why oldest? Because at this point CS has two snapshot on primary entries for same volume, one with old pool_id and other one with
// current pool id. So, verify and if volume found to be migrated, delete snapshot entry with previous pool store_id.
SnapshotDataStoreVO oldestSnapshotOnPrimary = snapshotStoreDao.findOldestSnapshotForVolume(snapshot.getVolumeId(), DataStoreRole.Primary);
VolumeVO volume = volumeDao.findById(snapshot.getVolumeId());
if (oldestSnapshotOnPrimary != null) {
if (oldestSnapshotOnPrimary.getDataStoreId() == volume.getPoolId()) {
int _deltaSnapshotMax = NumbersUtil.parseInt(configDao.getValue("snapshot.delta.max"),
SnapshotManager.DELTAMAX);
int deltaSnap = _deltaSnapshotMax;
int i;
if (i >= deltaSnap) {
fullBackup = true;
} else {
fullBackup = false;
for (i = 1; i < deltaSnap; i++) {
Long prevBackupId = parentSnapshotOnBackupStore.getParentSnapshotId();
if (prevBackupId == 0) {
break;
}
parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(prevBackupId, DataStoreRole.Image);
if (parentSnapshotOnBackupStore == null) {
break;
}
}
if (i >= deltaSnap) {
fullBackup = true;
} else {
fullBackup = false;
}
} else {
// if there is an snapshot entry for previousPool(primary storage) of migrated volume, delete it becasue CS created one more snapshot entry for current pool
snapshotStoreDao.remove(oldestSnapshotOnPrimary.getId());
}
}
}

View File

@ -54,6 +54,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
private SearchBuilder<SnapshotDataStoreVO> snapshotSearch;
private SearchBuilder<SnapshotDataStoreVO> storeSnapshotSearch;
private SearchBuilder<SnapshotDataStoreVO> snapshotIdSearch;
private SearchBuilder<SnapshotDataStoreVO> volumeIdSearch;
private final String parentSearch = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where store_id = ? "
+ " and store_role = ? and volume_id = ? and state = 'Ready'" + " order by created DESC " + " limit 1";
@ -61,6 +62,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
" store_role = ? and volume_id = ? and state = 'Ready'" +
" order by created DESC " +
" limit 1";
private final String findOldestSnapshot = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where " +
" store_role = ? and volume_id = ? and state = 'Ready'" +
" order by created ASC " +
" limit 1";
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
@ -110,6 +115,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
snapshotIdSearch.and("snapshot_id", snapshotIdSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ);
snapshotIdSearch.done();
volumeIdSearch = createSearchBuilder();
volumeIdSearch.and("volume_id", volumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
volumeIdSearch.done();
return true;
}
@ -225,7 +234,34 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
return findByStoreSnapshot(role, sid, snid);
}
} catch (SQLException e) {
s_logger.debug("Failed to find parent snapshot: " + e.toString());
s_logger.debug("Failed to find latest snapshot for volume: " + volumeId + " due to: " + e.toString());
} finally {
try {
if (pstmt != null)
pstmt.close();
} catch (SQLException e) {
}
}
return null;
}
@Override
public SnapshotDataStoreVO findOldestSnapshotForVolume(Long volumeId, DataStoreRole role) {
TransactionLegacy txn = TransactionLegacy.currentTxn();
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = txn.prepareStatement(findOldestSnapshot);
pstmt.setString(1, role.toString());
pstmt.setLong(2, volumeId);
rs = pstmt.executeQuery();
while (rs.next()) {
long sid = rs.getLong(1);
long snid = rs.getLong(3);
return findByStoreSnapshot(role, sid, snid);
}
} catch (SQLException e) {
s_logger.debug("Failed to find oldest snapshot for volume: " + volumeId + " due to: " + e.toString());
} finally {
try {
if (pstmt != null)
@ -367,4 +403,13 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
}
}
@Override
public void updateVolumeIds(long oldVolId, long newVolId) {
SearchCriteria<SnapshotDataStoreVO> sc = volumeIdSearch.create();
sc.setParameters("volume_id", oldVolId);
SnapshotDataStoreVO snapshot = createForUpdate();
snapshot.setVolumeId(newVolId);
UpdateBuilder ub = getUpdateBuilder(snapshot);
update(ub, sc, null);
}
}