refactoring createVolume to new API framework

This commit is contained in:
Kris McQueen 2010-09-10 23:25:22 -07:00
parent b73cd10fbe
commit 1e46e2e588
7 changed files with 285 additions and 488 deletions

View File

@ -18,40 +18,20 @@
package com.cloud.api.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import com.cloud.api.BaseCmd;
import com.cloud.api.BaseAsyncCreateCmd;
import com.cloud.api.BaseCmd.Manager;
import com.cloud.api.Implementation;
import com.cloud.api.Parameter;
import com.cloud.api.ServerApiException;
import com.cloud.async.executor.VolumeOperationResultObject;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.api.response.VolumeResponse;
import com.cloud.serializer.SerializerHelper;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.Snapshot;
import com.cloud.user.Account;
import com.cloud.utils.Pair;
import com.cloud.storage.VolumeVO;
public class CreateVolumeCmd extends BaseCmd {
@Implementation(createMethod="createVolumeDB", method="createVolume", manager=Manager.StorageManager)
public class CreateVolumeCmd extends BaseAsyncCreateCmd {
public static final Logger s_logger = Logger.getLogger(CreateVolumeCmd.class.getName());
private static final String s_name = "createvolumeresponse";
private static final List<Pair<Enum, Boolean>> s_properties = new ArrayList<Pair<Enum, Boolean>>();
static {
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.ACCOUNT_OBJ, Boolean.FALSE));
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.USER_ID, Boolean.FALSE));
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.ACCOUNT, Boolean.FALSE));
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.DISK_OFFERING_ID, Boolean.FALSE));
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.DOMAIN_ID, Boolean.FALSE));
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.NAME, Boolean.TRUE));
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.SNAPSHOT_ID, Boolean.FALSE));
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.ZONE_ID, Boolean.FALSE));
s_properties.add(new Pair<Enum, Boolean>(BaseCmd.Properties.SIZE, Boolean.FALSE));
}
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
@ -69,6 +49,9 @@ public class CreateVolumeCmd extends BaseCmd {
@Parameter(name="name", type=CommandType.STRING, required=true)
private String volumeName;
@Parameter(name="size", type=CommandType.LONG)
private Long size;
@Parameter(name="snapshotid", type=CommandType.LONG)
private Long snapshotId;
@ -96,6 +79,10 @@ public class CreateVolumeCmd extends BaseCmd {
return volumeName;
}
public Long getSize() {
return size;
}
public Long getSnapshotId() {
return snapshotId;
}
@ -120,145 +107,30 @@ public class CreateVolumeCmd extends BaseCmd {
@Override
public String getResponse() {
}
VolumeVO volume = (VolumeVO)getResponseObject();
@Override
public List<Pair<String, Object>> execute(Map<String, Object> params) {
Account account = (Account) params.get(BaseCmd.Properties.ACCOUNT_OBJ.getName());
Long userId = (Long) params.get(BaseCmd.Properties.USER_ID.getName());
String accountName = (String) params.get(BaseCmd.Properties.ACCOUNT.getName());
Long domainId = (Long) params.get(BaseCmd.Properties.DOMAIN_ID.getName());
String name = (String) params.get(BaseCmd.Properties.NAME.getName());
Long zoneId = (Long) params.get(BaseCmd.Properties.ZONE_ID.getName());
Long diskOfferingId = (Long) params.get(BaseCmd.Properties.DISK_OFFERING_ID.getName());
Long snapshotId = (Long)params.get(BaseCmd.Properties.SNAPSHOT_ID.getName());
Long size = (Long)params.get(BaseCmd.Properties.SIZE.getName());
if (account == null) {
// Admin API call
// Check if accountName was passed in
if ((accountName == null) || (domainId == null)) {
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Account and domainId must be passed in.");
}
// Look up the account by name and domain ID
account = getManagementServer().findActiveAccount(accountName, domainId);
// If the account is null, this means that the accountName and domainId passed in were invalid
if (account == null)
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to find account with name: " + accountName + " and domain ID: " + domainId);
} else {
// User API call
// If the account is an admin, and accountName/domainId were passed in, use the account specified by these parameters
if (isAdmin(account.getType())) {
if (domainId != null) {
if (!getManagementServer().isChildDomain(account.getDomainId(), domainId)) {
throw new ServerApiException(BaseCmd.ACCOUNT_ERROR, "Unable to create volume in domain " + domainId + ", permission denied.");
}
if (accountName != null) {
account = getManagementServer().findActiveAccount(accountName, domainId);
if (account == null)
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to find account with name: " + accountName + " and domain ID: " + domainId);
}
}
}
}
// If command is executed via the Admin API, set userId to the id of System account (1)
if (userId == null) {
userId = Long.valueOf(Account.ACCOUNT_ID_SYSTEM);
}
if(size==null){
size = Long.valueOf(0);
}
VolumeResponse response = new VolumeResponse();
response.setId(volume.getId());
response.setName(param.getName());
response.setVolumeType(volume.getVolumeType().toString());
response.setSize(volume.getSize());
response.setCreated(volume.getCreated());
response.setState(volume.getStatus().toString());
response.setAccountName(ggetManagementServer().findAccountById(volume.getAccountId()).getAccountName());
response.setDomainId(volume.getDomainId());
response.setDiskOfferingId(volume.getDiskOfferingId());
boolean useSnapshot = false;
if (snapshotId == null) {
if ((zoneId == null)) {
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Missing parameter,zoneid must be specified.");
}
if (diskOfferingId == null && size == 0) {
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Missing parameter(s),either a positive volume size or a valid disk offering id must be specified.");
} else if(diskOfferingId == null && size != 0) {
//validate the size to ensure between min and max size range
try {
boolean ok = getManagementServer().validateCustomVolumeSizeRange(size);
if (!ok) {
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Invalid size for custom volume creation:");
}
} catch (InvalidParameterValueException e) {
s_logger.warn("Invalid size for custom volume creation");
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Invalid size for custom volume creation:"+e.getMessage());
}
//this is the case of creating var size vol with private disk offering
List<DiskOfferingVO> privateTemplateList = getManagementServer().findPrivateDiskOffering();
diskOfferingId = privateTemplateList.get(0).getId(); //we use this id for creating volume
}
} else {
useSnapshot = true;
//Verify parameters
Snapshot snapshotCheck = getManagementServer().findSnapshotById(snapshotId);
if (snapshotCheck == null) {
throw new ServerApiException (BaseCmd.SNAPSHOT_INVALID_PARAM_ERROR, "unable to find a snapshot with id " + snapshotId);
}
if (account != null) {
if (isAdmin(account.getType())) {
Account snapshotOwner = getManagementServer().findAccountById(snapshotCheck.getAccountId());
if (!getManagementServer().isChildDomain(account.getDomainId(), snapshotOwner.getDomainId())) {
throw new ServerApiException(BaseCmd.ACCOUNT_ERROR, "Unable to create volume from snapshot with id " + snapshotId + ", permission denied.");
}
} else if (account.getId().longValue() != snapshotCheck.getAccountId()) {
throw new ServerApiException(BaseCmd.SNAPSHOT_INVALID_PARAM_ERROR, "unable to find a snapshot with id " + snapshotId + " for this account");
}
}
if (volume.getDiskOfferingId() != null) {
response.setDiskOfferingName(getManagementServer().findDiskOfferingById(volume.getDiskOfferingId()).getName());
response.setDiskOfferingDisplayText(getManagementServer().findDiskOfferingById(volume.getDiskOfferingId()).getDisplayText());
}
response.setDomain(getManagementServer().findDomainIdById(volume.getDomainId()).getName());
response.setStorageType("shared"); // NOTE: You can never create a local disk volume but if that changes, we need to change this
if (volume.getPoolId() != null)
response.setStorage(getManagementServer().findPoolById(volume.getPoolId()).getName());
response.setZoneId(volume.getDataCenterId());
response.setZoneName(getManagementServer().getDataCenterBy(volume.getDataCenterId()).getName());
try {
long jobId = 0;
if (useSnapshot) {
jobId = getManagementServer().createVolumeFromSnapshotAsync(userId, account.getId(), snapshotId, name);
} else {
jobId = getManagementServer().createVolumeAsync(userId, account.getId(), name, zoneId, diskOfferingId, size);
}
if (jobId == 0) {
s_logger.warn("Unable to schedule async-job for CreateVolume command");
} else {
if(s_logger.isDebugEnabled())
s_logger.debug("CreateVolume command has been accepted, job id: " + jobId);
}
long volumeId = waitInstanceCreation(jobId);
List<Pair<String, Object>> returnValues = new ArrayList<Pair<String, Object>>();
returnValues.add(new Pair<String, Object>(BaseCmd.Properties.JOB_ID.getName(), Long.valueOf(jobId)));
returnValues.add(new Pair<String, Object>(BaseCmd.Properties.VOLUME_ID.getName(), Long.valueOf(volumeId)));
return returnValues;
} catch (Exception ex) {
s_logger.error("Failed to create volume " + (useSnapshot ? ("from snapshot " + snapshotId) : ("in zone " + zoneId + " with disk offering " + diskOfferingId)), ex);
if (useSnapshot) {
throw new ServerApiException(BaseCmd.CREATE_VOLUME_FROM_SNAPSHOT_ERROR, "Unable to create a volume from snapshot with id " + snapshotId + " for this account.");
} else {
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create volume: " + ex.getMessage());
}
}
return SerializerHelper.toSerializedString(response);
}
protected long getInstanceIdFromJobSuccessResult(String result) {
VolumeOperationResultObject resultObject = (VolumeOperationResultObject) SerializerHelper.fromSerializedString(result);
if(resultObject != null) {
return resultObject.getId();
}
return 0;
}
}

View File

@ -28,6 +28,7 @@ import com.cloud.api.commands.CreateDomainCmd;
import com.cloud.api.commands.CreatePortForwardingServiceCmd;
import com.cloud.api.commands.CreatePortForwardingServiceRuleCmd;
import com.cloud.api.commands.CreateUserCmd;
import com.cloud.api.commands.CreateVolumeCmd;
import com.cloud.api.commands.DeletePortForwardingServiceCmd;
import com.cloud.api.commands.DeleteUserCmd;
import com.cloud.api.commands.DeployVMCmd;
@ -348,19 +349,6 @@ public interface ManagementServer {
*/
Long getAccountIdForVlan(long vlanDbId);
/**
* Creates a data volume
* @param accountId
* @pparam userId
* @param name - name for the volume
* @param zoneId - id of the zone to create this volume on
* @param diskOfferingId - id of the disk offering to create this volume with
* @param size - size of the volume
* @return true if success, false if not
*/
VolumeVO createVolume(long accountId, long userId, String name, long zoneId, long diskOfferingId, long startEventId, long size) throws InternalErrorException;
long createVolumeAsync(long accountId, long userId, String name, long zoneId, long diskOfferingId, long size) throws InvalidParameterValueException, InternalErrorException, ResourceAllocationException;
/**
* Finds the root volume of the VM
* @param vmId
@ -1129,8 +1117,6 @@ public interface ManagementServer {
*/
boolean destroyTemplateSnapshot(Long userId, long snapshotId);
long createVolumeFromSnapshotAsync(long userId, long accountId, long snapshotId, String volumeName) throws InternalErrorException, ResourceAllocationException;
/**
* List all snapshots of a disk volume. Optionaly lists snapshots created by specified interval
* @param cmd the command containing the search criteria (order by, limit, etc.)
@ -1409,7 +1395,6 @@ public interface ManagementServer {
boolean checkLocalStorageConfigVal();
boolean validateCustomVolumeSizeRange(long size) throws InvalidParameterValueException;
boolean updateUser(UpdateUserCmd cmd) throws InvalidParameterValueException;
boolean updateTemplatePermissions(UpdateTemplateOrIsoPermissionsCmd cmd)throws InvalidParameterValueException, PermissionDeniedException,InternalErrorException;
String[] createApiKeyAndSecretKey(RegisterCmd cmd);

View File

@ -59,7 +59,6 @@ import com.cloud.api.commands.CreateDomainCmd;
import com.cloud.api.commands.CreatePortForwardingServiceCmd;
import com.cloud.api.commands.CreatePortForwardingServiceRuleCmd;
import com.cloud.api.commands.CreateUserCmd;
import com.cloud.api.commands.CreateVolumeCmd;
import com.cloud.api.commands.DeletePortForwardingServiceCmd;
import com.cloud.api.commands.DeleteUserCmd;
import com.cloud.api.commands.DeployVMCmd;
@ -128,8 +127,6 @@ import com.cloud.async.executor.NetworkGroupIngressParam;
import com.cloud.async.executor.SecurityGroupParam;
import com.cloud.async.executor.VMOperationParam;
import com.cloud.async.executor.VMOperationParam.VmOp;
import com.cloud.async.executor.VolumeOperationParam;
import com.cloud.async.executor.VolumeOperationParam.VolumeOp;
import com.cloud.capacity.CapacityVO;
import com.cloud.capacity.dao.CapacityDao;
import com.cloud.configuration.ConfigurationManager;
@ -376,8 +373,6 @@ public class ManagementServerImpl implements ManagementServer {
private boolean _isHypervisorSnapshotCapable = false;
private final int _maxVolumeSizeInGb;
protected ManagementServerImpl() {
ComponentLocator locator = ComponentLocator.getLocator(Name);
_lunDao = locator.getDao(PreallocatedLunDao.class);
@ -471,11 +466,6 @@ public class ManagementServerImpl implements ManagementServer {
// Parse the max number of UserVMs and public IPs from server-setup.xml,
// and set them in the right places
String maxVolumeSizeInGbString = _configs.get("max.volume.size.gb");
int maxVolumeSizeGb = NumbersUtil.parseInt(maxVolumeSizeInGbString, 2000);
_maxVolumeSizeInGb = maxVolumeSizeGb;
_routerRamSize = NumbersUtil.parseInt(_configs.get("router.ram.size"),NetworkManager.DEFAULT_ROUTER_VM_RAMSIZE);
_proxyRamSize = NumbersUtil.parseInt(_configs.get("consoleproxy.ram.size"), ConsoleProxyManager.DEFAULT_PROXY_VM_RAMSIZE);
_ssRamSize = NumbersUtil.parseInt(_configs.get("secstorage.ram.size"), SecondaryStorageVmManager.DEFAULT_SS_VM_RAMSIZE);
@ -1399,104 +1389,6 @@ public class ManagementServerImpl implements ManagementServer {
return null;
}
@Override
public VolumeVO createVolume(long userId, long accountId, String name, long zoneId, long diskOfferingId, long startEventId, long size) throws InternalErrorException {
EventUtils.saveStartedEvent(userId, accountId, EventTypes.EVENT_VOLUME_CREATE, "Creating volume", startEventId);
DataCenterVO zone = _dcDao.findById(zoneId);
DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
VolumeVO createdVolume = _storageMgr.createVolume(accountId, userId, name, zone, diskOffering, startEventId,size);
if (createdVolume != null)
return createdVolume;
else
throw new InternalErrorException("Failed to create volume.");
}
@Override
public long createVolumeAsync(long userId, long accountId, String name, long zoneId, long diskOfferingId, long size) throws InvalidParameterValueException, InternalErrorException, ResourceAllocationException {
// Check that the account is valid
AccountVO account = _accountDao.findById(accountId);
if (account == null) {
throw new InvalidParameterValueException("Please specify a valid account.");
}
// Check that the zone is valid
DataCenterVO zone = _dcDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Please specify a valid zone.");
}
// Check that the the disk offering is specified
DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
if ((diskOffering == null) || !DiskOfferingVO.Type.Disk.equals(diskOffering.getType())) {
throw new InvalidParameterValueException("Please specify a valid disk offering.");
}
// Check that there is a shared primary storage pool in the specified zone
List<StoragePoolVO> storagePools = _poolDao.listByDataCenterId(zoneId);
boolean sharedPoolExists = false;
for (StoragePoolVO storagePool : storagePools) {
if (storagePool.isShared()) {
sharedPoolExists = true;
}
}
// Check that there is at least one host in the specified zone
List<HostVO> hosts = _hostDao.listByDataCenter(zoneId);
if (hosts.isEmpty()) {
throw new InvalidParameterValueException("Please add a host in the specified zone before creating a new volume.");
}
if (!sharedPoolExists) {
throw new InvalidParameterValueException("Please specify a zone that has at least one shared primary storage pool.");
}
// Check that the resource limit for volumes won't be exceeded
if (_accountMgr.resourceLimitExceeded(account, ResourceType.volume)) {
ResourceAllocationException rae = new ResourceAllocationException("Maximum number of volumes for account: " + account.getAccountName() + " has been exceeded.");
rae.setResourceType("volume");
throw rae;
}
long eventId = EventUtils.saveScheduledEvent(userId, accountId, EventTypes.EVENT_VOLUME_CREATE, "creating volume");
VolumeOperationParam param = new VolumeOperationParam();
param.setOp(VolumeOp.Create);
param.setAccountId(accountId);
param.setUserId(UserContext.current().getUserId());
param.setName(name);
param.setZoneId(zoneId);
param.setDiskOfferingId(diskOfferingId);
param.setEventId(eventId);
param.setSize(size);
Gson gson = GsonHelper.getBuilder().create();
AsyncJobVO job = new AsyncJobVO();
job.setUserId(UserContext.current().getUserId());
job.setAccountId(accountId);
job.setCmd("VolumeOperation");
job.setCmdInfo(gson.toJson(param));
job.setCmdOriginator(CreateVolumeCmd.getResultObjectName());
return _asyncMgr.submitAsyncJob(job);
}
@Override
public long createVolumeFromSnapshotAsync(long userId, long accountId, long snapshotId, String volumeName) throws InternalErrorException, ResourceAllocationException {
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
AccountVO account = _accountDao.findById(snapshot.getAccountId());
// Check that the resource limit for volumes won't be exceeded
if (_accountMgr.resourceLimitExceeded(account, ResourceType.volume)) {
ResourceAllocationException rae = new ResourceAllocationException("Maximum number of volumes for account: " + account.getAccountName() + " has been exceeded.");
rae.setResourceType("volume");
throw rae;
}
return _snapMgr.createVolumeFromSnapshotAsync(userId, accountId, snapshotId, volumeName);
}
@Override
public VolumeVO findRootVolume(long vmId) {
List<VolumeVO> volumes = _volumeDao.findByInstanceAndType(vmId, VolumeType.ROOT);
@ -6859,17 +6751,6 @@ public class ManagementServerImpl implements ManagementServer {
return false;
}
@Override
public boolean validateCustomVolumeSizeRange(long size) throws InvalidParameterValueException {
if (size<0 || (size>0 && size < 1)) {
throw new InvalidParameterValueException("Please specify a size of at least 1 Gb.");
} else if (size > _maxVolumeSizeInGb) {
throw new InvalidParameterValueException("The maximum size allowed is " + _maxVolumeSizeInGb + " Gb.");
}
return true;
}
@Override
public boolean lockAccount(LockAccountCmd cmd) {

View File

@ -24,6 +24,7 @@ import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.api.commands.CancelPrimaryStorageMaintenanceCmd;
import com.cloud.api.commands.CreateStoragePoolCmd;
import com.cloud.api.commands.CreateVolumeCmd;
import com.cloud.api.commands.DeletePoolCmd;
import com.cloud.api.commands.DeleteVolumeCmd;
import com.cloud.api.commands.PreparePrimaryStorageForMaintenanceCmd;
@ -32,6 +33,7 @@ import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.exception.InternalErrorException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceInUseException;
import com.cloud.exception.StorageUnavailableException;
@ -191,19 +193,23 @@ public interface StorageManager extends Manager {
* @return VolumeVO
*/
VolumeVO moveVolume(VolumeVO volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId) throws InternalErrorException;
/**
* Creates a new volume in a pool in the specified zone
* @param accountId
* @param userId
* @param name
* @param dc
* @param diskOffering
* @param size
* @return VolumeVO
* Creates the database object for a volume based on the given criteria
* @param cmd the API command wrapping the criteria (account/domainId [admin only], zone, diskOffering, snapshot, name)
* @return the volume object
* @throws InvalidParameterValueException
* @throws PermissionDeniedException
*/
VolumeVO createVolume(long accountId, long userId, String name, DataCenterVO dc, DiskOfferingVO diskOffering, long startEventId, long size);
VolumeVO createVolumeDB(CreateVolumeCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, ResourceAllocationException;
/**
* Creates the volume based on the given criteria
* @param cmd the API command wrapping the criteria (account/domainId [admin only], zone, diskOffering, snapshot, name)
* @return the volume object
*/
VolumeVO createVolume(CreateVolumeCmd cmd);
/**
* Marks the specified volume as destroyed in the management server database. The expunge thread will delete the volume from its storage pool.
* @param volume
@ -270,11 +276,6 @@ public interface StorageManager extends Manager {
List<StoragePoolVO> getStoragePoolsForVm(long vmId);
String getPrimaryStorageNameLabel(VolumeVO volume);
/**
* Creates a volume from the specified snapshot. A new volume is returned which is not attached to any VM Instance
*/
VolumeVO createVolumeFromSnapshot(long userId, long accountId, long snapshotId, String volumeName, long startEventId);
/**
* Enable maintenance for primary storage

View File

@ -65,6 +65,7 @@ import com.cloud.api.BaseCmd;
import com.cloud.api.ServerApiException;
import com.cloud.api.commands.CancelPrimaryStorageMaintenanceCmd;
import com.cloud.api.commands.CreateStoragePoolCmd;
import com.cloud.api.commands.CreateVolumeCmd;
import com.cloud.api.commands.DeletePoolCmd;
import com.cloud.api.commands.DeleteVolumeCmd;
import com.cloud.api.commands.PreparePrimaryStorageForMaintenanceCmd;
@ -97,6 +98,7 @@ import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InternalErrorException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceInUseException;
import com.cloud.exception.StorageUnavailableException;
@ -132,6 +134,7 @@ import com.cloud.storage.snapshot.SnapshotScheduler;
import com.cloud.template.TemplateManager;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserContext;
import com.cloud.user.dao.AccountDao;
@ -212,6 +215,7 @@ public class StorageManagerImpl implements StorageManager {
protected int _pingInterval = 60; // seconds
protected int _hostRetry;
protected int _overProvisioningFactor = 1;
private int _maxVolumeSizeInGb;
private int _totalRetries;
private int _pauseInterval;
@ -396,16 +400,16 @@ public class StorageManagerImpl implements StorageManager {
return answers[0];
}
protected DiskCharacteristicsTO createDiskCharacteristics(VolumeVO volume, VMTemplateVO template, DataCenterVO dc, DiskOfferingVO diskOffering) {
private DiskCharacteristicsTO createDiskCharacteristics(VolumeVO volume, VMTemplateVO template, long dataCenterId, DiskOfferingVO diskOffering) {
if (volume.getVolumeType() == VolumeType.ROOT && Storage.ImageFormat.ISO != template.getFormat()) {
SearchCriteria<VMTemplateHostVO> sc = HostTemplateStatesSearch.create();
sc.setParameters("id", template.getId());
sc.setParameters("state", com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOADED);
sc.setJoinParameters("host", "dcId", dc.getId());
sc.setJoinParameters("host", "dcId", dataCenterId);
List<VMTemplateHostVO> sss = _vmTemplateHostDao.search(sc, null);
if (sss.size() == 0) {
throw new CloudRuntimeException("Template " + template.getName() + " has not been completely downloaded to zone " + dc.getId());
throw new CloudRuntimeException("Template " + template.getName() + " has not been completely downloaded to zone " + dataCenterId);
}
VMTemplateHostVO ss = sss.get(0);
@ -427,43 +431,14 @@ public class StorageManagerImpl implements StorageManager {
}
@DB
protected Pair<VolumeVO, String> createVolumeFromSnapshot(long userId, long accountId, String userSpecifiedName, DataCenterVO dc, DiskOfferingVO diskOffering, SnapshotVO snapshot, String templatePath, Long originalVolumeSize, VMTemplateVO template) {
private Pair<VolumeVO, String> createVolumeFromSnapshot(VolumeVO volume, SnapshotVO snapshot, String templatePath, Long originalVolumeSize, VMTemplateVO template) {
VolumeVO createdVolume = null;
Long volumeId = null;
String volumeFolder = null;
// Create the Volume object and save it so that we can return it to the user
Account account = _accountDao.findById(accountId);
VolumeVO volume = new VolumeVO(null, userSpecifiedName, -1, -1, -1, -1, new Long(-1), null, null, 0, Volume.VolumeType.DATADISK);
volume.setPoolId(null);
volume.setDataCenterId(dc.getId());
volume.setPodId(null);
volume.setAccountId(accountId);
volume.setDomainId(account.getDomainId().longValue());
volume.setMirrorState(MirrorState.NOT_MIRRORED);
if (diskOffering != null) {
volume.setDiskOfferingId(diskOffering.getId());
}
volume.setSize(originalVolumeSize);
volume.setStorageResourceType(StorageResourceType.STORAGE_POOL);
volume.setInstanceId(null);
volume.setUpdated(new Date());
volume.setStatus(AsyncInstanceCreateStatus.Creating);
volume = _volsDao.persist(volume);
volumeId = volume.getId();
AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor();
if(asyncExecutor != null) {
AsyncJobVO job = asyncExecutor.getJob();
if(s_logger.isInfoEnabled())
s_logger.info("CreateVolume created a new instance " + volumeId + ", update async job-" + job.getId() + " progress status");
_asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId);
_asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId);
}
Account account = _accountDao.findById(volume.getAccountId());
final HashSet<StoragePool> poolsToAvoid = new HashSet<StoragePool>();
StoragePoolVO pool = null;
@ -472,10 +447,10 @@ public class StorageManagerImpl implements StorageManager {
Pair<HostPodVO, Long> pod = null;
String volumeUUID = null;
String details = null;
DiskCharacteristicsTO dskCh = createDiskCharacteristics(volume, template, dc, diskOffering);
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
DataCenterVO dc = _dcDao.findById(volume.getDataCenterId());
DiskCharacteristicsTO dskCh = createDiskCharacteristics(volume, template, dc.getId(), diskOffering);
// Determine what pod to store the volume in
while ((pod = _agentMgr.findPod(null, null, dc, account.getId(), podsToAvoid)) != null) {
// Determine what storage pool to store the volume in
@ -484,13 +459,13 @@ public class StorageManagerImpl implements StorageManager {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Attempting to create volume from snapshotId: " + snapshot.getId() + " on storage pool " + pool.getName());
}
// Get the newly created VDI from the snapshot.
// This will return a null volumePath if it could not be created
Pair<String, String> volumeDetails = createVDIFromSnapshot(userId, snapshot, pool, templatePath);
Pair<String, String> volumeDetails = createVDIFromSnapshot(UserContext.current().getUserId(), snapshot, pool, templatePath);
volumeUUID = volumeDetails.first();
details = volumeDetails.second();
if (volumeUUID != null) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Volume with UUID " + volumeUUID + " was created on storage pool " + pool.getName());
@ -500,7 +475,6 @@ public class StorageManagerImpl implements StorageManager {
}
s_logger.warn("Unable to create volume on pool " + pool.getName() + ", reason: " + details);
}
if (success) {
@ -517,7 +491,7 @@ public class StorageManagerImpl implements StorageManager {
if (success) {
// Increment the number of volumes
_accountMgr.incrementResourceCount(accountId, ResourceType.volume);
_accountMgr.incrementResourceCount(account.getId(), ResourceType.volume);
createdVolume.setStatus(AsyncInstanceCreateStatus.Created);
createdVolume.setPodId(pod.first().getId());
@ -536,19 +510,17 @@ public class StorageManagerImpl implements StorageManager {
return new Pair<VolumeVO, String>(createdVolume, details);
}
@Override
@DB
public VolumeVO createVolumeFromSnapshot(long userId, long accountId, long snapshotId, String volumeName, long startEventId) {
private VolumeVO createVolumeFromSnapshot(VolumeVO volume, long snapshotId/*, long startEventId*/) {
// FIXME: start event id needs to come from somewhere
EventVO event = new EventVO();
event.setUserId(userId);
event.setAccountId(accountId);
event.setUserId(UserContext.current().getUserId());
event.setAccountId(volume.getAccountId());
event.setType(EventTypes.EVENT_VOLUME_CREATE);
event.setState(EventState.Started);
event.setStartId(startEventId);
// FIXME: event.setStartId(startEventId);
event.setDescription("Creating volume from snapshot with id: "+snapshotId);
_eventDao.persist(event);
// By default, assume failure.
VolumeVO createdVolume = null;
String details = null;
@ -612,7 +584,7 @@ public class StorageManagerImpl implements StorageManager {
s_logger.error("Original volume must have been a ROOT DISK or a DATA DISK");
return null;
}
Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(userId, accountId, volumeName, dc, diskOffering, snapshot, templatePath, originalVolume.getSize(), template);
Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(volume, snapshot, templatePath, originalVolume.getSize(), template);
createdVolume = volumeDetails.first();
if (createdVolume != null) {
volumeId = createdVolume.getId();
@ -636,11 +608,11 @@ public class StorageManagerImpl implements StorageManager {
String poolName = _storagePoolDao.findById(createdVolume.getPoolId()).getName();
String eventParams = "id=" + volumeId +"\ndoId="+diskOfferingId+"\ntId="+templateId+"\ndcId="+originalVolume.getDataCenterId()+"\nsize="+sizeMB;
event = new EventVO();
event.setAccountId(accountId);
event.setUserId(userId);
event.setAccountId(volume.getAccountId());
event.setUserId(UserContext.current().getUserId());
event.setType(EventTypes.EVENT_VOLUME_CREATE);
event.setParameters(eventParams);
event.setStartId(startEventId);
// FIXME: event.setStartId(startEventId);
event.setState(EventState.Completed);
if (createdVolume.getPath() != null) {
event.setDescription("Created volume: "+ createdVolume.getName() + " with size: " + sizeMB + " MB in pool: " + poolName + " from snapshot id: " + snapshotId);
@ -703,9 +675,9 @@ public class StorageManagerImpl implements StorageManager {
DiskCharacteristicsTO dskCh = null;
if (volume.getVolumeType() == VolumeType.ROOT && Storage.ImageFormat.ISO != template.getFormat()) {
dskCh = createDiskCharacteristics(volume, template, dc, offering);
dskCh = createDiskCharacteristics(volume, template, dc.getId(), offering);
} else {
dskCh = createDiskCharacteristics(volume, template, dc, diskOffering);
dskCh = createDiskCharacteristics(volume, template, dc.getId(), diskOffering);
}
Transaction txn = Transaction.currentTxn();
@ -1572,92 +1544,197 @@ public class StorageManagerImpl implements StorageManager {
}
@Override
@DB
public VolumeVO createVolume(long accountId, long userId, String userSpecifiedName, DataCenterVO dc, DiskOfferingVO diskOffering, long startEventId, long size)
{
String volumeName = "";
VolumeVO createdVolume = null;
try
{
// Determine the volume's name
volumeName = getRandomVolumeName();
// Create the Volume object and save it so that we can return it to the user
Account account = _accountDao.findById(accountId);
VolumeVO volume = new VolumeVO(null, userSpecifiedName, -1, -1, -1, -1, new Long(-1), null, null, 0, Volume.VolumeType.DATADISK);
volume.setPoolId(null);
volume.setDataCenterId(dc.getId());
volume.setPodId(null);
volume.setAccountId(accountId);
volume.setDomainId(account.getDomainId().longValue());
volume.setMirrorState(MirrorState.NOT_MIRRORED);
volume.setDiskOfferingId(diskOffering.getId());
volume.setStorageResourceType(StorageResourceType.STORAGE_POOL);
volume.setInstanceId(null);
volume.setUpdated(new Date());
volume.setStatus(AsyncInstanceCreateStatus.Creating);
volume.setDomainId(account.getDomainId().longValue());
volume = _volsDao.persist(volume);
AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor();
if (asyncExecutor != null) {
AsyncJobVO job = asyncExecutor.getJob();
if (s_logger.isInfoEnabled())
s_logger.info("CreateVolume created a new instance " + volume.getId() + ", update async job-" + job.getId() + " progress status");
_asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volume.getId());
_asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volume.getId());
}
List<StoragePoolVO> poolsToAvoid = new ArrayList<StoragePoolVO>();
Set<Long> podsToAvoid = new HashSet<Long>();
Pair<HostPodVO, Long> pod = null;
while ((pod = _agentMgr.findPod(null, null, dc, account.getId(), podsToAvoid)) != null) {
if ((createdVolume = createVolume(volume, null, null, dc, pod.first(), null, null, diskOffering, poolsToAvoid, size)) != null) {
break;
} else {
podsToAvoid.add(pod.first().getId());
}
}
// Create an event
EventVO event = new EventVO();
event.setAccountId(accountId);
event.setUserId(userId);
event.setType(EventTypes.EVENT_VOLUME_CREATE);
event.setStartId(startEventId);
Transaction txn = Transaction.currentTxn();
public VolumeVO createVolumeDB(CreateVolumeCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, ResourceAllocationException {
// FIXME: some of the scheduled event stuff might be missing here...
Account account = (Account)UserContext.current().getAccountObject();
String accountName = cmd.getAccountName();
Long domainId = cmd.getDomainId();
Account targetAccount = null;
if ((account == null) || isAdmin(account.getType())) {
// Admin API call
if ((domainId != null) && (accountName != null)) {
if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), domainId)) {
throw new PermissionDeniedException("Unable to create volume in domain " + domainId + ", permission denied.");
}
txn.start();
if (createdVolume != null) {
// Increment the number of volumes
_accountMgr.incrementResourceCount(accountId, ResourceType.volume);
// Set event parameters
long sizeMB = createdVolume.getSize() / (1024 * 1024);
StoragePoolVO pool = _storagePoolDao.findById(createdVolume.getPoolId());
String eventParams = "id=" + createdVolume.getId() + "\ndoId=" + diskOffering.getId() + "\ntId=" + -1 + "\ndcId=" + dc.getId() + "\nsize=" + sizeMB;
event.setLevel(EventVO.LEVEL_INFO);
event.setDescription("Created volume: " + createdVolume.getName() + " with size: " + sizeMB + " MB in pool: " + pool.getName());
event.setParameters(eventParams);
_eventDao.persist(event);
targetAccount = _accountDao.findActiveAccount(accountName, domainId);
} else {
// Mark the existing volume record as corrupted
volume.setStatus(AsyncInstanceCreateStatus.Corrupted);
volume.setDestroyed(true);
_volsDao.update(volume.getId(), volume);
}
targetAccount = account;
}
txn.commit();
} catch (Exception e) {
s_logger.error("Unhandled exception while saving volume " + volumeName, e);
// If the account is null, this means that the accountName and domainId passed in were invalid
if (targetAccount == null) {
throw new InvalidParameterValueException("Unable to find account with name: " + accountName + " and domain ID: " + domainId);
}
} else {
targetAccount = account;
}
// check if the volume can be created for the user
// Check that the resource limit for volumes won't be exceeded
if (_accountMgr.resourceLimitExceeded((AccountVO)targetAccount, ResourceType.volume)) {
ResourceAllocationException rae = new ResourceAllocationException("Maximum number of volumes for account: " + targetAccount.getAccountName() + " has been exceeded.");
rae.setResourceType("volume");
throw rae;
}
// validate input parameters before creating the volume
if (cmd.getSnapshotId() == null) {
Long zoneId = cmd.getZoneId();
if ((zoneId == null)) {
throw new InvalidParameterValueException("Missing parameter, zoneid must be specified.");
}
Long diskOfferingId = cmd.getDiskOfferingId();
Long size = cmd.getSize();
if ((diskOfferingId == null) && (size == null)) {
throw new InvalidParameterValueException("Missing parameter(s),either a positive volume size or a valid disk offering id must be specified.");
} else if ((diskOfferingId == null) && (size != null)) {
boolean ok = validateCustomVolumeSizeRange(size);
if (!ok) {
throw new InvalidParameterValueException("Invalid size for custom volume creation: " + size);
}
//this is the case of creating var size vol with private disk offering
List<DiskOfferingVO> privateTemplateList = _diskOfferingDao.findPrivateDiskOffering();
diskOfferingId = privateTemplateList.get(0).getId(); //we use this id for creating volume
} else {
// Check that the the disk offering is specified
DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
if ((diskOffering == null) || !DiskOfferingVO.Type.Disk.equals(diskOffering.getType())) {
throw new InvalidParameterValueException("Please specify a valid disk offering.");
}
}
} else {
Long snapshotId = cmd.getSnapshotId();
Snapshot snapshotCheck = _snapshotDao.findById(snapshotId);
if (snapshotCheck == null) {
throw new ServerApiException (BaseCmd.SNAPSHOT_INVALID_PARAM_ERROR, "unable to find a snapshot with id " + snapshotId);
}
if (account != null) {
if (isAdmin(account.getType())) {
Account snapshotOwner = _accountDao.findById(snapshotCheck.getAccountId());
if (!_domainDao.isChildDomain(account.getDomainId(), snapshotOwner.getDomainId())) {
throw new ServerApiException(BaseCmd.ACCOUNT_ERROR, "Unable to create volume from snapshot with id " + snapshotId + ", permission denied.");
}
} else if (account.getId().longValue() != snapshotCheck.getAccountId()) {
throw new ServerApiException(BaseCmd.SNAPSHOT_INVALID_PARAM_ERROR, "unable to find a snapshot with id " + snapshotId + " for this account");
}
}
}
Long zoneId = cmd.getZoneId();
// Check that the zone is valid
DataCenterVO zone = _dcDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Please specify a valid zone.");
}
// Check that there is a shared primary storage pool in the specified zone
List<StoragePoolVO> storagePools = _storagePoolDao.listByDataCenterId(zoneId);
boolean sharedPoolExists = false;
for (StoragePoolVO storagePool : storagePools) {
if (storagePool.isShared()) {
sharedPoolExists = true;
}
}
// Check that there is at least one host in the specified zone
List<HostVO> hosts = _hostDao.listByDataCenter(zoneId);
if (hosts.isEmpty()) {
throw new InvalidParameterValueException("Please add a host in the specified zone before creating a new volume.");
}
if (!sharedPoolExists) {
throw new InvalidParameterValueException("Please specify a zone that has at least one shared primary storage pool.");
}
String userSpecifiedName = cmd.getVolumeName();
if (userSpecifiedName == null) {
userSpecifiedName = getRandomVolumeName();
}
VolumeVO volume = new VolumeVO(null, userSpecifiedName, -1, -1, -1, -1, new Long(-1), null, null, 0, Volume.VolumeType.DATADISK);
volume.setPoolId(null);
volume.setDataCenterId(zoneId);
volume.setPodId(null);
volume.setAccountId(targetAccount.getId());
volume.setDomainId(account.getDomainId().longValue());
volume.setMirrorState(MirrorState.NOT_MIRRORED);
volume.setDiskOfferingId(cmd.getDiskOfferingId());
volume.setStorageResourceType(StorageResourceType.STORAGE_POOL);
volume.setInstanceId(null);
volume.setUpdated(new Date());
volume.setStatus(AsyncInstanceCreateStatus.Creating);
volume.setDomainId(account.getDomainId().longValue());
volume = _volsDao.persist(volume);
return volume;
}
@Override @DB
public VolumeVO createVolume(CreateVolumeCmd cmd) {
VolumeVO createdVolume = _volsDao.findById(cmd.getId());
Long userId = UserContext.current().getUserId();
if (cmd.getSnapshotId() != null) {
return createVolumeFromSnapshot(createdVolume, cmd.getSnapshotId());
} else {
DataCenterVO dc = _dcDao.findById(cmd.getZoneId());
DiskOfferingVO diskOffering = _diskOfferingDao.findById(cmd.getDiskOfferingId());
long size = diskOffering.getDiskSize();
try {
List<StoragePoolVO> poolsToAvoid = new ArrayList<StoragePoolVO>();
Set<Long> podsToAvoid = new HashSet<Long>();
Pair<HostPodVO, Long> pod = null;
while ((pod = _agentMgr.findPod(null, null, dc, createdVolume.getAccountId(), podsToAvoid)) != null) {
if ((createdVolume = createVolume(createdVolume, null, null, dc, pod.first(), null, null, diskOffering, poolsToAvoid, size)) != null) {
break;
} else {
podsToAvoid.add(pod.first().getId());
}
}
// Create an event
EventVO event = new EventVO();
event.setAccountId(createdVolume.getAccountId());
event.setUserId(userId);
event.setType(EventTypes.EVENT_VOLUME_CREATE);
event.setStartId(startEventId); // FIX other event stuff as well for createVolume
Transaction txn = Transaction.currentTxn();
txn.start();
if (createdVolume != null) {
// Increment the number of volumes
_accountMgr.incrementResourceCount(createdVolume.getAccountId(), ResourceType.volume);
// Set event parameters
long sizeMB = createdVolume.getSize() / (1024 * 1024);
StoragePoolVO pool = _storagePoolDao.findById(createdVolume.getPoolId());
String eventParams = "id=" + createdVolume.getId() + "\ndoId=" + diskOffering.getId() + "\ntId=" + -1 + "\ndcId=" + dc.getId() + "\nsize=" + sizeMB;
event.setLevel(EventVO.LEVEL_INFO);
event.setDescription("Created volume: " + createdVolume.getName() + " with size: " + sizeMB + " MB in pool: " + pool.getName());
event.setParameters(eventParams);
_eventDao.persist(event);
} else {
// Mark the existing volume record as corrupted
createdVolume.setStatus(AsyncInstanceCreateStatus.Corrupted);
createdVolume.setDestroyed(true);
_volsDao.update(createdVolume.getId(), createdVolume);
}
txn.commit();
} catch (Exception e) {
s_logger.error("Unhandled exception while creating volume " + createdVolume.getName(), e);
}
}
return createdVolume;
}
@ -2333,4 +2410,14 @@ public class StorageManagerImpl implements StorageManager {
return true;
}
}
private boolean validateCustomVolumeSizeRange(long size) throws InvalidParameterValueException {
if (size<0 || (size>0 && size < 1)) {
throw new InvalidParameterValueException("Please specify a size of at least 1 Gb.");
} else if (size > _maxVolumeSizeInGb) {
throw new InvalidParameterValueException("The maximum size allowed is " + _maxVolumeSizeInGb + " Gb.");
}
return true;
}
}

View File

@ -71,11 +71,6 @@ public interface SnapshotManager extends Manager {
*/
SnapshotVO createSnapshotImpl(long volumeId, List<Long> policyIds) throws InvalidParameterValueException, ResourceAllocationException, InternalErrorException;
/**
* Creates a volume from the specified snapshot. A new volume is returned which is not attached to any VM Instance
*/
long createVolumeFromSnapshotAsync(long userId, long accountId, long snapshotId, String volumeName) throws InternalErrorException;
/**
* Destroys the specified snapshot from secondary storage
*/

View File

@ -1087,36 +1087,6 @@ public class SnapshotManagerImpl implements SnapshotManager {
txn.commit();
}
@Override
public long createVolumeFromSnapshotAsync(long userId, long accountId, long snapshotId, String volumeName) throws InternalErrorException {
// Precondition the snapshot is valid
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
VolumeVO volume = _volsDao.findById(snapshot.getVolumeId());
EventVO event = new EventVO();
event.setUserId(userId);
event.setAccountId(accountId);
event.setType(EventTypes.EVENT_VOLUME_CREATE);
event.setState(EventState.Scheduled);
event.setDescription("Scheduled async job for creating volume from snapshot with id: "+snapshotId);
event = _eventDao.persist(event);
SnapshotOperationParam param = new SnapshotOperationParam(userId, accountId, volume.getId(), snapshotId, volumeName);
param.setEventId(event.getId());
Gson gson = GsonHelper.getBuilder().create();
AsyncJobVO job = new AsyncJobVO();
job.setUserId(userId);
job.setAccountId(snapshot.getAccountId());
job.setCmd("CreateVolumeFromSnapshot");
job.setCmdInfo(gson.toJson(param));
job.setCmdOriginator(CreateVolumeCmd.getResultObjectName());
return _asyncMgr.submitAsyncJob(job);
}
@Override
public boolean deleteSnapshotDirsForAccount(long accountId) {
@ -1531,6 +1501,12 @@ public class SnapshotManagerImpl implements SnapshotManager {
PoliciesForSnapSearch.join("policyRef", policyRefSearch, policyRefSearch.entity().getPolicyId(), PoliciesForSnapSearch.entity().getId());
policyRefSearch.done();
PoliciesForSnapSearch.done();
String maxVolumeSizeInGbString = configDao.get("max.volume.size.gb");
int maxVolumeSizeGb = NumbersUtil.parseInt(maxVolumeSizeInGbString, 2000);
_maxVolumeSizeInGb = maxVolumeSizeGb;
s_logger.info("Snapshot Manager is configured.");
return true;