You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by mt...@apache.org on 2013/11/07 05:06:23 UTC

git commit: updated refs/heads/master to 10c513a

Updated Branches:
  refs/heads/master 691666562 -> 10c513a25


CLOUDSTACK-4810: Enable hypervisor snapshots for CloudStack-managed storage (for XenServer and VMware)


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

Branch: refs/heads/master
Commit: 10c513a2592d2be4a06b0478672a3ea0b1ad0da3
Parents: 6916665
Author: Mike Tutkowski <mi...@solidfire.com>
Authored: Wed Nov 6 20:51:33 2013 -0700
Committer: Mike Tutkowski <mi...@solidfire.com>
Committed: Wed Nov 6 21:02:39 2013 -0700

----------------------------------------------------------------------
 api/src/com/cloud/offering/DiskOffering.java    |  3 +++
 api/src/com/cloud/storage/Volume.java           |  4 ++++
 .../org/apache/cloudstack/api/ApiConstants.java |  1 +
 .../admin/offering/CreateDiskOfferingCmd.java   |  7 ++++++
 .../classes/resources/messages.properties       |  1 +
 .../orchestration/VolumeOrchestrator.java       | 24 ++++++++++++++++++++
 .../src/com/cloud/storage/DiskOfferingVO.java   | 11 +++++++++
 .../schema/src/com/cloud/storage/VolumeVO.java  | 12 +++++++++-
 .../cloudstack/storage/volume/VolumeObject.java | 10 ++++++++
 .../driver/SolidfirePrimaryDataStoreDriver.java | 21 ++++++++++++-----
 .../configuration/ConfigurationManagerImpl.java | 13 ++++++++---
 setup/db/db/schema-421to430.sql                 |  5 +++-
 ui/dictionary.jsp                               |  1 +
 ui/scripts/configuration.js                     | 21 ++++++++++++++++-
 ui/scripts/docs.js                              |  4 ++++
 15 files changed, 126 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/api/src/com/cloud/offering/DiskOffering.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/offering/DiskOffering.java b/api/src/com/cloud/offering/DiskOffering.java
index a4c7dc3..fa6d66b 100644
--- a/api/src/com/cloud/offering/DiskOffering.java
+++ b/api/src/com/cloud/offering/DiskOffering.java
@@ -89,4 +89,7 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId
 
     Long getIopsWriteRate();
 
+    void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
+
+    Integer getHypervisorSnapshotReserve();
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/api/src/com/cloud/storage/Volume.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java
index 57e0494..dd59f12 100755
--- a/api/src/com/cloud/storage/Volume.java
+++ b/api/src/com/cloud/storage/Volume.java
@@ -185,4 +185,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
 	void setReservationId(String reserv);
 	Storage.ImageFormat getFormat();
 	Long getVmSnapshotChainSize();
