mirror of https://github.com/apache/cloudstack.git
CLOUDSTACK-8097: Failed to create volume snapshot after vm live migration across clusters.
This commit is contained in:
parent
3090e4a030
commit
0c4128e024
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue