You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ml...@apache.org on 2014/03/18 06:39:30 UTC

[1/2] git commit: updated refs/heads/master to 345fc3c

Repository: cloudstack
Updated Branches:
  refs/heads/master ae1d6a771 -> 345fc3c8e


CLOUDSTACK-6181: Merge of resize root feature (resize-root branch)


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/d638d04c
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/d638d04c
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/d638d04c

Branch: refs/heads/master
Commit: d638d04cbf2d9ea7a185319364296cf4122b7ce3
Parents: ae1d6a7
Author: Marcus Sorensen <ma...@betterservers.com>
Authored: Fri Mar 14 03:40:08 2014 -0600
Committer: Marcus Sorensen <ma...@betterservers.com>
Committed: Fri Mar 14 07:17:33 2014 -0600

----------------------------------------------------------------------
 .../api/command/user/vm/DeployVMCmd.java        |   6 +
 debian/rules                                    |   2 +-
 .../orchestration/VolumeOrchestrator.java       |  10 +-
 .../kvm/resource/LibvirtComputingResource.java  |   6 +
 .../kvm/storage/KVMStoragePoolManager.java      |  12 +-
 .../kvm/storage/KVMStorageProcessor.java        |  29 +-
 .../kvm/storage/LibvirtStorageAdaptor.java      |  31 ++-
 .../apache/cloudstack/utils/qemu/QemuImg.java   |  10 +-
 .../com/cloud/storage/VolumeApiServiceImpl.java |  49 ++--
 server/src/com/cloud/vm/UserVmManagerImpl.java  |  22 +-
 .../smoke/test_deploy_vm_root_resize.py         | 263 +++++++++++++++++++
 test/integration/smoke/test_volumes.py          |  69 ++++-
 .../devcloud-kvm-advanced-fusion.cfg            |  16 +-
 tools/devcloud-kvm/devcloud-kvm-advanced.cfg    |   3 +-
 tools/devcloud-kvm/devcloud-kvm.cfg             |   3 +-
 tools/marvin/marvin/integration/lib/base.py     |   7 +-
 16 files changed, 479 insertions(+), 59 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 6ad092e..1414774 100755
--- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -121,6 +121,9 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
     @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId")
     private Long size;
 
+    @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")
+    private Long rootdisksize;
+
     @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine")
     private String group;
 
@@ -226,6 +229,9 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
                 }
             }
         }
+        if (rootdisksize != null && !customparameterMap.containsKey("rootdisksize")) {
+            customparameterMap.put("rootdisksize", rootdisksize.toString());
+        }
         return customparameterMap;
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/debian/rules
----------------------------------------------------------------------
diff --git a/debian/rules b/debian/rules
index 4edf893..197e243 100755
--- a/debian/rules
+++ b/debian/rules
@@ -35,7 +35,7 @@ build: build-indep
 build-indep: build-indep-stamp
 
 build-indep-stamp: configure
-	mvn clean package -Pawsapi -DskipTests -Dsystemvm \
+	mvn -T C1.5 clean package -Pawsapi -DskipTests -Dsystemvm \
         -Dcs.replace.properties=replace.properties.tmp \
 		${ACS_BUILD_OPTS}
 	touch $@

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
----------------------------------------------------------------------
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index cfa788f..5921128 100644
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -612,8 +612,14 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
         assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template really....";
 
         Long size = _tmpltMgr.getTemplateSize(template.getId(), vm.getDataCenterId());
-        if (rootDisksize != null) {
-            size = (rootDisksize * 1024 * 1024 * 1024);
+        if (rootDisksize != null ) {
+            rootDisksize = rootDisksize * 1024 * 1024 * 1024;
+            if (rootDisksize > size) {
+                s_logger.debug("Using root disk size of " + rootDisksize + " for volume " + name);
+                size = rootDisksize;
+            } else {
+                s_logger.debug("Using root disk size of " + size + " for volume " + name + "since specified root disk size of " + rootDisksize + " is smaller than template");
+            }
         }
 
         minIops = minIops != null ? minIops : offering.getMinIops();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index ec8bc11..49f23f5 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -1827,6 +1827,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         boolean shrinkOk = cmd.getShrinkOk();
         StorageFilerTO spool = cmd.getPool();
 
+        if ( currentSize == newSize) {
+            // nothing to do
+            s_logger.info("No need to resize volume: current size " + currentSize + " is same as new size " + newSize);
+            return new ResizeVolumeAnswer(cmd, true, "success", currentSize);
+        }
+
         try {
             KVMStoragePool pool = _storagePoolMgr.getStoragePool(spool.getType(), spool.getUuid());
             KVMPhysicalDisk vol = pool.getPhysicalDisk(volid);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
index 9e07e4b..583d48a 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
@@ -302,17 +302,21 @@ public class KVMStoragePoolManager {
     }
 
     public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, KVMStoragePool destPool, int timeout) {
+        return createDiskFromTemplate(template, name, destPool, template.getSize(), timeout);
+    }
+
+    public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, KVMStoragePool destPool, long size, int timeout) {
         StorageAdaptor adaptor = getStorageAdaptor(destPool.getType());
 
         // LibvirtStorageAdaptor-specific statement
         if (destPool.getType() == StoragePoolType.RBD) {
-            return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, template.getSize(), destPool, timeout);
+            return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, size, destPool, timeout);
         } else if (destPool.getType() == StoragePoolType.CLVM) {
-            return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, template.getSize(), destPool, timeout);
+            return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, size, destPool, timeout);
         } else if (template.getFormat() == PhysicalDiskFormat.DIR) {
-            return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.DIR, template.getSize(), destPool, timeout);
+            return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.DIR, size, destPool, timeout);
         } else {
-            return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.QCOW2, template.getSize(), destPool, timeout);
+            return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.QCOW2, size, destPool, timeout);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
index 2ea3b42..9813389 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
@@ -182,13 +182,14 @@ public class KVMStorageProcessor implements StorageProcessor {
                         break;
                     }
                 }
-                if (tmplVol == null) {
-                    return new PrimaryStorageDownloadAnswer("Failed to get template from pool: " + secondaryPool.getUuid());
-                }
             } else {
                 tmplVol = secondaryPool.getPhysicalDisk(tmpltname);
             }
 
+            if (tmplVol == null) {
+                return new PrimaryStorageDownloadAnswer("Failed to get template from pool: " + secondaryPool.getUuid());
+            }
+
             /* Copy volume to primary storage */
             s_logger.debug("Copying template to primary storage, template format is " + tmplVol.getFormat() );
             KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid());
@@ -196,6 +197,14 @@ public class KVMStorageProcessor implements StorageProcessor {
             KVMPhysicalDisk primaryVol = null;
             if (destData instanceof VolumeObjectTO) {
                 VolumeObjectTO volume = (VolumeObjectTO)destData;
+                // 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
+                if (volume.getSize() != null && volume.getSize() > tmplVol.getVirtualSize()) {
+                    s_logger.debug("Using configured size of " + volume.getSize());
+                    tmplVol.setSize(volume.getSize());
+                    tmplVol.setVirtualSize(volume.getSize());
+                } else {
+                    s_logger.debug("Using template's size of " + tmplVol.getVirtualSize());
+                }
                 primaryVol = storagePoolMgr.copyPhysicalDisk(tmplVol, volume.getUuid(), primaryPool, cmd.getWaitInMillSeconds());
             } else if (destData instanceof TemplateObjectTO) {
                 TemplateObjectTO destTempl = (TemplateObjectTO)destData;
@@ -239,7 +248,7 @@ public class KVMStorageProcessor implements StorageProcessor {
     }
 
     // this is much like PrimaryStorageDownloadCommand, but keeping it separate. copies template direct to root disk
-    private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool, String volUuid, int timeout) {
+    private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool, String volUuid, Long size, int timeout) {
         int index = templateUrl.lastIndexOf("/");
         String mountpoint = templateUrl.substring(0, index);
         String templateName = null;
@@ -275,6 +284,14 @@ public class KVMStorageProcessor implements StorageProcessor {
 
             /* Copy volume to primary storage */
 
+            if (size > templateVol.getSize()) {
+                s_logger.debug("Overriding provided template's size with new size " + size);
+                templateVol.setSize(size);
+                templateVol.setVirtualSize(size);
+            } else {
+                s_logger.debug("Using templates disk size of " + templateVol.getVirtualSize() + "since size passed was " + size);
+            }
+
             KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(templateVol, volUuid, primaryPool, timeout);
             return primaryVol;
         } catch (CloudRuntimeException e) {
@@ -306,14 +323,14 @@ public class KVMStorageProcessor implements StorageProcessor {
 
             if (primaryPool.getType() == StoragePoolType.CLVM) {
                 templatePath = ((NfsTO)imageStore).getUrl() + File.separator + templatePath;
-                vol = templateToPrimaryDownload(templatePath, primaryPool, volume.getUuid(), cmd.getWaitInMillSeconds());
+                vol = templateToPrimaryDownload(templatePath, primaryPool, volume.getUuid(), volume.getSize(), cmd.getWaitInMillSeconds());
             } else {
                 if (templatePath.contains("/mnt")) {
                     //upgrade issue, if the path contains path, need to extract the volume uuid from path
                     templatePath = templatePath.substring(templatePath.lastIndexOf(File.separator) + 1);
                 }
                 BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath);
-                vol = storagePoolMgr.createDiskFromTemplate(BaseVol, volume.getUuid(), BaseVol.getPool(), cmd.getWaitInMillSeconds());
+                vol = storagePoolMgr.createDiskFromTemplate(BaseVol, volume.getUuid(), BaseVol.getPool(), volume.getSize(), cmd.getWaitInMillSeconds());
             }
             if (vol == null) {
                 return new CopyCmdAnswer(" Can't create storage volume on storage pool");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
index 38ce32b..5de8bd2 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
@@ -823,11 +823,21 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
                 } else if (format == PhysicalDiskFormat.QCOW2) {
                     QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat());
                     QemuImgFile destFile = new QemuImgFile(disk.getPath());
+                    if (size > template.getVirtualSize()) {
+                        destFile.setSize(size);
+                    } else {
+                        destFile.setSize(template.getVirtualSize());
+                    }
                     QemuImg qemu = new QemuImg(timeout);
                     qemu.create(destFile, backingFile);
                 } else if (format == PhysicalDiskFormat.RAW) {
                     QemuImgFile sourceFile = new QemuImgFile(template.getPath(), template.getFormat());
                     QemuImgFile destFile = new QemuImgFile(disk.getPath(), PhysicalDiskFormat.RAW);
+                    if (size > template.getVirtualSize()) {
+                        destFile.setSize(size);
+                    } else {
+                        destFile.setSize(template.getVirtualSize());
+                    }
                     QemuImg qemu = new QemuImg(timeout);
                     qemu.convert(sourceFile, destFile);
                 }
@@ -835,8 +845,14 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
                 format = PhysicalDiskFormat.RAW;
                 disk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + newUuid, newUuid, destPool);
                 disk.setFormat(format);
-                disk.setSize(template.getVirtualSize());
-                disk.setVirtualSize(disk.getSize());
+                if (size > template.getVirtualSize()) {
+                    disk.setSize(size);
+                    disk.setVirtualSize(size);
+                } else {
+                    // leave these as they were if size isn't applicable
+                    disk.setSize(template.getVirtualSize());
+                    disk.setVirtualSize(disk.getSize());
+                }
 
                 QemuImg qemu = new QemuImg(timeout);
                 QemuImgFile srcFile;
@@ -844,6 +860,11 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
                         new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), destPool.getSourcePort(), destPool.getAuthUserName(),
                                 destPool.getAuthSecret(), disk.getPath()));
                 destFile.setFormat(format);
+                if (size > template.getVirtualSize()) {
+                    destFile.setSize(size);
+                } else {
+                    destFile.setSize(template.getVirtualSize());
+                }
 
                 if (srcPool.getType() != StoragePoolType.RBD) {
                     srcFile = new QemuImgFile(template.getPath(), template.getFormat());
@@ -877,9 +898,9 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
                             if (srcImage.isOldFormat()) {
                                 /* The source image is RBD format 1, we have to do a regular copy */
                                 s_logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName() +
-                                        " is RBD format 1. We have to perform a regular copy (" + template.getVirtualSize() + " bytes)");
+                                        " is RBD format 1. We have to perform a regular copy (" + disk.getVirtualSize() + " bytes)");
 
-                                rbd.create(disk.getName(), template.getVirtualSize(), rbdFeatures, rbdOrder);
+                                rbd.create(disk.getName(), disk.getVirtualSize(), rbdFeatures, rbdOrder);
                                 RbdImage destImage = rbd.open(disk.getName());
 
                                 s_logger.debug("Starting to copy " + srcImage.getName() + " to " + destImage.getName() + " in Ceph pool " + srcPool.getSourceDir());
@@ -923,7 +944,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
 
                             s_logger.debug("Creating " + disk.getName() + " on the destination cluster " + rDest.confGet("mon_host") + " in pool " +
                                     destPool.getSourceDir());
-                            dRbd.create(disk.getName(), template.getVirtualSize(), rbdFeatures, rbdOrder);
+                            dRbd.create(disk.getName(), disk.getVirtualSize(), rbdFeatures, rbdOrder);
 
                             RbdImage srcImage = sRbd.open(template.getName());
                             RbdImage destImage = dRbd.open(disk.getName());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java
index b380815..4bec375 100644
--- a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java
+++ b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java
@@ -111,10 +111,12 @@ public class QemuImg {
         }
 
         s.add(file.getFileName());
-
-        if (backingFile == null) {
+        if (file.getSize() != 0L) {
             s.add(Long.toString(file.getSize()));
+        } else if (backingFile == null) {
+            throw new QemuImgException("No size was passed, and no backing file was passed");
         }
+
         String result = s.execute();
         if (result != null) {
             throw new QemuImgException(result);
@@ -206,6 +208,10 @@ public class QemuImg {
         if (result != null) {
             throw new QemuImgException(result);
         }
+
+        if (srcFile.getSize() < destFile.getSize()) {
+            this.resize(destFile, destFile.getSize());
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/server/src/com/cloud/storage/VolumeApiServiceImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
index 30b5479..d820d02 100644
--- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
@@ -701,27 +701,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
 
         newDiskOffering = _diskOfferingDao.findById(cmd.getNewDiskOfferingId());
 
-        /*
-         * Volumes with no hypervisor have never been assigned, and can be
-         * resized by recreating. perhaps in the future we can just update the
-         * db entry for the volume
-         */
-        if (_volsDao.getHypervisorType(volume.getId()) == HypervisorType.None) {
-            throw new InvalidParameterValueException("Can't resize a volume that has never been attached, not sure which hypervisor type. Recreate volume to resize.");
-        }
-
-        /* Only works for KVM/Xen for now */
-        if (_volsDao.getHypervisorType(volume.getId()) != HypervisorType.KVM && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.XenServer
-                && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.VMware) {
-            throw new InvalidParameterValueException("Cloudstack currently only supports volumes marked as KVM or XenServer hypervisor for resize");
-        }
-
-        if (volume.getState() != Volume.State.Ready) {
-            throw new InvalidParameterValueException("Volume should be in ready state before attempting a resize");
+        /* Only works for KVM/Xen/VMware for now, and volumes with 'None' since they're just allocated in db */
+        if (_volsDao.getHypervisorType(volume.getId()) != HypervisorType.KVM
+            && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.XenServer
+            && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.VMware
+            && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.None) {
+            throw new InvalidParameterValueException("Cloudstack currently only supports volumes marked as KVM, VMware, XenServer hypervisor for resize");
         }
 
-        if (!volume.getVolumeType().equals(Volume.Type.DATADISK)) {
-            throw new InvalidParameterValueException("Can only resize DATA volumes");
+        if (volume.getState() != Volume.State.Ready && volume.getState() != Volume.State.Allocated) {
+            throw new InvalidParameterValueException("Volume should be in ready or allocated state before attempting a resize. "
+                                                     + "Volume " + volume.getUuid() + " state is:" + volume.getState());
         }
 
         /*
@@ -729,7 +719,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
          * required, get the correct size value
          */
         if (newDiskOffering == null) {
-            if (diskOffering.isCustomized()) {
+            if (diskOffering.isCustomized() || volume.getVolumeType().equals(Volume.Type.ROOT)) {
                 newSize = cmd.getSize();
 
                 if (newSize == null) {
@@ -741,6 +731,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
                 throw new InvalidParameterValueException("current offering" + volume.getDiskOfferingId() + " cannot be resized, need to specify a disk offering");
             }
         } else {
+            if (!volume.getVolumeType().equals(Volume.Type.DATADISK)) {
+                throw new InvalidParameterValueException("Can only resize Data volumes via new disk offering");
+            }
 
             if (newDiskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(newDiskOffering.getType())) {
                 throw new InvalidParameterValueException("Disk offering ID is missing or invalid");
@@ -784,8 +777,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
         /* does the caller have the authority to act on this volume? */
         _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
 
-        UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
-
         long currentSize = volume.getSize();
 
         /*
@@ -805,6 +796,20 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
                     - currentSize));
         }
 
+        /* If this volume has never been beyond allocated state, short circuit everything and simply update the database */
+        if (volume.getState() == Volume.State.Allocated) {
+            s_logger.debug("Volume is allocated, but never created, simply updating database with new size");
+            volume.setSize(newSize);
+            if (newDiskOffering != null) {
+                volume.setDiskOfferingId(cmd.getNewDiskOfferingId());
+            }
+            _volsDao.update(volume.getId(), volume);
+            return volume;
+        }
+
+        UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
+
+
         if (userVm != null) {
             // serialize VM operation
             AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 393613a..f9063cf 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -2835,11 +2835,31 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
                     vm.setIsoId(template.getId());
                 }
                 Long rootDiskSize = null;
+                // custom root disk size, resizes base template to larger size
                 if (customParameters.containsKey("rootdisksize")) {
                     if (NumbersUtil.parseLong(customParameters.get("rootdisksize"), -1) <= 0) {
                         throw new InvalidParameterValueException("rootdisk size should be a non zero number.");
                     }
-                    rootDiskSize = Long.parseLong(customParameters.get("rootDisksize"));
+                    rootDiskSize = Long.parseLong(customParameters.get("rootdisksize"));
+
+                    // only KVM supports rootdisksize override
+                    if (hypervisor != HypervisorType.KVM) {
+                        throw new InvalidParameterValueException("Hypervisor " + hypervisor + " does not support rootdisksize override");
+                    }
+
+                    // rotdisksize must be larger than template
+                    VMTemplateVO templateVO = _templateDao.findById(template.getId());
+                    if (templateVO == null) {
+                        throw new InvalidParameterValueException("Unable to look up template by id " + template.getId());
+                    }
+
+                    if ((rootDiskSize << 30) < templateVO.getSize()) {
+                        throw new InvalidParameterValueException("unsupported: rootdisksize override is smaller than template size " + templateVO.getSize());
+                    } else {
+                        s_logger.debug("rootdisksize of " + (rootDiskSize << 30) + " was larger than template size of " + templateVO.getSize());
+                    }
+
+                    s_logger.debug("found root disk size of " + rootDiskSize);
                     customParameters.remove("rootdisksize");
                 }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/test/integration/smoke/test_deploy_vm_root_resize.py
----------------------------------------------------------------------
diff --git a/test/integration/smoke/test_deploy_vm_root_resize.py b/test/integration/smoke/test_deploy_vm_root_resize.py
new file mode 100644
index 0000000..e17d2df
--- /dev/null
+++ b/test/integration/smoke/test_deploy_vm_root_resize.py
@@ -0,0 +1,263 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+#Test from the Marvin - Testing in Python wiki
+
+#All tests inherit from cloudstackTestCase
+from marvin.cloudstackTestCase import cloudstackTestCase
+
+#Import Integration Libraries
+
+#base - contains all resources as entities and defines create, delete, list operations on them
+from marvin.integration.lib.base import Account, VirtualMachine, ServiceOffering
+
+#utils - utility classes for common cleanup, external library wrappers etc
+from marvin.integration.lib.utils import cleanup_resources
+
+#common - commonly used methods for all tests are listed here
+from marvin.integration.lib.common import get_zone, get_domain, get_template, list_volumes
+
+from nose.plugins.attrib import attr
+
+class TestData(object):
+    """Test data object that is required to create resources
+    """
+    def __init__(self):
+        self.testdata = {
+            #data to create an account
+            "account": {
+                "email": "test@test.com",
+                "firstname": "Test",
+                "lastname": "User",
+                "username": "test",
+                "password": "password",
+            },
+            #data reqd for virtual machine creation
+            "virtual_machine" : {
+                "name" : "testvm",
+                "displayname" : "Test VM",
+            },
+            #small service offering
+            "service_offering": {
+                "small": {
+                    "name": "Small Instance",
+                    "displaytext": "Small Instance",
+                    "cpunumber": 1,
+                    "cpuspeed": 100,
+                    "memory": 256,
+                },
+            },
+            "ostype": 'CentOS 5.3 (64-bit)',
+        }
+
+class TestDeployVM(cloudstackTestCase):
+    """Test deploy a VM into a user account
+    """
+
+    def setUp(self):
+        self.testdata = TestData().testdata
+        self.apiclient = self.testClient.getApiClient()
+
+        # Get Zone, Domain and Default Built-in template
+        self.domain = get_domain(self.apiclient, self.testdata)
+        self.zone = get_zone(self.apiclient, self.testdata)
+        self.testdata["mode"] = self.zone.networktype
+        self.template = get_template(self.apiclient, self.zone.id, self.testdata["ostype"])
+#       for testing with specific template
+#        self.template = get_template(self.apiclient, self.zone.id, self.testdata["ostype"], templatetype='USER', services = {"template":'31f52a4d-5681-43f7-8651-ad4aaf823618'})
+        
+
+        #create a user account
+        self.account = Account.create(
+            self.apiclient,
+            self.testdata["account"],
+            domainid=self.domain.id
+        )
+        #create a service offering
+        self.service_offering = ServiceOffering.create(
+            self.apiclient,
+            self.testdata["service_offering"]["small"]
+        )
+        #build cleanup list
+        self.cleanup = [
+            self.service_offering,
+            self.account
+        ]
+
+    @attr(tags = ['advanced', 'simulator', 'basic', 'sg'])
+    def test_00_deploy_vm_root_resize(self):
+        """Test deploy virtual machine with root resize
+
+        # Validate the following:
+        # 1. listVirtualMachines returns accurate information
+        # 2. root disk has new size per listVolumes
+        # 3. Rejects non-supported hypervisor types
+        """
+        if(self.apiclient.hypervisor == 'kvm'):
+            newrootsize = (self.template.size >> 30) + 2 
+            self.virtual_machine = VirtualMachine.create(
+                self.apiclient,
+                self.testdata["virtual_machine"],
+                accountid=self.account.name,
+                zoneid=self.zone.id,
+                domainid=self.account.domainid,
+                serviceofferingid=self.service_offering.id,
+                templateid=self.template.id,
+                rootdisksize=newrootsize
+            )
+
+            list_vms = VirtualMachine.list(self.apiclient, id=self.virtual_machine.id)
+
+            self.debug(
+                "Verify listVirtualMachines response for virtual machine: %s"\
+                % self.virtual_machine.id
+            )
+
+            self.assertEqual(
+                isinstance(list_vms, list),
+                True,
+                "List VM response was not a valid list"
+            )
+            self.assertNotEqual(
+                len(list_vms),
+                0,
+                "List VM response was empty"
+            )
+
+            vm = list_vms[0]
+            self.assertEqual(
+                vm.id,
+                self.virtual_machine.id,
+                "Virtual Machine ids do not match"
+            )
+            self.assertEqual(
+                vm.name,
+                self.virtual_machine.name,
+                "Virtual Machine names do not match"
+            )
+            self.assertEqual(
+                vm.state,
+                "Running",
+                msg="VM is not in Running state"
+            )
+
+            # get root vol from created vm, verify it is correct size
+            list_volume_response = list_volumes(
+                                                self.apiclient,
+                                                virtualmachineid=self.virtual_machine.id,
+                                                type='ROOT',
+                                                listall=True
+                                                )
+
+            rootvolume = list_volume_response[0]
+            success = False
+            if rootvolume is not None and rootvolume.size  == (newrootsize << 30):
+                success = True
+
+            self.assertEqual(
+                             success,
+                             True,
+                             "Check if the root volume resized appropriately"
+                            )
+        else:
+            self.debug("hypervisor %s unsupported for test 00, verifying it errors properly" % self.apiclient.hypervisor)
+
+            newrootsize = (self.template.size >> 30) + 2
+            success = False
+            try:
+                self.virtual_machine = VirtualMachine.create(
+                    self.apiclient,
+                    self.testdata["virtual_machine"],
+                    accountid=self.account.name,
+                    zoneid=self.zone.id,
+                    domainid=self.account.domainid,
+                    serviceofferingid=self.service_offering.id,
+                    templateid=self.template.id,
+                    rootdisksize=newrootsize
+                )
+            except Exception as ex:
+                if "Hypervisor XenServer does not support rootdisksize override" in str(ex):
+                    success = True
+                else:
+                    self.debug("virtual machine create did not fail appropriately. Error was actually : " + str(ex));
+
+            self.assertEqual(success, True, "Check if unsupported hypervisor %s fails appropriately" % self.apiclient.hypervisor)
+
+    @attr(tags = ['advanced', 'simulator', 'basic', 'sg'])
+    def test_01_deploy_vm_root_resize(self):
+        """Test proper failure to deploy virtual machine with rootdisksize of 0 
+        """
+        if (self.apiclient.hypervisor == 'kvm'):
+            newrootsize = 0
+            success = False
+            try:
+                self.virtual_machine = VirtualMachine.create(
+                    self.apiclient,
+                    self.testdata["virtual_machine"],
+                    accountid=self.account.name,
+                    zoneid=self.zone.id,
+                    domainid=self.account.domainid,
+                    serviceofferingid=self.service_offering.id,
+                    templateid=self.template.id,
+                    rootdisksize=newrootsize
+                )
+            except Exception as ex:
+                if "rootdisk size should be a non zero number" in str(ex):
+                    success = True
+                else:
+                    self.debug("virtual machine create did not fail appropriately. Error was actually : " + str(ex));
+
+            self.assertEqual(success, True, "Check if passing 0 as rootdisksize fails appropriately")
+        else:
+            self.debug("test 01 does not support hypervisor type " + self.apiclient.hypervisor);
+
+    @attr(tags = ['advanced', 'simulator', 'basic', 'sg'])
+    def test_02_deploy_vm_root_resize(self):
+        """Test proper failure to deploy virtual machine with rootdisksize less than template size
+        """
+        if (self.apiclient.hypervisor == 'kvm'):
+            newrootsize = (self.template.size >> 30) - 1
+            
+            self.assertEqual(newrootsize > 0, True, "Provided template is less than 1G in size, cannot run test")
+
+            success = False
+            try:
+                self.virtual_machine = VirtualMachine.create(
+                    self.apiclient,
+                    self.testdata["virtual_machine"],
+                    accountid=self.account.name,
+                    zoneid=self.zone.id,
+                    domainid=self.account.domainid,
+                    serviceofferingid=self.service_offering.id,
+                    templateid=self.template.id,
+                    rootdisksize=newrootsize
+                )
+            except Exception as ex:
+                if "rootdisksize override is smaller than template size" in str(ex):
+                    success = True
+                else:
+                    self.debug("virtual machine create did not fail appropriately. Error was actually : " + str(ex));
+
+            self.assertEqual(success, True, "Check if passing rootdisksize < templatesize fails appropriately")
+        else:
+            self.debug("test 01 does not support hypervisor type " + self.apiclient.hypervisor);
+
+    def tearDown(self):
+        try:
+            cleanup_resources(self.apiclient, self.cleanup)
+        except Exception as e:
+            self.debug("Warning! Exception in tearDown: %s" % e)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/test/integration/smoke/test_volumes.py
----------------------------------------------------------------------
diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py
index 8f41803..719c824 100644
--- a/test/integration/smoke/test_volumes.py
+++ b/test/integration/smoke/test_volumes.py
@@ -571,6 +571,31 @@ class TestVolumes(cloudstackTestCase):
                 success,
                 True,
                 "ResizeVolume - verify disk offering is handled appropriately")
+
+        # try to resize a root disk with a disk offering, root can only be resized by size=
+        # get root vol from created vm
+        list_volume_response = list_volumes(
+                                            self.apiClient,
+                                            virtualmachineid=self.virtual_machine.id,
+                                            type='ROOT',
+                                            listall=True
+                                            )
+
+        rootvolume = list_volume_response[0]
+
+        cmd.id             = rootvolume.id
+        cmd.diskofferingid = self.services['diskofferingid']
+        success            = False
+        try:
+            response = self.apiClient.resizeVolume(cmd)
+        except Exception as ex:
+            if "Can only resize Data volumes" in str(ex):
+                success = True
+        self.assertEqual(
+                success,
+                True,
+                "ResizeVolume - verify root disks cannot be resized by disk offering id")
+            
         # Ok, now let's try and resize a volume that is not custom.
         cmd.id             = self.volume.id
         cmd.diskofferingid = self.services['diskofferingid']
@@ -647,6 +672,7 @@ class TestVolumes(cloudstackTestCase):
         elif hosts[0].hypervisor.lower() == "vmware":
             self.skipTest("Resize Volume is unsupported on VmWare")
 
+        # resize the data disk
         self.debug("Resize Volume ID: %s" % self.volume.id)
 
         cmd                = resizeVolume.resizeVolumeCmd()
@@ -675,7 +701,48 @@ class TestVolumes(cloudstackTestCase):
         self.assertEqual(
                          success,
                          True,
-                         "Check if the volume resized appropriately"
+                         "Check if the data volume resized appropriately"
+                         )
+
+        # resize the root disk
+        self.debug("Resize Root for : %s" % self.virtual_machine.id)
+
+        # get root vol from created vm
+        list_volume_response = list_volumes(
+                                            self.apiClient,
+                                            virtualmachineid=self.virtual_machine.id,
+                                            type='ROOT',
+                                            listall=True
+                                            )
+
+        rootvolume = list_volume_response[0]
+
+        cmd                = resizeVolume.resizeVolumeCmd()
+        cmd.id             = rootvolume.id
+        cmd.size           = 10
+
+        self.apiClient.resizeVolume(cmd)
+
+        count = 0
+        success = False
+        while count < 3:
+            list_volume_response = list_volumes(
+                                                self.apiClient,
+                                                id=rootvolume.id
+                                                )
+            for vol in list_volume_response:
+                if vol.id == rootvolume.id and vol.size == 10737418240L and vol.state == 'Ready':
+                    success = True
+            if success:
+                break
+            else:
+                time.sleep(10)
+                count += 1
+
+        self.assertEqual(
+                         success,
+                         True,
+                         "Check if the root volume resized appropriately"
                          )
 
         #start the vm if it is on xenserver

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/tools/devcloud-kvm/devcloud-kvm-advanced-fusion.cfg
----------------------------------------------------------------------
diff --git a/tools/devcloud-kvm/devcloud-kvm-advanced-fusion.cfg b/tools/devcloud-kvm/devcloud-kvm-advanced-fusion.cfg
index 53e1a44..2084417 100644
--- a/tools/devcloud-kvm/devcloud-kvm-advanced-fusion.cfg
+++ b/tools/devcloud-kvm/devcloud-kvm-advanced-fusion.cfg
@@ -120,20 +120,14 @@
         "port": 3306, 
         "user": "cloud"
     }, 
-    "logger": [
-        {
-            "name": "TestClient", 
-            "file": "/var/log/testclient.log"
-        }, 
-        {
-            "name": "TestCase", 
-            "file": "/var/log/testcase.log"
-        }
-    ], 
+    "logger": {
+        "LogFolderPath": "/tmp/"
+    },
     "mgtSvr": [
         {
             "mgtSvrIp": "172.17.10.10", 
-            "port": 8096
+            "port": 8096,
+            "hypervisor": "kvm"
         }
     ]
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/tools/devcloud-kvm/devcloud-kvm-advanced.cfg
----------------------------------------------------------------------
diff --git a/tools/devcloud-kvm/devcloud-kvm-advanced.cfg b/tools/devcloud-kvm/devcloud-kvm-advanced.cfg
index 53e1a44..74c36b7 100644
--- a/tools/devcloud-kvm/devcloud-kvm-advanced.cfg
+++ b/tools/devcloud-kvm/devcloud-kvm-advanced.cfg
@@ -133,7 +133,8 @@
     "mgtSvr": [
         {
             "mgtSvrIp": "172.17.10.10", 
-            "port": 8096
+            "port": 8096,
+            "hypervisor": "kvm"
         }
     ]
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/tools/devcloud-kvm/devcloud-kvm.cfg
----------------------------------------------------------------------
diff --git a/tools/devcloud-kvm/devcloud-kvm.cfg b/tools/devcloud-kvm/devcloud-kvm.cfg
index 3122e5a..ab7f9a5 100644
--- a/tools/devcloud-kvm/devcloud-kvm.cfg
+++ b/tools/devcloud-kvm/devcloud-kvm.cfg
@@ -104,7 +104,8 @@
     "mgtSvr": [
         {
             "mgtSvrIp": "127.0.0.1", 
-            "port": 8096
+            "port": 8096,
+            "hypervisor": "kvm"
         }
     ],
     "dbSvr": 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d638d04c/tools/marvin/marvin/integration/lib/base.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py
index b7e2be4..ca9f2b4 100755
--- a/tools/marvin/marvin/integration/lib/base.py
+++ b/tools/marvin/marvin/integration/lib/base.py
@@ -325,7 +325,7 @@ class VirtualMachine:
                     securitygroupids=None, projectid=None, startvm=None,
                     diskofferingid=None, affinitygroupnames=None, affinitygroupids=None, group=None,
                     hostid=None, keypair=None, ipaddress=None, mode='default', method='GET',
-                    customcpunumber=None, customcpuspeed=None, custommemory=None):
+                    customcpunumber=None, customcpuspeed=None, custommemory=None, rootdisksize=None):
         """Create the instance"""
 
         cmd = deployVirtualMachine.deployVirtualMachineCmd()
@@ -413,7 +413,7 @@ class VirtualMachine:
         if "userdata" in services:
             cmd.userdata = base64.urlsafe_b64encode(services["userdata"])
 
-        cmd.details = [{"cpuNumber": "","cpuSpeed":"","memory":""}]
+        cmd.details = [{"cpuNumber": "","cpuSpeed":"","memory":"","rootdisksize":""}]
 
         if customcpunumber:
             cmd.details[0]["cpuNumber"] = customcpunumber
@@ -424,6 +424,9 @@ class VirtualMachine:
         if custommemory:
             cmd.details[0]["memory"] = custommemory
 
+        if rootdisksize:
+            cmd.details[0]["rootdisksize"] = rootdisksize
+
         if group:
             cmd.group = group
 


[2/2] git commit: updated refs/heads/master to 345fc3c

Posted by ml...@apache.org.
Change newly added details map for deployVirtualMachine to an empty
map, instead of passing all possible keys as empty values


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/345fc3c8
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/345fc3c8
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/345fc3c8

Branch: refs/heads/master
Commit: 345fc3c8ece5720b9f4391d8ebe386d757f952fa
Parents: d638d04
Author: Marcus Sorensen <ma...@betterservers.com>
Authored: Fri Mar 14 07:13:34 2014 -0600
Committer: Marcus Sorensen <ma...@betterservers.com>
Committed: Fri Mar 14 07:18:06 2014 -0600

----------------------------------------------------------------------
 tools/marvin/marvin/integration/lib/base.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/345fc3c8/tools/marvin/marvin/integration/lib/base.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py
index ca9f2b4..38e8568 100755
--- a/tools/marvin/marvin/integration/lib/base.py
+++ b/tools/marvin/marvin/integration/lib/base.py
@@ -413,7 +413,7 @@ class VirtualMachine:
         if "userdata" in services:
             cmd.userdata = base64.urlsafe_b64encode(services["userdata"])
 
-        cmd.details = [{"cpuNumber": "","cpuSpeed":"","memory":"","rootdisksize":""}]
+        cmd.details = [{}]
 
         if customcpunumber:
             cmd.details[0]["cpuNumber"] = customcpunumber