CS-14297: added "forced" option to deleteStoragePool command. If forced=true, all destroyed volumes are marked as Expunged even when we can't reach primary storage at the moment of deletion.

This commit is contained in:
Alena Prokharchyk 2012-04-16 13:16:59 -07:00
parent 72b5a94af8
commit 8bb5a96cbe
5 changed files with 97 additions and 21 deletions

View File

@ -37,6 +37,10 @@ public class DeletePoolCmd extends BaseCmd {
@IdentityMapper(entityTableName="storage_pool")
@Parameter(name = ApiConstants.ID, type = CommandType.LONG, required = true, description = "Storage pool id")
private Long id;
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force destroy storage pool " +
"(force expunge volumes in Destroyed state as a part of pool removal)")
private Boolean forced;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
@ -45,6 +49,10 @@ public class DeletePoolCmd extends BaseCmd {
public Long getId() {
return id;
}
public boolean isForced() {
return (forced != null) ? forced : false;
}
// ///////////////////////////////////////////////////
// ///////////// API Implementation///////////////////

View File

@ -44,7 +44,8 @@ public class StopVMCmd extends BaseAsyncCmd {
@Parameter(name = ApiConstants.ID, type = CommandType.LONG, required = true, description = "The ID of the virtual machine")
private Long id;
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the VM. The caller knows the VM is stopped.")
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force stop the VM " +
"(vm is marked as Stopped even when command fails to be send to the backend). The caller knows the VM is stopped.")
private Boolean forced;
// ///////////////////////////////////////////////////

View File

@ -1389,9 +1389,10 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
@Override
@DB
public boolean deletePool(DeletePoolCmd command) {
Long id = command.getId();
public boolean deletePool(DeletePoolCmd cmd) {
Long id = cmd.getId();
boolean deleteFlag = false;
boolean forced = cmd.isForced();
// verify parameters
StoragePoolVO sPool = _storagePoolDao.findById(id);
@ -1405,14 +1406,29 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
throw new InvalidParameterValueException("Unable to delete local storage id: " + id);
}
// Check if the pool has associated volumes in the volumes table
// If it does, then you cannot delete the pool
Pair<Long, Long> volumeRecords = _volsDao.getCountAndTotalByPool(id);
if (volumeRecords.first() > 0) {
s_logger.warn("Cannot delete pool " + sPool.getName() + " as there are associated vols for this pool");
return false; // cannot delete as there are associated vols
Pair<Long, Long> vlms = _volsDao.getCountAndTotalByPool(id);
if (forced) {
if (vlms.first() > 0) {
Pair<Long, Long> nonDstrdVlms = _volsDao.getNonDestroyedCountAndTotalByPool(id);
if (nonDstrdVlms.first() > 0) {
throw new CloudRuntimeException("Cannot delete pool " + sPool.getName() + " as there are associated " +
"non-destroyed vols for this pool");
}
//force expunge non-destroyed volumes
List<VolumeVO> vols = _volsDao.listVolumesToBeDestroyed();
for (VolumeVO vol : vols) {
expungeVolume(vol, true);
}
}
} else {
// Check if the pool has associated volumes in the volumes table
// If it does , then you cannot delete the pool
if (vlms.first() > 0) {
throw new CloudRuntimeException("Cannot delete pool " + sPool.getName() + " as there are associated vols" +
" for this pool");
}
}
// First get the host_id from storage_pool_host_ref for given pool id
StoragePoolVO lock = _storagePoolDao.acquireInLockTable(sPool.getId());
@ -1450,8 +1466,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
} else {
// Remove the SR associated with the Xenserver
for (StoragePoolHostVO host : hostPoolRecords) {
DeleteStoragePoolCommand cmd = new DeleteStoragePoolCommand(sPool);
final Answer answer = _agentMgr.easySend(host.getHostId(), cmd);
DeleteStoragePoolCommand deleteCmd = new DeleteStoragePoolCommand(sPool);
final Answer answer = _agentMgr.easySend(host.getHostId(), deleteCmd);
if (answer != null && answer.getResult()) {
deleteFlag = true;
@ -1991,7 +2007,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
List<VolumeVO> vols = _volsDao.listVolumesToBeDestroyed();
for (VolumeVO vol : vols) {
try {
expungeVolume(vol);
expungeVolume(vol, false);
} catch (Exception e) {
s_logger.warn("Unable to destroy " + vol.getId(), e);
}
@ -2575,7 +2591,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
}
try {
expungeVolume(volume);
expungeVolume(volume, false);
} catch (Exception e) {
s_logger.warn("Failed to expunge volume:", e);
return false;
@ -3107,7 +3123,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
// add code here
}
public void expungeVolume(VolumeVO vol) {
public void expungeVolume(VolumeVO vol, boolean force) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Expunging " + vol);
}
@ -3137,18 +3153,36 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
}
DestroyCommand cmd = new DestroyCommand(pool, vol, vmName);
boolean removeVolume = false;
try {
Answer answer = sendToPool(pool, cmd);
if (answer != null && answer.getResult()) {
_volsDao.remove(vol.getId());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Volume successfully expunged from " + poolId);
}
removeVolume = true;
} else {
s_logger.info("Will retry delete of " + vol + " from " + poolId);
}
} catch (StorageUnavailableException e) {
s_logger.info("Storage is unavailable currently. Will retry delete of " + vol + " from " + poolId);
if (force) {
s_logger.info("Storage is unavailable currently, but marking volume id=" + vol.getId() + " as expunged anyway due to force=true");
removeVolume = true;
} else {
s_logger.info("Storage is unavailable currently. Will retry delete of " + vol + " from " + poolId);
}
} catch (RuntimeException ex) {
if (force) {
s_logger.info("Failed to expunge volume, but marking volume id=" + vol.getId() + " as expunged anyway " +
"due to force=true. Volume failed to expunge due to ", ex);
removeVolume = true;
} else {
throw ex;
}
} finally {
if (removeVolume) {
_volsDao.remove(vol.getId());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Volume successfully expunged from " + poolId);
}
}
}
}
@ -3180,7 +3214,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
txn.commit();
for (VolumeVO expunge : toBeExpunged) {
expungeVolume(expunge);
expungeVolume(expunge, false);
}
}

View File

@ -23,29 +23,50 @@ import com.cloud.utils.db.GenericDao;
import com.cloud.utils.fsm.StateDao;
public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.State, Volume.Event, Volume> {
List<VolumeVO> findDetachedByAccount(long accountId);
List<VolumeVO> findByAccount(long accountId);
Pair<Long, Long> getCountAndTotalByPool(long poolId);
Pair<Long, Long> getNonDestroyedCountAndTotalByPool(long poolId);
List<VolumeVO> findByInstance(long id);
List<VolumeVO> findByInstanceAndType(long id, Volume.Type vType);
List<VolumeVO> findByInstanceIdDestroyed(long vmId);
List<VolumeVO> findByAccountAndPod(long accountId, long podId);
List<VolumeVO> findByTemplateAndZone(long templateId, long zoneId);
void deleteVolumesByInstance(long instanceId);
void attachVolume(long volumeId, long vmId, long deviceId);
void detachVolume(long volumeId);
boolean isAnyVolumeActivelyUsingTemplateOnPool(long templateId, long poolId);
List<VolumeVO> findCreatedByInstance(long id);
List<VolumeVO> findByPoolId(long poolId);
List<VolumeVO> findByInstanceAndDeviceId(long instanceId, long deviceId);
List<VolumeVO> findUsableVolumesForInstance(long instanceId);
Long countAllocatedVolumesForAccount(long accountId);
HypervisorType getHypervisorType(long volumeId);
List<VolumeVO> listVolumesToBeDestroyed();
ImageFormat getImageFormat(Long volumeId);
List<VolumeVO> findReadyRootVolumesByInstance(long instanceId);
List<Long> listPoolIdsByVolumeCount(long dcId, Long podId, Long clusterId, long accountId);
}

View File

@ -27,6 +27,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Volume;
import com.cloud.storage.Volume.Event;
import com.cloud.storage.Volume.State;
import com.cloud.storage.Volume.Type;
import com.cloud.storage.VolumeVO;
import com.cloud.utils.Pair;
@ -268,6 +269,7 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
TotalSizeByPoolSearch.select("count", Func.COUNT, (Object[])null);
TotalSizeByPoolSearch.and("poolId", TotalSizeByPoolSearch.entity().getPoolId(), Op.EQ);
TotalSizeByPoolSearch.and("removed", TotalSizeByPoolSearch.entity().getRemoved(), Op.NULL);
TotalSizeByPoolSearch.and("state", TotalSizeByPoolSearch.entity().getState(), Op.NEQ);
TotalSizeByPoolSearch.done();
ActiveTemplateSearch = createSearchBuilder(Long.class);
@ -379,4 +381,14 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
throw new CloudRuntimeException("Caught: " + ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT, e);
}
}
@Override @DB(txn=false)
public Pair<Long, Long> getNonDestroyedCountAndTotalByPool(long poolId) {
SearchCriteria<SumCount> sc = TotalSizeByPoolSearch.create();
sc.setParameters("poolId", poolId);
sc.setParameters("state", State.Destroy);
List<SumCount> results = customSearch(sc, null);
SumCount sumCount = results.get(0);
return new Pair<Long, Long>(sumCount.count, sumCount.sum);
}
}