Skip to content

Commit 1b79466

Browse files
shwstpprrohityadavcloud
authored andcommitted
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 <[email protected]>
1 parent 46752e9 commit 1b79466

File tree

2 files changed

+166
-18
lines changed

2 files changed

+166
-18
lines changed

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -555,19 +555,29 @@ public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationExcept
555555
Long zoneId = cmd.getZoneId();
556556
Long diskOfferingId = null;
557557
DiskOfferingVO diskOffering = null;
558-
Storage.ProvisioningType provisioningType;
559558
Long size = null;
560559
Long minIops = null;
561560
Long maxIops = null;
562561
// Volume VO used for extracting the source template id
563562
VolumeVO parentVolume = null;
564563

565564
// validate input parameters before creating the volume
566-
if ((cmd.getSnapshotId() == null && cmd.getDiskOfferingId() == null) || (cmd.getSnapshotId() != null && cmd.getDiskOfferingId() != null)) {
567-
throw new InvalidParameterValueException("Either disk Offering Id or snapshot Id must be passed whilst creating volume");
565+
if (cmd.getSnapshotId() == null && cmd.getDiskOfferingId() == null) {
566+
throw new InvalidParameterValueException("At least one of disk Offering ID or snapshot ID must be passed whilst creating volume");
568567
}
569568

570-
if (cmd.getSnapshotId() == null) {// create a new volume
569+
// disallow passing disk offering ID with DATA disk volume snapshots
570+
if (cmd.getSnapshotId() != null && cmd.getDiskOfferingId() != null) {
571+
SnapshotVO snapshot = _snapshotDao.findById(cmd.getSnapshotId());
572+
if (snapshot != null) {
573+
parentVolume = _volsDao.findByIdIncludingRemoved(snapshot.getVolumeId());
574+
if (parentVolume != null && parentVolume.getVolumeType() != Volume.Type.ROOT)
575+
throw new InvalidParameterValueException("Disk Offering ID cannot be passed whilst creating volume from snapshot other than ROOT disk snapshots");
576+
}
577+
parentVolume = null;
578+
}
579+
580+
if (cmd.getDiskOfferingId() != null) { // create a new volume
571581

572582
diskOfferingId = cmd.getDiskOfferingId();
573583
size = cmd.getSize();
@@ -641,13 +651,13 @@ public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationExcept
641651
}
642652
}
643653

644-
provisioningType = diskOffering.getProvisioningType();
645-
646654
if (!validateVolumeSizeRange(size)) {// convert size from mb to gb
647655
// for validation
648656
throw new InvalidParameterValueException("Invalid size for custom volume creation: " + size + " ,max volume size is:" + _maxVolumeSizeInGb);
649657
}
650-
} else { // create volume from snapshot
658+
}
659+
660+
if (cmd.getSnapshotId() != null) { // create volume from snapshot
651661
Long snapshotId = cmd.getSnapshotId();
652662
SnapshotVO snapshotCheck = _snapshotDao.findById(snapshotId);
653663
if (snapshotCheck == null) {
@@ -659,19 +669,25 @@ public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationExcept
659669
}
660670
parentVolume = _volsDao.findByIdIncludingRemoved(snapshotCheck.getVolumeId());
661671

662-
diskOfferingId = snapshotCheck.getDiskOfferingId();
663-
diskOffering = _diskOfferingDao.findById(diskOfferingId);
664672
if (zoneId == null) {
665673
// if zoneId is not provided, we default to create volume in the same zone as the snapshot zone.
666674
zoneId = snapshotCheck.getDataCenterId();
667675
}
668-
size = snapshotCheck.getSize(); // ; disk offering is used for tags
669-
// purposes
670676

671-
minIops = snapshotCheck.getMinIops();
672-
maxIops = snapshotCheck.getMaxIops();
677+
if (diskOffering == null) { // Pure snapshot is being used to create volume.
678+
diskOfferingId = snapshotCheck.getDiskOfferingId();
679+
diskOffering = _diskOfferingDao.findById(diskOfferingId);
680+
681+
minIops = snapshotCheck.getMinIops();
682+
maxIops = snapshotCheck.getMaxIops();
683+
size = snapshotCheck.getSize(); // ; disk offering is used for tags purposes
684+
} else {
685+
if (size < snapshotCheck.getSize()) {
686+
throw new InvalidParameterValueException(String.format("Invalid size for volume creation: %dGB, snapshot size is: %dGB",
687+
size / (1024 * 1024 * 1024), snapshotCheck.getSize() / (1024 * 1024 * 1024)));
688+
}
689+
}
673690

674-
provisioningType = diskOffering.getProvisioningType();
675691
// check snapshot permissions
676692
_accountMgr.checkAccess(caller, null, true, snapshotCheck);
677693

@@ -693,9 +709,10 @@ public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationExcept
693709
// permission check
694710
_accountMgr.checkAccess(caller, null, false, vm);
695711
}
696-
697712
}
698713

714+
Storage.ProvisioningType provisioningType = diskOffering.getProvisioningType();
715+
699716
// Check that the resource limit for primary storage won't be exceeded
700717
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, displayVolume, new Long(size));
701718

