This is an automated email from the ASF dual-hosted git repository. rohit pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push: new 1b79466 server: allow disk offering selection for volume from snapshot (#3246) 1b79466 is described below commit 1b79466dd94a12f1b14dd1e5fc51e2e54b578c67 Author: Abhishek Kumar <abhishek.ku...@shapeblue.com> AuthorDate: Tue Jun 25 22:00:24 2019 +0530 server: allow disk offering selection for volume from snapshot (#3246) Problem: Volume created from a snapshot does not show its disk offering. Root Cause: The volume created from a snapshot of a root disk does not have a disk offering therefore the disk offering of the created volume from the snapshot is empty. Solution: Refactored createVolume API and extended UI to allow user to select a disk offering while creating a volume using a root disk volume snapshot. For creating volumes using data disk volume snapshot, the disk offering given by the snapshot will be assigned. Disk offering selection in the UI form for volume creation from snapshot is depicted in screenshot below. Signed-off-by: Abhishek Kumar <abhishek.ku...@shapeblue.com> --- .../com/cloud/storage/VolumeApiServiceImpl.java | 47 ++++--- ui/scripts/storage.js | 137 ++++++++++++++++++++- 2 files changed, 166 insertions(+), 18 deletions(-) diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 37f6d95..099f88b 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -555,7 +555,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; @@ -563,11 +562,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(); @@ -641,13 +651,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) { @@ -659,19 +669,25 @@ 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(); } - size = snapshotCheck.getSize(); // ; disk offering is used for tags - // purposes - minIops = snapshotCheck.getMinIops(); - maxIops = snapshotCheck.getMaxIops(); + if (diskOffering == null) { // Pure snapshot is being used to create volume. + diskOfferingId = snapshotCheck.getDiskOfferingId(); + diskOffering = _diskOfferingDao.findById(diskOfferingId); + + 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); @@ -693,9 +709,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 f1d3330..789af42 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -1969,6 +1969,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: { @@ -2005,13 +2008,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;i<diskofferingObjs.length;i++) { + for(var j=i+1;j<diskofferingObjs.length;j++) { + if((diskofferingObjs[i].disksize>diskofferingObjs[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 != '') { @@ -2020,6 +2120,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, @@ -2036,6 +2165,9 @@ } } }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); } }); }, @@ -2043,7 +2175,6 @@ poll: pollAsyncJobResult } }, - revertSnapshot: { label: 'label.action.revert.snapshot', messages: {