diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index ca4f0b499fd..11ce1d25b5b 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -551,7 +551,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Long zoneId = cmd.getZoneId(); Long diskOfferingId = null; DiskOfferingVO diskOffering = null; - Storage.ProvisioningType provisioningType; Long size = null; Long minIops = null; Long maxIops = null; @@ -559,11 +558,22 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic VolumeVO parentVolume = 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 && cmd.getDiskOfferingId() == null) { + throw new InvalidParameterValueException("At least one of disk Offering ID or snapshot ID must be passed whilst creating volume"); } - if (cmd.getSnapshotId() == null) {// create a new volume + // disallow passing disk offering ID with DATA disk volume snapshots + if (cmd.getSnapshotId() != null && cmd.getDiskOfferingId() != null) { + SnapshotVO snapshot = _snapshotDao.findById(cmd.getSnapshotId()); + if (snapshot != null) { + parentVolume = _volsDao.findByIdIncludingRemoved(snapshot.getVolumeId()); + if (parentVolume != null && parentVolume.getVolumeType() != Volume.Type.ROOT) + throw new InvalidParameterValueException("Disk Offering ID cannot be passed whilst creating volume from snapshot other than ROOT disk snapshots"); + } + parentVolume = null; + } + + if (cmd.getDiskOfferingId() != null) { // create a new volume diskOfferingId = cmd.getDiskOfferingId(); size = cmd.getSize(); @@ -633,13 +643,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } - provisioningType = diskOffering.getProvisioningType(); - 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 + } + + if (cmd.getSnapshotId() != null) { // create volume from snapshot Long snapshotId = cmd.getSnapshotId(); SnapshotVO snapshotCheck = _snapshotDao.findById(snapshotId); if (snapshotCheck == null) { @@ -651,8 +661,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } parentVolume = _volsDao.findByIdIncludingRemoved(snapshotCheck.getVolumeId()); - diskOfferingId = snapshotCheck.getDiskOfferingId(); - diskOffering = _diskOfferingDao.findById(diskOfferingId); if (zoneId == null) { // if zoneId is not provided, we default to create volume in the same zone as the snapshot zone. zoneId = snapshotCheck.getDataCenterId(); @@ -660,13 +668,20 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _configMgr.checkDiskOfferingAccess(null, diskOffering, _dcDao.findById(zoneId)); - size = snapshotCheck.getSize(); // ; disk offering is used for tags - // purposes + if (diskOffering == null) { // Pure snapshot is being used to create volume. + diskOfferingId = snapshotCheck.getDiskOfferingId(); + diskOffering = _diskOfferingDao.findById(diskOfferingId); - minIops = snapshotCheck.getMinIops(); - maxIops = snapshotCheck.getMaxIops(); + minIops = snapshotCheck.getMinIops(); + maxIops = snapshotCheck.getMaxIops(); + size = snapshotCheck.getSize(); // ; disk offering is used for tags purposes + } else { + if (size < snapshotCheck.getSize()) { + throw new InvalidParameterValueException(String.format("Invalid size for volume creation: %dGB, snapshot size is: %dGB", + size / (1024 * 1024 * 1024), snapshotCheck.getSize() / (1024 * 1024 * 1024))); + } + } - provisioningType = diskOffering.getProvisioningType(); // check snapshot permissions _accountMgr.checkAccess(caller, null, true, snapshotCheck); @@ -688,9 +703,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic // permission check _accountMgr.checkAccess(caller, null, false, vm); } - } + Storage.ProvisioningType provisioningType = diskOffering.getProvisioningType(); + // Check that the resource limit for primary storage won't be exceeded _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, displayVolume, new Long(size)); diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js index 6c23f8f49e5..e01676571ec 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -1985,6 +1985,9 @@ } else { args.$form.find('.form-item[rel=zoneid]').hide(); } + if(args.context.snapshots[0].volumetype!='ROOT') { + args.$form.find('.form-item[rel=diskOffering]').hide(); + } }, fields: { name: { @@ -2021,13 +2024,110 @@ } }); } + }, + diskOffering: { + label: 'label.disk.offering', + docID: 'helpVolumeDiskOffering', + select: function(args) { + var snapshotSizeInGB = Math.floor(args.context.snapshots[0].virtualsize/(1024 * 1024 * 1024)) + $.ajax({ + url: createURL("listDiskOfferings"), + dataType: "json", + async: false, + success: function(json) { + diskofferingObjs = json.listdiskofferingsresponse.diskoffering; + var items = []; + // Sort offerings list with size and keep custom offerings at end + for(var i=0;idiskofferingObjs[j].disksize && + diskofferingObjs[j].disksize!=0) || + (diskofferingObjs[i].disksize==0 && + diskofferingObjs[j].disksize!=0)) { + var temp = diskofferingObjs[i]; + diskofferingObjs[i] = diskofferingObjs[j]; + diskofferingObjs[j] = temp; + } + } + } + $(diskofferingObjs).each(function() { + if(this.disksize==0 || this.disksize>=snapshotSizeInGB) { + items.push({ + id: this.id, + description: this.displaytext + }); + } + }); + args.response.success({ + data: items + }); + } + }); + + args.$select.change(function() { + var diskOfferingId = $(this).val(); + selectedDiskOfferingObj = null; + $(diskofferingObjs).each(function() { + if (this.id == diskOfferingId) { + selectedDiskOfferingObj = this; + return false; + } + }); + + if (selectedDiskOfferingObj == null) return; + + var $form = $(this).closest('form'); + var $diskSize = $form.find('.form-item[rel=diskSize]'); + if (selectedDiskOfferingObj.iscustomized == true) { + $diskSize.css('display', 'inline-block'); + $form.find('input[name=diskSize]').val(''+snapshotSizeInGB); + } else { + $diskSize.hide(); + } + + var $minIops = $form.find('.form-item[rel=minIops]'); + var $maxIops = $form.find('.form-item[rel=maxIops]'); + if (selectedDiskOfferingObj.iscustomizediops == true) { + $minIops.css('display', 'inline-block'); + $maxIops.css('display', 'inline-block'); + } else { + $minIops.hide(); + $maxIops.hide(); + } + }); + } + }, + diskSize: { + label: 'label.disk.size.gb', + docID: 'helpVolumeSizeGb', + validation: { + required: true, + number: true + }, + isHidden: true + }, + minIops: { + label: 'label.disk.iops.min', + validation: { + required: false, + number: true + }, + isHidden: true + }, + maxIops: { + label: 'label.disk.iops.max', + validation: { + required: false, + number: true + }, + isHidden: true } } }, action: function(args) { var data = { - snapshotid: args.context.snapshots[0].id, - name: args.data.name + name: args.data.name, + snapshotid: args.context.snapshots[0].id }; if (args.$form.find('.form-item[rel=zoneid]').css("display") != "none" && args.data.zoneid != '') { @@ -2036,6 +2136,35 @@ }); } + if (args.$form.find('.form-item[rel=diskOffering]').css("display") != "none") { + if (args.data.diskOffering) { + $.extend(data, { + diskofferingid: args.data.diskOffering + }); + } + if (selectedDiskOfferingObj) { + if(selectedDiskOfferingObj.iscustomized == true) { + $.extend(data, { + size: args.data.diskSize + }); + } + + if (selectedDiskOfferingObj.iscustomizediops == true) { + if (args.data.minIops != "" && args.data.minIops > 0) { + $.extend(data, { + miniops: args.data.minIops + }); + } + + if (args.data.maxIops != "" && args.data.maxIops > 0) { + $.extend(data, { + maxiops: args.data.maxIops + }); + } + } + } + } + $.ajax({ url: createURL('createVolume'), data: data, @@ -2052,6 +2181,9 @@ } } }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); } }); }, @@ -2059,7 +2191,6 @@ poll: pollAsyncJobResult } }, - revertSnapshot: { label: 'label.action.revert.snapshot', messages: {