Skip to content

Commit d638d04

Browse files
author
Marcus Sorensen
committed
CLOUDSTACK-6181: Merge of resize root feature (resize-root branch)
1 parent ae1d6a7 commit d638d04

16 files changed

Lines changed: 479 additions & 59 deletions

File tree

api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
121121
@Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId")
122122
private Long size;
123123

124+
@Parameter(name = ApiConstants.ROOT_DISK_SIZE, type = CommandType.LONG, description = "Optional field to resize root disk on deploy. Only applies to template-based deployments. Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided")
125+
private Long rootdisksize;
126+
124127
@Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine")
125128
private String group;
126129

@@ -226,6 +229,9 @@ public Map<String, String> getDetails() {
226229
}
227230
}
228231
}
232+
if (rootdisksize != null && !customparameterMap.containsKey("rootdisksize")) {
233+
customparameterMap.put("rootdisksize", rootdisksize.toString());
234+
}
229235
return customparameterMap;
230236
}
231237

debian/rules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ build: build-indep
3535
build-indep: build-indep-stamp
3636

3737
build-indep-stamp: configure
38-
mvn clean package -Pawsapi -DskipTests -Dsystemvm \
38+
mvn -T C1.5 clean package -Pawsapi -DskipTests -Dsystemvm \
3939
-Dcs.replace.properties=replace.properties.tmp \
4040
${ACS_BUILD_OPTS}
4141
touch $@

engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,8 +612,14 @@ public DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering
612612
assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template really....";
613613

614614
Long size = _tmpltMgr.getTemplateSize(template.getId(), vm.getDataCenterId());
615-
if (rootDisksize != null) {
616-
size = (rootDisksize * 1024 * 1024 * 1024);
615+
if (rootDisksize != null ) {
616+
rootDisksize = rootDisksize * 1024 * 1024 * 1024;
617+
if (rootDisksize > size) {
618+
s_logger.debug("Using root disk size of " + rootDisksize + " for volume " + name);
619+
size = rootDisksize;
620+
} else {
621+
s_logger.debug("Using root disk size of " + size + " for volume " + name + "since specified root disk size of " + rootDisksize + " is smaller than template");
622+
}
617623
}
618624

619625
minIops = minIops != null ? minIops : offering.getMinIops();

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,6 +1827,12 @@ public Answer execute(ResizeVolumeCommand cmd) {
18271827
boolean shrinkOk = cmd.getShrinkOk();
18281828
StorageFilerTO spool = cmd.getPool();
18291829

1830+
if ( currentSize == newSize) {
1831+
// nothing to do
1832+
s_logger.info("No need to resize volume: current size " + currentSize + " is same as new size " + newSize);
1833+
return new ResizeVolumeAnswer(cmd, true, "success", currentSize);
1834+
}
1835+
18301836
try {
18311837
KVMStoragePool pool = _storagePoolMgr.getStoragePool(spool.getType(), spool.getUuid());
18321838
KVMPhysicalDisk vol = pool.getPhysicalDisk(volid);

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,17 +302,21 @@ public boolean deleteStoragePool(StoragePoolType type, String uuid) {
302302
}
303303

304304
public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, KVMStoragePool destPool, int timeout) {
305+
return createDiskFromTemplate(template, name, destPool, template.getSize(), timeout);
306+
}
307+
308+
public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, KVMStoragePool destPool, long size, int timeout) {
305309
StorageAdaptor adaptor = getStorageAdaptor(destPool.getType());
306310

307311
// LibvirtStorageAdaptor-specific statement
308312
if (destPool.getType() == StoragePoolType.RBD) {
309-
return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, template.getSize(), destPool, timeout);
313+
return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, size, destPool, timeout);
310314
} else if (destPool.getType() == StoragePoolType.CLVM) {
311-
return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, template.getSize(), destPool, timeout);
315+
return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, size, destPool, timeout);
312316
} else if (template.getFormat() == PhysicalDiskFormat.DIR) {
313-
return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.DIR, template.getSize(), destPool, timeout);
317+
return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.DIR, size, destPool, timeout);
314318
} else {
315-
return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.QCOW2, template.getSize(), destPool, timeout);
319+
return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.QCOW2, size, destPool, timeout);
316320
}
317321
}
318322

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,29 @@ public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) {
182182
break;
183183
}
184184
}
185-
if (tmplVol == null) {
186-
return new PrimaryStorageDownloadAnswer("Failed to get template from pool: " + secondaryPool.getUuid());
187-
}
188185
} else {
189186
tmplVol = secondaryPool.getPhysicalDisk(tmpltname);
190187
}
191188

189+
if (tmplVol == null) {
190+
return new PrimaryStorageDownloadAnswer("Failed to get template from pool: " + secondaryPool.getUuid());
191+
}
192+
192193
/* Copy volume to primary storage */
193194
s_logger.debug("Copying template to primary storage, template format is " + tmplVol.getFormat() );
194195
KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid());
195196

196197
KVMPhysicalDisk primaryVol = null;
197198
if (destData instanceof VolumeObjectTO) {
198199
VolumeObjectTO volume = (VolumeObjectTO)destData;
200+
// pass along volume's target size if it's bigger than template's size, for storage types that copy template rather than cloning on deploy
201+
if (volume.getSize() != null && volume.getSize() > tmplVol.getVirtualSize()) {
202+
s_logger.debug("Using configured size of " + volume.getSize());
203+
tmplVol.setSize(volume.getSize());
204+
tmplVol.setVirtualSize(volume.getSize());
205+
} else {
206+
s_logger.debug("Using template's size of " + tmplVol.getVirtualSize());
207+
}
199208
primaryVol = storagePoolMgr.copyPhysicalDisk(tmplVol, volume.getUuid(), primaryPool, cmd.getWaitInMillSeconds());
200209
} else if (destData instanceof TemplateObjectTO) {
201210
TemplateObjectTO destTempl = (TemplateObjectTO)destData;
@@ -239,7 +248,7 @@ else if (primaryVol.getFormat() == PhysicalDiskFormat.QCOW2) {
239248
}
240249

241250
// this is much like PrimaryStorageDownloadCommand, but keeping it separate. copies template direct to root disk
242-
private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool, String volUuid, int timeout) {
251+
private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool, String volUuid, Long size, int timeout) {
243252
int index = templateUrl.lastIndexOf("/");
244253
String mountpoint = templateUrl.substring(0, index);
245254
String templateName = null;
@@ -275,6 +284,14 @@ private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStorage
275284

276285
/* Copy volume to primary storage */
277286

287+
if (size > templateVol.getSize()) {
288+
s_logger.debug("Overriding provided template's size with new size " + size);
289+
templateVol.setSize(size);
290+
templateVol.setVirtualSize(size);
291+
} else {
292+
s_logger.debug("Using templates disk size of " + templateVol.getVirtualSize() + "since size passed was " + size);
293+
}
294+
278295
KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(templateVol, volUuid, primaryPool, timeout);
279296
return primaryVol;
280297
} catch (CloudRuntimeException e) {
@@ -306,14 +323,14 @@ public Answer cloneVolumeFromBaseTemplate(CopyCommand cmd) {
306323

307324
if (primaryPool.getType() == StoragePoolType.CLVM) {
308325
templatePath = ((NfsTO)imageStore).getUrl() + File.separator + templatePath;
309-
vol = templateToPrimaryDownload(templatePath, primaryPool, volume.getUuid(), cmd.getWaitInMillSeconds());
326+
vol = templateToPrimaryDownload(templatePath, primaryPool, volume.getUuid(), volume.getSize(), cmd.getWaitInMillSeconds());
310327
} else {
311328
if (templatePath.contains("/mnt")) {
312329
//upgrade issue, if the path contains path, need to extract the volume uuid from path
313330
templatePath = templatePath.substring(templatePath.lastIndexOf(File.separator) + 1);
314331
}
315332
BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath);
316-
vol = storagePoolMgr.createDiskFromTemplate(BaseVol, volume.getUuid(), BaseVol.getPool(), cmd.getWaitInMillSeconds());
333+
vol = storagePoolMgr.createDiskFromTemplate(BaseVol, volume.getUuid(), BaseVol.getPool(), volume.getSize(), cmd.getWaitInMillSeconds());
317334
}
318335
if (vol == null) {
319336
return new CopyCmdAnswer(" Can't create storage volume on storage pool");

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -823,27 +823,48 @@ public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String n
823823
} else if (format == PhysicalDiskFormat.QCOW2) {
824824
QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat());
825825
QemuImgFile destFile = new QemuImgFile(disk.getPath());
826+
if (size > template.getVirtualSize()) {
827+
destFile.setSize(size);
828+
} else {
829+
destFile.setSize(template.getVirtualSize());
830+
}
826831
QemuImg qemu = new QemuImg(timeout);
827832
qemu.create(destFile, backingFile);
828833
} else if (format == PhysicalDiskFormat.RAW) {
829834
QemuImgFile sourceFile = new QemuImgFile(template.getPath(), template.getFormat());
830835
QemuImgFile destFile = new QemuImgFile(disk.getPath(), PhysicalDiskFormat.RAW);
836+
if (size > template.getVirtualSize()) {
837+
destFile.setSize(size);
838+
} else {
839+
destFile.setSize(template.getVirtualSize());
840+
}
831841
QemuImg qemu = new QemuImg(timeout);
832842
qemu.convert(sourceFile, destFile);
833843
}
834844
} else {
835845
format = PhysicalDiskFormat.RAW;
836846
disk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + newUuid, newUuid, destPool);
837847
disk.setFormat(format);
838-
disk.setSize(template.getVirtualSize());
839-
disk.setVirtualSize(disk.getSize());
848+
if (size > template.getVirtualSize()) {
849+
disk.setSize(size);
850+
disk.setVirtualSize(size);
851+
} else {
852+
// leave these as they were if size isn't applicable
853+
disk.setSize(template.getVirtualSize());
854+
disk.setVirtualSize(disk.getSize());
855+
}
840856

841857
QemuImg qemu = new QemuImg(timeout);
842858
QemuImgFile srcFile;
843859
QemuImgFile destFile =
844860
new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), destPool.getSourcePort(), destPool.getAuthUserName(),
845861
destPool.getAuthSecret(), disk.getPath()));
846862
destFile.setFormat(format);
863+
if (size > template.getVirtualSize()) {
864+
destFile.setSize(size);
865+
} else {
866+
destFile.setSize(template.getVirtualSize());
867+
}
847868

848869
if (srcPool.getType() != StoragePoolType.RBD) {
849870
srcFile = new QemuImgFile(template.getPath(), template.getFormat());
@@ -877,9 +898,9 @@ public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String n
877898
if (srcImage.isOldFormat()) {
878899
/* The source image is RBD format 1, we have to do a regular copy */
879900
s_logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName() +
880-
" is RBD format 1. We have to perform a regular copy (" + template.getVirtualSize() + " bytes)");
901+
" is RBD format 1. We have to perform a regular copy (" + disk.getVirtualSize() + " bytes)");
881902

882-
rbd.create(disk.getName(), template.getVirtualSize(), rbdFeatures, rbdOrder);
903+
rbd.create(disk.getName(), disk.getVirtualSize(), rbdFeatures, rbdOrder);
883904
RbdImage destImage = rbd.open(disk.getName());
884905

885906
s_logger.debug("Starting to copy " + srcImage.getName() + " to " + destImage.getName() + " in Ceph pool " + srcPool.getSourceDir());
@@ -923,7 +944,7 @@ public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String n
923944

924945
s_logger.debug("Creating " + disk.getName() + " on the destination cluster " + rDest.confGet("mon_host") + " in pool " +
925946
destPool.getSourceDir());
926-
dRbd.create(disk.getName(), template.getVirtualSize(), rbdFeatures, rbdOrder);
947+
dRbd.create(disk.getName(), disk.getVirtualSize(), rbdFeatures, rbdOrder);
927948

928949
RbdImage srcImage = sRbd.open(template.getName());
929950
RbdImage destImage = dRbd.open(disk.getName());

plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,12 @@ public void create(QemuImgFile file, QemuImgFile backingFile, Map<String, String
111111
}
112112

