Repository: cloudstack Updated Branches: refs/heads/master 3090e4a03 -> 0c4128e02
CLOUDSTACK-8097: Failed to create volume snapshot after vm live migration across clusters. Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/0c4128e0 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/0c4128e0 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/0c4128e0 Branch: refs/heads/master Commit: 0c4128e024b519f24625ad8cd6b53ddf0137728e Parents: 3090e4a Author: Sanjay Tripathi <sanjay.tripa...@citrix.com> Authored: Fri Dec 19 14:26:48 2014 +0530 Committer: Sanjay Tripathi <sanjay.tripa...@citrix.com> Committed: Fri Dec 19 14:26:48 2014 +0530 ---------------------------------------------------------------------- .../orchestration/VolumeOrchestrator.java | 16 +++++- .../src/com/cloud/storage/dao/SnapshotDao.java | 2 + .../com/cloud/storage/dao/SnapshotDaoImpl.java | 10 ++++ .../datastore/db/SnapshotDataStoreDao.java | 4 ++ .../snapshot/XenserverSnapshotStrategy.java | 57 +++++++++++++------- .../image/db/SnapshotDataStoreDaoImpl.java | 47 +++++++++++++++- 6 files changed, 113 insertions(+), 23 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0c4128e0/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java ---------------------------------------------------------------------- diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 515d5ec..e04bd6d 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -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) { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0c4128e0/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java b/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java index f55352b2..ff2e445 100644 --- a/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java +++ b/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java @@ -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); + } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0c4128e0/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java b/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java index 204447c..84a92d7 100644 --- a/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -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); + } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0c4128e0/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java index e24c035..231b241 100644 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java @@ -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); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0c4128e0/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java ---------------------------------------------------------------------- diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java index 90efcde..51f75bd 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -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; - } - } - if (i >= deltaSnap) { - fullBackup = true; - } else { - fullBackup = false; + // 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; + + 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()); + } } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0c4128e0/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java ---------------------------------------------------------------------- diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java index 28d6598..ea73ecd 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java @@ -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); + } }