+
+    void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
+
+    Integer getHypervisorSnapshotReserve();
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/api/src/org/apache/cloudstack/api/ApiConstants.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 3b2bc67..8ab6c5e 100755
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -54,6 +54,7 @@ public class ApiConstants {
     public static final String CUSTOMIZED_IOPS = "customizediops";
     public static final String MIN_IOPS = "miniops";
     public static final String MAX_IOPS = "maxiops";
+    public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve";
     public static final String DESCRIPTION = "description";
     public static final String DESTINATION_ZONE_ID = "destzoneid";
     public static final String DETAILS = "details";

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
index 4741591..77aeb54 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
@@ -86,6 +86,9 @@ public class CreateDiskOfferingCmd extends BaseCmd {
     @Parameter(name=ApiConstants.MAX_IOPS, type=CommandType.LONG, required=false, description="max iops of the disk offering")
     private Long maxIops;
 
+    @Parameter(name=ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE, type=CommandType.INTEGER, required=false, description="Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)")
+    private Integer hypervisorSnapshotReserve;
+
 /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -150,6 +153,10 @@ public class CreateDiskOfferingCmd extends BaseCmd {
         return displayOffering;
     }
 
+    public Integer getHypervisorSnapshotReserve() {
+        return hypervisorSnapshotReserve;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/client/WEB-INF/classes/resources/messages.properties
----------------------------------------------------------------------
diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties
index e450c29..5885bd0 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -39,6 +39,7 @@ label.custom.disk.iops=Custom IOPS
 label.disk.iops.min=Min IOPS
 label.disk.iops.max=Max IOPS
 label.disk.iops.total=IOPS Total
+label.hypervisor.snapshot.reserve=Hypervisor Snapshot Reserve
 label.view.secondary.ips=View secondary IPs
 message.validate.invalid.characters=Invalid characters found; please correct.
 message.acquire.ip.nic=Please confirm that you would like to acquire a new secondary IP for this NIC.<br/>NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/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 8d841d8..ab626c8 100644
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -406,6 +406,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
             AsyncCallFuture<VolumeApiResult> future = null;
             boolean isNotCreatedFromTemplate = volume.getTemplateId() == null ? true : false;
             if (isNotCreatedFromTemplate) {
+                volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
                 future = volService.createVolumeAsync(volume, store);
             } else {
                 TemplateInfo templ = tmplFactory.getTemplate(template.getId(), DataStoreRole.Image);
@@ -435,6 +436,29 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
         throw new CloudRuntimeException("create volume failed even after template re-deploy");
     }
 
+    // For managed storage on Xen and VMware, we need to potentially make space for hypervisor snapshots.
+    // The disk offering can collect this information and pass it on to the volume that's about to be created.
+    // Ex. if you want a 10 GB CloudStack volume to reside on managed storage on Xen, this leads to an SR
+    // that is a total size of (10 GB * (hypervisorSnapshotReserveSpace / 100) + 10 GB).
+    private VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType) {
+        Integer hypervisorSnapshotReserve = diskOffering.getHypervisorSnapshotReserve();
+
+        if (hyperType == HypervisorType.KVM) {
+            hypervisorSnapshotReserve = null;
+        }
+        else if (hypervisorSnapshotReserve == null || hypervisorSnapshotReserve < 0) {
+            hypervisorSnapshotReserve = 0;
+        }
+
+        VolumeVO volume = _volsDao.findById(volumeInfo.getId());
+
+        volume.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
+
+        _volsDao.update(volume.getId(), volume);
+
+        return volFactory.getVolume(volume.getId());
+    }
+
     public String getRandomVolumeName() {
         return UUID.randomUUID().toString();
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
index 04064b6..b5b3451 100755
--- a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
+++ b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
@@ -127,6 +127,9 @@ public class DiskOfferingVO implements DiskOffering {
     @Column(name = "state")
     State state;
 
+    @Column(name="hv_ss_reserve")
+    Integer hypervisorSnapshotReserve;
+
     public DiskOfferingVO() {
         uuid = UUID.randomUUID().toString();
     }
@@ -440,4 +443,12 @@ public class DiskOfferingVO implements DiskOffering {
     public Long getIopsWriteRate() {
         return iopsWriteRate;
     }
+
+    public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
+        this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
+    }
+
+    public Integer getHypervisorSnapshotReserve() {
+        return hypervisorSnapshotReserve;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/engine/schema/src/com/cloud/storage/VolumeVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java
index 86c8b3d..a130d89 100755
--- a/engine/schema/src/com/cloud/storage/VolumeVO.java
+++ b/engine/schema/src/com/cloud/storage/VolumeVO.java
@@ -35,7 +35,6 @@ import javax.persistence.Transient;
 import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.db.GenericDao;
-import com.cloud.vm.VirtualMachine.State;
 
 @Entity
 @Table(name = "volumes")
@@ -161,6 +160,9 @@ public class VolumeVO implements Volume {
     // @Column(name="reservation")
     String reservationId;
 
+    @Column(name="hv_ss_reserve")
+    Integer hypervisorSnapshotReserve;
+
     // Real Constructor
     public VolumeVO(Type type, String name, long dcId, long domainId,
             long accountId, long diskOfferingId, long size,
@@ -580,4 +582,12 @@ public class VolumeVO implements Volume {
     public void setState(State state) {
         this.state = state;
     }
+
+    public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
+        this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
+    }
+
+    public Integer getHypervisorSnapshotReserve() {
+        return hypervisorSnapshotReserve;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
----------------------------------------------------------------------
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
index f5a1276..f761a0c 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
@@ -141,6 +141,16 @@ public class VolumeObject implements VolumeInfo {
         return volumeVO.getMaxIops();
     }
 
+    @Override
+    public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
+        volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
+    }
+
+    @Override
+    public Integer getHypervisorSnapshotReserve() {
+        return volumeVO.getHypervisorSnapshotReserve();
+    }
+
     public long getVolumeId() {
         return volumeVO.getId();
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java
----------------------------------------------------------------------
diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java
index a02474d..1c726cd 100644
--- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java
+++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.storage.datastore.driver;
 
+import java.text.NumberFormat;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -278,8 +279,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         Long minIops = volumeInfo.getMinIops();
         Long maxIops = volumeInfo.getMaxIops();
 
-        if (minIops == null || minIops <= 0 ||
-                maxIops == null || maxIops <= 0) {
+        if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) {
             long defaultMaxIops = getDefaultMaxIops(storagePoolId);
 
             iops = new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops));
@@ -288,10 +288,20 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
             iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops()));
         }
 
-        long volumeSize = volumeInfo.getSize() * 2; // in reality, use a multiplier that's at cluster-level scope
+        long volumeSize = volumeInfo.getSize();
+        Integer hypervisorSnapshotReserve = volumeInfo.getHypervisorSnapshotReserve();
+
+        if (hypervisorSnapshotReserve != null) {
+            if (hypervisorSnapshotReserve < 25) {
+                hypervisorSnapshotReserve = 25;
+            }
+
+            volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f);
+        }
 
         long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
-                getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true, volumeInfo.getSize().toString(),
+                getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
+                NumberFormat.getNumberInstance().format(volumeInfo.getSize().toString()),
                 iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
 
         return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
@@ -410,8 +420,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
             SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
 
             if (!sfAccountExists(sfAccountName, sfConnection)) {
-                SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName,
-                        sfConnection);
+                SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName, sfConnection);
 
                 updateCsDbWithAccountInfo(account.getId(), sfAccount);
             }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
index 2e9b388..a31d06f 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -2188,7 +2188,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
     protected DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized,
     		boolean localStorageRequired, boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops,
-    		Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) {
+    		Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) {
         long diskSize = 0;// special case for custom disk offerings
         if (numGibibytes != null && (numGibibytes <= 0)) {
             throw new InvalidParameterValueException("Please specify a disk size of at least 1 Gb.");
@@ -2254,6 +2254,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         if (iopsWriteRate != null && (iopsWriteRate > 0))
             newDiskOffering.setIopsWriteRate(iopsWriteRate);
 
+        if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) {
+            throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0.");
+        }
+
+        newDiskOffering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
+
         CallContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId());
         DiskOfferingVO offering = _diskOfferingDao.persist(newDiskOffering);
         if (offering != null) {
@@ -2303,10 +2309,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         Long bytesWriteRate = cmd.getBytesWriteRate();
         Long iopsReadRate = cmd.getIopsReadRate();
         Long iopsWriteRate = cmd.getIopsWriteRate();
+        Integer hypervisorSnapshotReserve = cmd.getHypervisorSnapshotReserve();
 
         return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized,
-        		localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops,
-        		bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate);
+                localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops,
+                bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate, hypervisorSnapshotReserve);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/setup/db/db/schema-421to430.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-421to430.sql b/setup/db/db/schema-421to430.sql
index 26e4abf..980650d 100644
--- a/setup/db/db/schema-421to430.sql
+++ b/setup/db/db/schema-421to430.sql
@@ -16,7 +16,7 @@
 -- under the License.
 
 --;