ui/scripts/storage.js

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,9 @@
19691969
} else {
19701970
args.$form.find('.form-item[rel=zoneid]').hide();
19711971
}
1972+
if(args.context.snapshots[0].volumetype!='ROOT') {
1973+
args.$form.find('.form-item[rel=diskOffering]').hide();
1974+
}
19721975
},
19731976
fields: {
19741977
name: {
@@ -2005,13 +2008,110 @@
20052008
}
20062009
});
20072010
}
2011+
},
2012+
diskOffering: {
2013+
label: 'label.disk.offering',
2014+
docID: 'helpVolumeDiskOffering',
2015+
select: function(args) {
2016+
var snapshotSizeInGB = Math.floor(args.context.snapshots[0].virtualsize/(1024 * 1024 * 1024))
2017+
$.ajax({
2018+
url: createURL("listDiskOfferings"),
2019+
dataType: "json",
2020+
async: false,
2021+
success: function(json) {
2022+
diskofferingObjs = json.listdiskofferingsresponse.diskoffering;
2023+
var items = [];
2024+
// Sort offerings list with size and keep custom offerings at end
2025+
for(var i=0;i<diskofferingObjs.length;i++) {
2026+
for(var j=i+1;j<diskofferingObjs.length;j++) {
2027+
if((diskofferingObjs[i].disksize>diskofferingObjs[j].disksize &&
2028+
diskofferingObjs[j].disksize!=0) ||
2029+
(diskofferingObjs[i].disksize==0 &&
2030+
diskofferingObjs[j].disksize!=0)) {
2031+
var temp = diskofferingObjs[i];
2032+
diskofferingObjs[i] = diskofferingObjs[j];
2033+
diskofferingObjs[j] = temp;
2034+
}
2035+
}
2036+
}
2037+
$(diskofferingObjs).each(function() {
2038+
if(this.disksize==0 || this.disksize>=snapshotSizeInGB) {
2039+
items.push({
2040+
id: this.id,
2041+
description: this.displaytext
2042+
});
2043+
}
2044+
});
2045+
args.response.success({
2046+
data: items
2047+
});
2048+
}
2049+
});
2050+
2051+
args.$select.change(function() {
2052+
var diskOfferingId = $(this).val();
2053+
selectedDiskOfferingObj = null;
2054+
$(diskofferingObjs).each(function() {
2055+
if (this.id == diskOfferingId) {
2056+
selectedDiskOfferingObj = this;
2057+
return false;
2058+
}
2059+
});
2060+
2061+
if (selectedDiskOfferingObj == null) return;
2062+
2063+
var $form = $(this).closest('form');
2064+
var $diskSize = $form.find('.form-item[rel=diskSize]');
2065+
if (selectedDiskOfferingObj.iscustomized == true) {
2066+
$diskSize.css('display', 'inline-block');
2067+
$form.find('input[name=diskSize]').val(''+snapshotSizeInGB);
2068+
} else {
2069+
$diskSize.hide();
2070+
}
2071+
2072+
var $minIops = $form.find('.form-item[rel=minIops]');
2073+
var $maxIops = $form.find('.form-item[rel=maxIops]');
2074+
if (selectedDiskOfferingObj.iscustomizediops == true) {
2075+
$minIops.css('display', 'inline-block');
2076+
$maxIops.css('display', 'inline-block');
2077+
} else {
2078+
$minIops.hide();
2079+
$maxIops.hide();
2080+
}
2081+
});
2082+
}
2083+
},
2084+
diskSize: {
2085+
label: 'label.disk.size.gb',
2086+
docID: 'helpVolumeSizeGb',
2087+
validation: {
2088+
required: true,
2089+
number: true
2090+
},
2091+
isHidden: true
2092+
},
2093+
minIops: {
2094+
label: 'label.disk.iops.min',
2095+
validation: {
2096+
required: false,
2097+
number: true
2098+
},
2099+
isHidden: true
2100+
},
2101+
maxIops: {
2102+
label: 'label.disk.iops.max',
2103+
validation: {
2104+
required: false,
2105+
number: true
2106+
},
2107+
isHidden: true
20082108
}
20092109
}
20102110
},
20112111
action: function(args) {
20122112
var data = {
2013-
snapshotid: args.context.snapshots[0].id,
2014-
name: args.data.name
2113+
name: args.data.name,
2114+
snapshotid: args.context.snapshots[0].id
20152115
};
20162116

20172117
if (args.$form.find('.form-item[rel=zoneid]').css("display") != "none" && args.data.zoneid != '') {
@@ -2020,6 +2120,35 @@
20202120
});
20212121
}
20222122

2123+
if (args.$form.find('.form-item[rel=diskOffering]').css("display") != "none") {
2124+
if (args.data.diskOffering) {
2125+
$.extend(data, {
2126+
diskofferingid: args.data.diskOffering
2127+
});
2128+
}
2129+
if (selectedDiskOfferingObj) {
2130+
if(selectedDiskOfferingObj.iscustomized == true) {
2131+
$.extend(data, {
2132+
size: args.data.diskSize
2133+
});
2134+
}
2135+
2136+
if (selectedDiskOfferingObj.iscustomizediops == true) {
2137+
if (args.data.minIops != "" && args.data.minIops > 0) {
2138+
$.extend(data, {
2139+
miniops: args.data.minIops
2140+
});
2141+
}
2142+
2143+
if (args.data.maxIops != "" && args.data.maxIops > 0) {
2144+
$.extend(data, {
2145+
maxiops: args.data.maxIops
2146+
});
2147+
}
2148+
}
2149+
}
2150+
}
2151+
20232152
$.ajax({
20242153
url: createURL('createVolume'),
20252154
data: data,
@@ -2036,14 +2165,16 @@
20362165
}
20372166
}
20382167
});
2168+
},
2169+
error: function(json) {
2170+
args.response.error(parseXMLHttpResponse(json));
20392171
}
20402172
});
20412173
},
20422174
notification: {
20432175
poll: pollAsyncJobResult
20442176
}
20452177
},
2046-
20472178
revertSnapshot: {
20482179
label: 'label.action.revert.snapshot',
20492180
messages: {

0 commit comments

Comments
 (0)