@Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true)
public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationException {
// FIXME: some of the scheduled event stuff might be missing here...
Account caller = UserContext.current().getCaller();
long ownerId = cmd.getEntityOwnerId();
// permission check
_accountMgr.checkAccess(caller, null, true, _accountMgr.getActiveAccountById(ownerId));
// Check that the resource limit for volumes won't be exceeded
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.volume);
Long zoneId = cmd.getZoneId();
Long diskOfferingId = null;
DiskOfferingVO diskOffering = null;
Long size = null;
// validate input parameters before creating the volume
if ((cmd.getSnapshotId() == null && cmd.getDiskOfferingId() == null) || (cmd.getSnapshotId() != null && cmd.getDiskOfferingId() != null)) {
throw new InvalidParameterValueException("Either disk Offering Id or snapshot Id must be passed whilst creating volume");
}
if (cmd.getSnapshotId() == null) {// create a new volume
diskOfferingId = cmd.getDiskOfferingId();
size = cmd.getSize();
Long sizeInGB = size;
if (size != null) {
if (size > 0) {
size = size * 1024 * 1024 * 1024; // user specify size in GB
} else {
throw new InvalidParameterValueException("Disk size must be larger than 0");
}
}
// Check that the the disk offering is specified
diskOffering = _diskOfferingDao.findById(diskOfferingId);
if ((diskOffering == null) || diskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(diskOffering.getType())) {
throw new InvalidParameterValueException("Please specify a valid disk offering.");
}
if (diskOffering.isCustomized()) {
if (size == null) {
throw new InvalidParameterValueException("This disk offering requires a custom size specified");
}
if ((sizeInGB < _customDiskOfferingMinSize) || (sizeInGB > _customDiskOfferingMaxSize)) {
throw new InvalidParameterValueException("Volume size: " + sizeInGB + "GB is out of allowed range. Max: " + _customDiskOfferingMaxSize + " Min:" + _customDiskOfferingMinSize);
}
}
if (!diskOffering.isCustomized() && size != null) {
throw new InvalidParameterValueException("This disk offering does not allow custom size");
}
if (diskOffering.getDomainId() == null) {
// do nothing as offering is public
} else {
_configMgr.checkDiskOfferingAccess(caller, diskOffering);
}
if (diskOffering.getDiskSize() > 0) {
size = diskOffering.getDiskSize();
}
if (!validateVolumeSizeRange(size)) {// convert size from mb to gb for validation
throw new InvalidParameterValueException("Invalid size for custom volume creation: " + size + " ,max volume size is:" + _maxVolumeSizeInGb);
}
} else { // create volume from snapshot
Long snapshotId = cmd.getSnapshotId();
SnapshotVO snapshotCheck = _snapshotDao.findById(snapshotId);
if (snapshotCheck == null) {
throw new InvalidParameterValueException("unable to find a snapshot with id " + snapshotId);
}
if (snapshotCheck.getStatus() != Snapshot.Status.BackedUp) {
throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.Status.BackedUp + " state yet and can't be used for volume creation");
}
diskOfferingId = snapshotCheck.getDiskOfferingId();
diskOffering = _diskOfferingDao.findById(diskOfferingId);
zoneId = snapshotCheck.getDataCenterId();
size = snapshotCheck.getSize(); // ; disk offering is used for tags purposes
// check snapshot permissions
_accountMgr.checkAccess(caller, null, true, snapshotCheck);
/*
* // bug #11428. Operation not supported if vmware and snapshots parent volume = ROOT
* if(snapshotCheck.getHypervisorType() == HypervisorType.VMware
* && _volumeDao.findByIdIncludingRemoved(snapshotCheck.getVolumeId()).getVolumeType() == Type.ROOT){
* throw new UnsupportedServiceException("operation not supported, snapshot with id " + snapshotId +
* " is created from ROOT volume");
* }
*
*/
}
// Verify that zone exists
DataCenterVO zone = _dcDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
}
// Check if zone is disabled
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId);
}
// If local storage is disabled then creation of volume with local disk offering not allowed
if (!zone.isLocalStorageEnabled() && diskOffering.getUseLocalStorage()) {
throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it");
}
// Check that there is appropriate primary storage pool in the specified zone
List<StoragePoolVO> storagePools = _storagePoolDao.listByDataCenterId(zoneId);
boolean appropriatePoolExists = false;
if (!diskOffering.getUseLocalStorage()) {
for (StoragePoolVO storagePool : storagePools) {
if (storagePool.isShared()) {
appropriatePoolExists = true;
break;
}
}
} else {
for (StoragePoolVO storagePool : storagePools) {
if (storagePool.isLocal()) {
appropriatePoolExists = true;
break;
}
}
}
// Check that there is at least one host in the specified zone
List<HostVO> hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByType(Host.Type.Routing, zoneId);
if (hosts.isEmpty()) {
throw new InvalidParameterValueException("There is no workable host in data center id " + zoneId + ", please check hosts' agent status and see if they are disabled");
}
if (!appropriatePoolExists) {
String storageType = diskOffering.getUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString();
throw new InvalidParameterValueException("Volume's disk offering uses " + storageType + " storage, please specify a zone that has at least one " + storageType + " primary storage pool.");
}
String userSpecifiedName = cmd.getVolumeName();
if (userSpecifiedName == null) {
userSpecifiedName = getRandomVolumeName();
}
Transaction txn = Transaction.currentTxn();
txn.start();
VolumeVO volume = new VolumeVO(userSpecifiedName, -1, -1, -1, -1, new Long(-1), null, null, 0, Volume.Type.DATADISK);
volume.setPoolId(null);
volume.setDataCenterId(zoneId);
volume.setPodId(null);
volume.setAccountId(ownerId);
volume.setDomainId(((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId()));
volume.setDiskOfferingId(diskOfferingId);
volume.setSize(size);
volume.setInstanceId(null);
volume.setUpdated(new Date());
volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId());
volume = _volsDao.persist(volume);
if(cmd.getSnapshotId() == null){
//for volume created from snapshot, create usage event after volume creation
UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, null, size);