--- Schema upgrade from 4.2.0 to 4.3.0;
+-- Schema upgrade from 4.2.1 to 4.3.0;
 --;
 
 -- Disable foreign key checking
@@ -108,6 +108,9 @@ UPDATE `cloud`.`configuration` SET `default_value` = `value`;
 
 #Upgrade the offerings and template table to have actual remove and states
 ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `state` CHAR(40) NOT NULL DEFAULT 'Active' COMMENT 'state for disk offering';
+ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `hv_ss_reserve` int(32) unsigned DEFAULT NULL COMMENT 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)';
+
+ALTER TABLE `cloud`.`volumes` ADD COLUMN `hv_ss_reserve` int(32) unsigned DEFAULT NULL COMMENT 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)';
 
 UPDATE `cloud`.`disk_offering` SET `state`='Inactive' WHERE `removed` IS NOT NULL;
 UPDATE `cloud`.`disk_offering` SET `removed`=NULL;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/ui/dictionary.jsp
----------------------------------------------------------------------
diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp
index 8bd547b..0ccfc23 100644
--- a/ui/dictionary.jsp
+++ b/ui/dictionary.jsp
@@ -50,6 +50,7 @@ dictionary = {
 'label.custom.disk.iops': '<fmt:message key="label.custom.disk.iops" />',
 'label.disk.iops.min': '<fmt:message key="label.disk.iops.min" />',
 'label.disk.iops.max': '<fmt:message key="label.disk.iops.max" />',
+'label.hypervisor.snapshot.reserve': '<fmt:message key="label.hypervisor.snapshot.reserve" />',
 'label.acquire.new.secondary.ip': '<fmt:message key="label.acquire.new.secondary.ip" />',
 'label.view.secondary.ips': '<fmt:message key="label.view.secondary.ips" />',
 'message.acquire.ip.nic': '<fmt:message key="message.acquire.ip.nic" />',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/ui/scripts/configuration.js
----------------------------------------------------------------------
diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js
index 4280597..9c05169 100644
--- a/ui/scripts/configuration.js
+++ b/ui/scripts/configuration.js
@@ -1241,6 +1241,7 @@
                                                 var $isCustomizedIops = $form.find('.form-item[rel=isCustomizedIops]');
                                                 var $minIops = $form.find('.form-item[rel=minIops]');
                                                 var $maxIops = $form.find('.form-item[rel=maxIops]');
+                                                var $hypervisorSnapshotReserve = $form.find('.form-item[rel=hypervisorSnapshotReserve]');
                                                 var $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]');
                                                 var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]');
                                                 var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]');