113113
s.add(file.getFileName());
114-
115-
if (backingFile == null) {
114+
if (file.getSize() != 0L) {
116115
s.add(Long.toString(file.getSize()));
116+
} else if (backingFile == null) {
117+
throw new QemuImgException("No size was passed, and no backing file was passed");
117118
}
119+
118120
String result = s.execute();
119121
if (result != null) {
120122
throw new QemuImgException(result);
@@ -206,6 +208,10 @@ public void convert(QemuImgFile srcFile, QemuImgFile destFile, Map<String, Strin
206208
if (result != null) {
207209
throw new QemuImgException(result);
208210
}
211+
212+
if (srcFile.getSize() < destFile.getSize()) {
213+
this.resize(destFile, destFile.getSize());
214+
}
209215
}
210216

211217
/**

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

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -701,35 +701,25 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep
701701

702702
newDiskOffering = _diskOfferingDao.findById(cmd.getNewDiskOfferingId());
703703

704-
/*
705-
* Volumes with no hypervisor have never been assigned, and can be
706-
* resized by recreating. perhaps in the future we can just update the
707-
* db entry for the volume
708-
*/
709-
if (_volsDao.getHypervisorType(volume.getId()) == HypervisorType.None) {
710-
throw new InvalidParameterValueException("Can't resize a volume that has never been attached, not sure which hypervisor type. Recreate volume to resize.");
711-
}
712-
713-
/* Only works for KVM/Xen for now */
714-
if (_volsDao.getHypervisorType(volume.getId()) != HypervisorType.KVM && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.XenServer
715-
&& _volsDao.getHypervisorType(volume.getId()) != HypervisorType.VMware) {
716-
throw new InvalidParameterValueException("Cloudstack currently only supports volumes marked as KVM or XenServer hypervisor for resize");
717-
}
718-
719-
if (volume.getState() != Volume.State.Ready) {
720-
throw new InvalidParameterValueException("Volume should be in ready state before attempting a resize");
704+
/* Only works for KVM/Xen/VMware for now, and volumes with 'None' since they're just allocated in db */
705+
if (_volsDao.getHypervisorType(volume.getId()) != HypervisorType.KVM
706+
&& _volsDao.getHypervisorType(volume.getId()) != HypervisorType.XenServer
707+
&& _volsDao.getHypervisorType(volume.getId()) != HypervisorType.VMware
708+
&& _volsDao.getHypervisorType(volume.getId()) != HypervisorType.None) {
709+
throw new InvalidParameterValueException("Cloudstack currently only supports volumes marked as KVM, VMware, XenServer hypervisor for resize");
721710
}
722711

723-
if (!volume.getVolumeType().equals(Volume.Type.DATADISK)) {
724-
throw new InvalidParameterValueException("Can only resize DATA volumes");
712+
if (volume.getState() != Volume.State.Ready && volume.getState() != Volume.State.Allocated) {
713+
throw new InvalidParameterValueException("Volume should be in ready or allocated state before attempting a resize. "
714+
+ "Volume " + volume.getUuid() + " state is:" + volume.getState());
725715
}
726716

727717
/*
728718
* figure out whether or not a new disk offering or size parameter is
729719
* required, get the correct size value
730720
*/
731721
if (newDiskOffering == null) {
732-
if (diskOffering.isCustomized()) {
722+
if (diskOffering.isCustomized() || volume.getVolumeType().equals(Volume.Type.ROOT)) {
733723
newSize = cmd.getSize();
734724

735725
if (newSize == null) {
@@ -741,6 +731,9 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep
741731
throw new InvalidParameterValueException("current offering" + volume.getDiskOfferingId() + " cannot be resized, need to specify a disk offering");
742732
}
743733
} else {
734+
if (!volume.getVolumeType().equals(Volume.Type.DATADISK)) {
735+
throw new InvalidParameterValueException("Can only resize Data volumes via new disk offering");
736+
}
744737

745738
if (newDiskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(newDiskOffering.getType())) {
746739
throw new InvalidParameterValueException("Disk offering ID is missing or invalid");
@@ -784,8 +777,6 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep
784777
/* does the caller have the authority to act on this volume? */
785778
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
786779

787-
UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
788-
789780
long currentSize = volume.getSize();
790781

791782
/*
@@ -805,6 +796,20 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep
805796
- currentSize));
806797
}
807798

799+
/* If this volume has never been beyond allocated state, short circuit everything and simply update the database */
800+
if (volume.getState() == Volume.State.Allocated) {
801+
s_logger.debug("Volume is allocated, but never created, simply updating database with new size");
802+
volume.setSize(newSize);
803+
if (newDiskOffering != null) {
804+
volume.setDiskOfferingId(cmd.getNewDiskOfferingId());
805+
}
806+
_volsDao.update(volume.getId(), volume);
807+
return volume;
808+
}
809+
810+
UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
811+
812+
808813
if (userVm != null) {
809814
// serialize VM operation
810815
AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();

0 commit comments

Comments
 (0)