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.kumar@shapeblue.com>
This commit is contained in:
Abhishek Kumar 2019-06-25 22:00:24 +05:30 committed by Rohit Yadav
parent 46752e9af6
commit 1b79466dd9
2 changed files with 166 additions and 18 deletions

View File

@ -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));

View File

@ -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: {