@@ -1256,17 +1257,20 @@
 
                                                     $isCustomizedIops.css('display', 'inline-block');
 
-                                                    if ($isCustomizedIops == true) {
+                                                    if ($isCustomizedIops.find('input[type=checkbox]').is(':checked')) {
                                                         $minIops.hide();
                                                         $maxIops.hide();
                                                     } else {
                                                         $minIops.css('display', 'inline-block');
                                                         $maxIops.css('display', 'inline-block');
                                                     }
+
+                                                    $hypervisorSnapshotReserve.css('display', 'inline-block');
                                                 } else if (qosId == 'hypervisor') { // Hypervisor Qos
                                                     $isCustomizedIops.hide();
                                                     $minIops.hide();
                                                     $maxIops.hide();
+                                                    $hypervisorSnapshotReserve.hide();
 
                                                     $diskBytesReadRate.css('display', 'inline-block');
                                                     $diskBytesWriteRate.css('display', 'inline-block');
@@ -1280,6 +1284,7 @@
                                                     $isCustomizedIops.hide();
                                                     $minIops.hide();
                                                     $maxIops.hide();
+                                                    $hypervisorSnapshotReserve.hide();
                                                 }
                                             });
                                         }
@@ -1309,6 +1314,14 @@
                                             number: true
                                         }
                                     },
+                                    hypervisorSnapshotReserve: {
+                                        label: 'label.hypervisor.snapshot.reserve',
+                                        docID: 'helpDiskOfferingHypervisorSnapshotReserve',
+                                        validation: {
+                                            required: false,
+                                            number: true
+                                        }
+                                    },
                                     diskBytesReadRate: {
                                         label: 'label.disk.bytes.read.rate',
                                         docID: 'helpDiskOfferingDiskBytesReadRate',
@@ -1416,6 +1429,12 @@
                                             });
                                         }
                                     }
+
+                                    if (args.data.hypervisorSnapshotReserve != null && args.data.hypervisorSnapshotReserve.length > 0) {
+                                        $.extend(data, {
+                                            hypervisorsnapshotreserve: args.data.hypervisorSnapshotReserve
+                                        });
+                                    }
                                 } else if (args.data.qosType == 'hypervisor') {
                                     if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
                                         $.extend(data, {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/10c513a2/ui/scripts/docs.js
----------------------------------------------------------------------
diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js
index 7b7edf4..a3151b1 100755
--- a/ui/scripts/docs.js
+++ b/ui/scripts/docs.js
@@ -326,6 +326,10 @@ cloudStack.docs = {
         desc: 'Appears only if Custom IOPS is not selected. Define the maximum volume IOPS.',
         externalLink: ''
     },
+    helpDiskOfferingHypervisorSnapshotReserve: {
+        desc: 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware) (Ex. The value 25 means 25%.)).',
+        externalLink: ''
+    },
     helpDiskOfferingStorageTags: {
         desc: 'Comma-separated list of attributes that should be associated with the primary storage for this disk. For example "ssd,blue".',
         externalLink: ''