You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ah...@apache.org on 2012/09/12 02:38:58 UTC

[18/50] [abbrv] git commit: CS-14277 Support for local data disk. Currently enable/disable config is at zone level, in subsequent checkins it can be made more granular. Following changes are made: - Create disk offering API now takes an extra par

CS-14277
Support for local data disk. Currently enable/disable config is at zone level, in subsequent checkins it can be made more granular.
    Following changes are made:
    - Create disk offering API now takes an extra parameter to denote storage type (local or shared). This is similar to storage type in service offering.
    - Create/delete of data volume on local storage
    - Attach/detach for local data volumes. Re-attach is allowed as long as vm host and data volume storage pool host is same.
    - Migration of VM instance is not supported if it uses local root or data volumes.
    - Migrate is not supported for local volumes.
    - Zone level config to enable/disable local storage usage for service and disk offerings.
    - Local storage gets discovered when a host is added/reconnected if zone level config is enabled. When disabled existing local storages are not removed but any new local storage is not added.
    - Deploy VM command validates service and disk offerings based on local storage config.
    - Upgrade uses the global config 'use.local.storage' to set the zone level config for local storage.
(cherry picked from commit 62710aed37606168012a0ed255a876c8e7954010)


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

Branch: refs/heads/javelin
Commit: 2404d3493dcb643433ca5812b8b33da775e4f131
Parents: be00326
Author: Koushik Das <ko...@citrix.com>
Authored: Tue Sep 11 17:08:09 2012 +0530
Committer: Alex Huang <al...@citrix.com>
Committed: Tue Sep 11 17:38:15 2012 -0700

----------------------------------------------------------------------
 api/src/com/cloud/api/ApiConstants.java            |    1 +
 .../cloud/api/commands/CreateDiskOfferingCmd.java  |   11 ++-
 api/src/com/cloud/api/commands/CreateZoneCmd.java  |   13 +++-
 api/src/com/cloud/api/commands/DeployVMCmd.java    |   14 +++-
 api/src/com/cloud/api/commands/UpdateZoneCmd.java  |   10 ++-
 .../cloud/api/response/DiskOfferingResponse.java   |   10 ++
 api/src/com/cloud/api/response/ZoneResponse.java   |    7 ++
 api/src/com/cloud/dc/DataCenter.java               |    1 +
 .../WEB-INF/classes/resources/messages.properties  |    1 +
 console-proxy/pom.xml                              |    4 +-
 server/src/com/cloud/api/ApiResponseHelper.java    |   21 +----
 server/src/com/cloud/configuration/Config.java     |    1 -
 .../cloud/configuration/ConfigurationManager.java  |    4 +-
 .../configuration/ConfigurationManagerImpl.java    |   26 +++++-
 server/src/com/cloud/dc/DataCenterVO.java          |   26 ++++--
 .../cloud/storage/LocalStoragePoolListener.java    |   10 ++-
 .../src/com/cloud/storage/StorageManagerImpl.java  |   64 +++++++++-----
 .../allocator/AbstractStoragePoolAllocator.java    |    4 +-
 .../allocator/FirstFitStoragePoolAllocator.java    |   10 ++-
 .../allocator/LocalStoragePoolAllocator.java       |   40 ++++++---
 .../storage/allocator/StoragePoolAllocator.java    |    2 +-
 .../allocator/UseLocalForRootAllocator.java        |   17 ++--
 server/src/com/cloud/test/DatabaseConfig.java      |    5 +-
 server/src/com/cloud/vm/UserVmManagerImpl.java     |   69 ++++++++++-----
 setup/db/create-schema.sql                         |    1 +
 setup/db/db/schema-302to40.sql                     |    4 +
 ui/scripts/configuration.js                        |   13 +++-
 ui/scripts/storage.js                              |   10 +--
 ui/scripts/system.js                               |   11 +++
 29 files changed, 286 insertions(+), 124 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/api/src/com/cloud/api/ApiConstants.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/ApiConstants.java b/api/src/com/cloud/api/ApiConstants.java
index 425b9fb..067ddf7 100755
--- a/api/src/com/cloud/api/ApiConstants.java
+++ b/api/src/com/cloud/api/ApiConstants.java
@@ -294,6 +294,7 @@ public class ApiConstants {
     public static final String DHCP_RANGE = "dhcprange";
     public static final String UUID = "uuid";
     public static final String SECURITY_GROUP_EANBLED = "securitygroupenabled";
+    public static final String LOCAL_STORAGE_ENABLED = "localstorageenabled";
     public static final String GUEST_IP_TYPE = "guestiptype";
     public static final String XEN_NETWORK_LABEL = "xennetworklabel";
     public static final String KVM_NETWORK_LABEL = "kvmnetworklabel";

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/api/src/com/cloud/api/commands/CreateDiskOfferingCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/CreateDiskOfferingCmd.java b/api/src/com/cloud/api/commands/CreateDiskOfferingCmd.java
index b3d9962..779221e 100755
--- a/api/src/com/cloud/api/commands/CreateDiskOfferingCmd.java
+++ b/api/src/com/cloud/api/commands/CreateDiskOfferingCmd.java
@@ -26,6 +26,7 @@ import com.cloud.api.Parameter;
 import com.cloud.api.ServerApiException;
 import com.cloud.api.response.DiskOfferingResponse;
 import com.cloud.offering.DiskOffering;
+import com.cloud.offering.ServiceOffering;
 import com.cloud.user.Account;
 
 @Implementation(description="Creates a disk offering.", responseObject=DiskOfferingResponse.class)
@@ -56,7 +57,10 @@ public class CreateDiskOfferingCmd extends BaseCmd {
     @IdentityMapper(entityTableName="domain")
     @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="the ID of the containing domain, null for public offerings")
     private Long domainId; 
-    
+
+    @Parameter(name=ApiConstants.STORAGE_TYPE, type=CommandType.STRING, description="the storage type of the disk offering. Values are local and shared.")
+    private String storageType = ServiceOffering.StorageType.shared.toString();
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -84,6 +88,11 @@ public class CreateDiskOfferingCmd extends BaseCmd {
     public Long getDomainId(){
     	return domainId;
     }
+
+    public String getStorageType() {
+        return storageType;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/api/src/com/cloud/api/commands/CreateZoneCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/CreateZoneCmd.java b/api/src/com/cloud/api/commands/CreateZoneCmd.java
index b36c721..1e9170d 100755
--- a/api/src/com/cloud/api/commands/CreateZoneCmd.java
+++ b/api/src/com/cloud/api/commands/CreateZoneCmd.java
@@ -73,6 +73,9 @@ public class CreateZoneCmd extends BaseCmd {
     @Parameter(name=ApiConstants.SECURITY_GROUP_EANBLED, type=CommandType.BOOLEAN, description="true if network is security group enabled, false otherwise")
     private Boolean securitygroupenabled;
 
+    @Parameter(name=ApiConstants.LOCAL_STORAGE_ENABLED, type=CommandType.BOOLEAN, description="true if local storage offering enabled, false otherwise")
+    private Boolean localStorageEnabled;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -123,8 +126,14 @@ public class CreateZoneCmd extends BaseCmd {
         }
         return securitygroupenabled;
     }
-    
-    
+
+    public Boolean getLocalStorageEnabled() {
+        if (localStorageEnabled == null) {
+            return false;
+        }
+        return localStorageEnabled;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/api/src/com/cloud/api/commands/DeployVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/DeployVMCmd.java b/api/src/com/cloud/api/commands/DeployVMCmd.java
index 9e2bc24..f67ee8f 100644
--- a/api/src/com/cloud/api/commands/DeployVMCmd.java
+++ b/api/src/com/cloud/api/commands/DeployVMCmd.java
@@ -374,14 +374,24 @@ public class DeployVMCmd extends BaseAsyncCreateCmd {
             if (template == null) {
                 throw new InvalidParameterValueException("Unable to use template " + templateId);
             }
-            
+
+            DiskOffering diskOffering = null;
             if (diskOfferingId != null) {
-                DiskOffering diskOffering = _configService.getDiskOffering(diskOfferingId);
+                diskOffering = _configService.getDiskOffering(diskOfferingId);
                 if (diskOffering == null) {
                     throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId);
                 }
             }
 
+            if (!zone.isLocalStorageEnabled()) {
+                if (serviceOffering.getUseLocalStorage()) {
+                    throw new InvalidParameterValueException("Zone is not configured to use local storage but service offering " + serviceOffering.getName() + " uses it");
+                }
+                if (diskOffering != null && diskOffering.getUseLocalStorage()) {
+                    throw new InvalidParameterValueException("Zone is not configured to use local storage but disk offering " + diskOffering.getName() + " uses it");
+                }
+            }
+
             UserVm vm = null;
             if (getHypervisor() == HypervisorType.BareMetal) {
                 vm = _bareMetalVmService.createVirtualMachine(this);

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/api/src/com/cloud/api/commands/UpdateZoneCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/UpdateZoneCmd.java b/api/src/com/cloud/api/commands/UpdateZoneCmd.java
index c22bff7..1fa0f79 100755
--- a/api/src/com/cloud/api/commands/UpdateZoneCmd.java
+++ b/api/src/com/cloud/api/commands/UpdateZoneCmd.java
@@ -81,7 +81,10 @@ public class UpdateZoneCmd extends BaseCmd {
     
     @Parameter(name=ApiConstants.DNS_SEARCH_ORDER, type=CommandType.LIST, collectionType = CommandType.STRING, description="the dns search order list")
     private List<String> dnsSearchOrder;   
-    
+
+    @Parameter(name=ApiConstants.LOCAL_STORAGE_ENABLED, type=CommandType.BOOLEAN, description="true if local storage offering enabled, false otherwise")
+    private Boolean localStorageEnabled;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -137,6 +140,11 @@ public class UpdateZoneCmd extends BaseCmd {
     public List<String> getDnsSearchOrder() {
         return dnsSearchOrder;
     }   
+
+    public Boolean getLocalStorageEnabled() {
+        return localStorageEnabled;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/api/src/com/cloud/api/response/DiskOfferingResponse.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/response/DiskOfferingResponse.java b/api/src/com/cloud/api/response/DiskOfferingResponse.java
index 9b4f891..9b37431 100644
--- a/api/src/com/cloud/api/response/DiskOfferingResponse.java
+++ b/api/src/com/cloud/api/response/DiskOfferingResponse.java
@@ -51,6 +51,9 @@ public class DiskOfferingResponse extends BaseResponse {
     @SerializedName(ApiConstants.TAGS) @Param(description="the tags for the disk offering")
     private String tags;
 
+    @SerializedName("storagetype") @Param(description="the storage type for this disk offering")
+    private String storageType;
+
     public Long getId() {
         return id.getValue();
     }
@@ -123,4 +126,11 @@ public class DiskOfferingResponse extends BaseResponse {
         this.customized = customized;
     }
 
+    public String getStorageType() {
+        return storageType;
+    }
+
+    public void setStorageType(String storageType) {
+        this.storageType = storageType;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/api/src/com/cloud/api/response/ZoneResponse.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/response/ZoneResponse.java b/api/src/com/cloud/api/response/ZoneResponse.java
index f591d70..98f808d 100755
--- a/api/src/com/cloud/api/response/ZoneResponse.java
+++ b/api/src/com/cloud/api/response/ZoneResponse.java
@@ -86,6 +86,9 @@ public class ZoneResponse extends BaseResponse {
     @SerializedName("capacity")  @Param(description="the capacity of the Zone", responseObject = CapacityResponse.class)
     private List<CapacityResponse> capacitites;
 
+    @SerializedName(ApiConstants.LOCAL_STORAGE_ENABLED) @Param(description="true if local storage offering enabled, false otherwise")
+    private boolean localStorageEnabled;
+
     public void setId(Long id) {
         this.id.setValue(id);
     }
@@ -165,4 +168,8 @@ public class ZoneResponse extends BaseResponse {
 	public void setDomainName(String domainName) {
 		this.domainName = domainName;
 	}
+
+    public void setLocalStorageEnabled(boolean localStorageEnabled) {
+        this.localStorageEnabled = localStorageEnabled;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/api/src/com/cloud/dc/DataCenter.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/dc/DataCenter.java b/api/src/com/cloud/dc/DataCenter.java
index 2d3064f..707cd34 100644
--- a/api/src/com/cloud/dc/DataCenter.java
+++ b/api/src/com/cloud/dc/DataCenter.java
@@ -75,4 +75,5 @@ public interface DataCenter extends Grouping {
 
     String getZoneToken();
 
+    boolean isLocalStorageEnabled();
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/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 1ec75ba..1535c31 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -1124,6 +1124,7 @@ label.linklocal.ip=Link Local IP Adddress
 label.load.balancer=Load Balancer
 label.loading=Loading
 label.local=Local
+label.local.storage.enabled=Local Storage Enabled
 label.login=Login
 label.logout=Logout
 label.lun=LUN

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/console-proxy/pom.xml
----------------------------------------------------------------------
diff --git a/console-proxy/pom.xml b/console-proxy/pom.xml
index 1bf4efe..8bfb753 100644
--- a/console-proxy/pom.xml
+++ b/console-proxy/pom.xml
@@ -138,7 +138,7 @@
           </execution>
         </executions>
       </plugin>
-      <plugin>
+      <!--plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>exec-maven-plugin</artifactId>
         <version>1.2.1</version>
@@ -163,7 +163,7 @@
             <argument>authorized_keys</argument>
           </arguments>
         </configuration>
-      </plugin>
+      </plugin-->
     </plugins>
   </build>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index aee6af0..b20aede 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -463,6 +463,7 @@ public class ApiResponseHelper implements ResponseGenerator {
         }
         diskOfferingResponse.setTags(offering.getTags());
         diskOfferingResponse.setCustomized(offering.isCustomized());
+        diskOfferingResponse.setStorageType(offering.getUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString());
         diskOfferingResponse.setObjectName("diskoffering");
         return diskOfferingResponse;
     }
@@ -954,6 +955,7 @@ public class ApiResponseHelper implements ResponseGenerator {
         zoneResponse.setId(dataCenter.getId());
         zoneResponse.setName(dataCenter.getName());
         zoneResponse.setSecurityGroupsEnabled(ApiDBUtils.isSecurityGroupEnabledInZone(dataCenter.getId()));
+        zoneResponse.setLocalStorageEnabled(dataCenter.isLocalStorageEnabled());
 
         if ((dataCenter.getDescription() != null) && !dataCenter.getDescription().equalsIgnoreCase("null")) {
             zoneResponse.setDescription(dataCenter.getDescription());
@@ -1121,24 +1123,6 @@ public class ApiResponseHelper implements ResponseGenerator {
         
         populateOwner(volResponse, volume);
 
-        String storageType;
-        try {
-            if (volume.getPoolId() == null) {
-                if (volume.getState() == Volume.State.Allocated || volume.getState() == Volume.State.UploadOp) {
-                    /* set it as shared, so the UI can attach it to VM */
-                    storageType = "shared";
-                } else {
-                    storageType = "unknown";
-                }
-            } else {
-                storageType = ApiDBUtils.volumeIsOnSharedStorage(volume.getId()) ? ServiceOffering.StorageType.shared.toString() : ServiceOffering.StorageType.local.toString();
-            }
-        } catch (InvalidParameterValueException e) {
-            s_logger.error(e.getMessage(), e);
-            throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Volume " + volume.getName() + " does not have a valid ID");
-        }
-
-        volResponse.setStorageType(storageType);
         if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
             volResponse.setServiceOfferingId(volume.getDiskOfferingId());
         } else {
@@ -1153,6 +1137,7 @@ public class ApiResponseHelper implements ResponseGenerator {
             volResponse.setDiskOfferingName(diskOffering.getName());
             volResponse.setDiskOfferingDisplayText(diskOffering.getDisplayText());
         }
+        volResponse.setStorageType(diskOffering.getUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString());
 
         Long poolId = volume.getPoolId();
         String poolName = (poolId == null) ? "none" : ApiDBUtils.findStoragePoolById(poolId).getName();

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index ebcd070..8554836 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -191,7 +191,6 @@ public enum Config {
 	ManagementHostIPAdr("Advanced", ManagementServer.class, String.class, "host", "localhost", "The ip address of management server", null),
 	ManagementNetwork("Advanced", ManagementServer.class, String.class, "management.network.cidr", null, "The cidr of management server network", null),
 	EventPurgeDelay("Advanced", ManagementServer.class, Integer.class, "event.purge.delay", "15", "Events older than specified number days will be purged. Set this value to 0 to never delete events", null),
-    UseLocalStorage("Advanced", ManagementServer.class, Boolean.class, "use.local.storage", "false", "Should we use the local storage if it's available?", null),
 	SecStorageVmMTUSize("Advanced", AgentManager.class, Integer.class, "secstorage.vm.mtu.size", String.valueOf(SecondaryStorageVmManager.DEFAULT_SS_VM_MTUSIZE), "MTU size (in Byte) of storage network in secondary storage vms", null),
 	MaxTemplateAndIsoSize("Advanced",  ManagementServer.class, Long.class, "max.template.iso.size", "50", "The maximum size for a downloaded template or ISO (in GB).", null),
 	SecStorageAllowedInternalDownloadSites("Advanced", ManagementServer.class, String.class, "secstorage.allowed.internal.sites", null, "Comma separated list of cidrs internal to the datacenter that can host template download servers, please note 0.0.0.0 is not a valid site", null),

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/configuration/ConfigurationManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java
index df28251..19e74bf 100644
--- a/server/src/com/cloud/configuration/ConfigurationManager.java
+++ b/server/src/com/cloud/configuration/ConfigurationManager.java
@@ -93,7 +93,7 @@ public interface ConfigurationManager extends ConfigurationService, Manager {
      * @param isCustomized
      * @return newly created disk offering
      */
-    DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized);
+    DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired);
 
     /**
      * Creates a new pod
@@ -133,7 +133,7 @@ public interface ConfigurationManager extends ConfigurationService, Manager {
      * @throws
      */
     DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, String guestCidr, String domain, Long domainId, NetworkType zoneType, String allocationState,
-            String networkDomain, boolean isSecurityGroupEnabled);
+            String networkDomain, boolean isSecurityGroupEnabled, boolean isLocalStorageEnabled);
 
     /**
      * Deletes a VLAN from the database, along with all of its IP addresses. Will not delete VLANs that have allocated

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/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 f9da08d..f8deb15 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -1364,6 +1364,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
         String dhcpProvider = cmd.getDhcpProvider();
         Map<?, ?> detailsMap = cmd.getDetails();
         String networkDomain = cmd.getDomain();
+        Boolean localStorageEnabled = cmd.getLocalStorageEnabled();
 
         Map<String, String> newDetails = new HashMap<String, String>();
         if (detailsMap != null) {
@@ -1470,6 +1471,9 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
         zone.setInternalDns1(internalDns1);
         zone.setInternalDns2(internalDns2);
         zone.setGuestNetworkCidr(guestCidr);
+        if (localStorageEnabled != null) {
+            zone.setLocalStorageEnabled(localStorageEnabled.booleanValue());
+        }
 
         if (networkDomain != null) {
             if (networkDomain.isEmpty()) {
@@ -1543,7 +1547,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
     @Override
     @DB
     public DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, String guestCidr, String domain, Long domainId,
-            NetworkType zoneType, String allocationStateStr, String networkDomain, boolean isSecurityGroupEnabled) {
+            NetworkType zoneType, String allocationStateStr, String networkDomain, boolean isSecurityGroupEnabled, boolean isLocalStorageEnabled) {
 
         // checking the following params outside checkzoneparams method as we do
         // not use these params for updatezone
@@ -1569,7 +1573,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
         try {
             txn.start();
             // Create the new zone in the database
-            DataCenterVO zone = new DataCenterVO(zoneName, null, dns1, dns2, internalDns1, internalDns2, guestCidr, domain, domainId, zoneType, zoneToken, networkDomain, isSecurityGroupEnabled);
+            DataCenterVO zone = new DataCenterVO(zoneName, null, dns1, dns2, internalDns1, internalDns2, guestCidr, domain, domainId, zoneType, zoneToken, networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled);
             if (allocationStateStr != null && !allocationStateStr.isEmpty()) {
                 Grouping.AllocationState allocationState = Grouping.AllocationState.valueOf(allocationStateStr);
                 zone.setAllocationState(allocationState);
@@ -1649,6 +1653,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
         String allocationState = cmd.getAllocationState();
         String networkDomain = cmd.getDomain();
         boolean isSecurityGroupEnabled = cmd.getSecuritygroupenabled();
+        boolean isLocalStorageEnabled = cmd.getLocalStorageEnabled();
 
         if (allocationState == null) {
             allocationState = Grouping.AllocationState.Disabled.toString();
@@ -1682,7 +1687,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
         }
 
         return createZone(userId, zoneName, dns1, dns2, internalDns1, internalDns2, guestCidr, domainVO != null ? domainVO.getName() : null, domainId, zoneType, allocationState, networkDomain,
-                isSecurityGroupEnabled);
+                isSecurityGroupEnabled, isLocalStorageEnabled);
     }
 
     @Override
@@ -1857,7 +1862,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
 
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_DISK_OFFERING_CREATE, eventDescription = "creating disk offering")
-    public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized) {
+    public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired) {
         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.");
@@ -1875,6 +1880,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
 
         tags = cleanupTags(tags);
         DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized);
+        newDiskOffering.setUseLocalStorage(localStorageRequired);
         UserContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId());
         DiskOfferingVO offering = _diskOfferingDao.persist(newDiskOffering);
         if (offering != null) {
@@ -1904,7 +1910,17 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
             throw new InvalidParameterValueException("Disksize is required for non-customized disk offering");
         }
 
-        return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized);
+        boolean localStorageRequired = false;
+        String storageType = cmd.getStorageType();
+        if (storageType != null) {
+            if (storageType.equalsIgnoreCase(ServiceOffering.StorageType.local.toString())) {
+                localStorageRequired = true;
+            } else if (!storageType.equalsIgnoreCase(ServiceOffering.StorageType.shared.toString())) {
+                throw new InvalidParameterValueException("Invalid storage type " + storageType + " specified, valid types are: 'local' and 'shared'");
+            }
+        }
+
+        return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized, localStorageRequired);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/dc/DataCenterVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/dc/DataCenterVO.java b/server/src/com/cloud/dc/DataCenterVO.java
index a2b7d5f..7b5280c 100644
--- a/server/src/com/cloud/dc/DataCenterVO.java
+++ b/server/src/com/cloud/dc/DataCenterVO.java
@@ -126,7 +126,10 @@ public class DataCenterVO implements DataCenter, Identity {
     
     @Column(name="is_security_group_enabled")
     boolean securityGroupEnabled;
-    
+
+    @Column(name="is_local_storage_enabled")
+    boolean localStorageEnabled;
+
     @Override
     public String getDnsProvider() {
         return dnsProvider;
@@ -172,14 +175,14 @@ public class DataCenterVO implements DataCenter, Identity {
         this.firewallProvider = firewallProvider;
     }
     
-    public DataCenterVO(long id, String name, String description, String dns1, String dns2, String dns3, String dns4,String guestCidr, String domain, Long domainId, NetworkType zoneType, String zoneToken, String domainSuffix) {
-        this(name, description, dns1, dns2, dns3, dns4, guestCidr, domain, domainId, zoneType, zoneToken, domainSuffix, false);
+    public DataCenterVO(long id, String name, String description, String dns1, String dns2, String dns3, String dns4, String guestCidr, String domain, Long domainId, NetworkType zoneType, String zoneToken, String domainSuffix) {
+        this(name, description, dns1, dns2, dns3, dns4, guestCidr, domain, domainId, zoneType, zoneToken, domainSuffix, false, false);
         this.id = id;
         this.allocationState = Grouping.AllocationState.Enabled;
         this.uuid = UUID.randomUUID().toString();
 	}
 
-    public DataCenterVO(String name, String description, String dns1, String dns2, String dns3, String dns4, String guestCidr, String domain, Long domainId, NetworkType zoneType, String zoneToken, String domainSuffix, boolean securityGroupEnabled) {
+    public DataCenterVO(String name, String description, String dns1, String dns2, String dns3, String dns4, String guestCidr, String domain, Long domainId, NetworkType zoneType, String zoneToken, String domainSuffix, boolean securityGroupEnabled, boolean localStorageEnabled) {
         this.name = name;
         this.description = description;
         this.dns1 = dns1;
@@ -192,8 +195,8 @@ public class DataCenterVO implements DataCenter, Identity {
         this.networkType = zoneType;
         this.allocationState = Grouping.AllocationState.Enabled;
         this.securityGroupEnabled = securityGroupEnabled;
-        
-        
+        this.localStorageEnabled = localStorageEnabled;
+
         if (zoneType == NetworkType.Advanced) {
             loadBalancerProvider = Provider.VirtualRouter.getName();
             firewallProvider = Provider.VirtualRouter.getName();
@@ -344,7 +347,16 @@ public class DataCenterVO implements DataCenter, Identity {
     public void setSecurityGroupEnabled(boolean enabled) {
         this.securityGroupEnabled = enabled;
     }
-    
+
+    @Override
+    public boolean isLocalStorageEnabled() {
+        return localStorageEnabled;
+    }
+
+    public void setLocalStorageEnabled(boolean enabled) {
+        this.localStorageEnabled = enabled;
+    }
+
     @Override
     public Map<String, String> getDetails() {
         return details;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/storage/LocalStoragePoolListener.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/LocalStoragePoolListener.java b/server/src/com/cloud/storage/LocalStoragePoolListener.java
index 1be7a55..3cf416a 100755
--- a/server/src/com/cloud/storage/LocalStoragePoolListener.java
+++ b/server/src/com/cloud/storage/LocalStoragePoolListener.java
@@ -31,6 +31,8 @@ import com.cloud.agent.api.StoragePoolInfo;
 import com.cloud.capacity.Capacity;
 import com.cloud.capacity.CapacityVO;
 import com.cloud.capacity.dao.CapacityDao;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.exception.ConnectionException;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
@@ -48,6 +50,7 @@ public class LocalStoragePoolListener implements Listener {
     @Inject StoragePoolHostDao _storagePoolHostDao;
     @Inject CapacityDao _capacityDao;
     @Inject StorageManager _storageMgr;
+    @Inject DataCenterDao _dcDao;
 
     @Override
     public int getTimeout() {
@@ -86,7 +89,12 @@ public class LocalStoragePoolListener implements Listener {
         if (pInfo == null) {
             return;
         }
-        
+
+        DataCenterVO dc = _dcDao.findById(host.getDataCenterId());
+        if (dc == null || !dc.isLocalStorageEnabled()) {
+            return;
+        }
+
         try {
             StoragePoolVO pool = _storagePoolDao.findPoolByHostPath(host.getDataCenterId(), host.getPodId(), pInfo.getHost(), pInfo.getHostPath(), pInfo.getUuid());
         	if(pool == null && host.getHypervisorType() == HypervisorType.VMware) {

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/storage/StorageManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java
index 4425b71..83b2846 100755
--- a/server/src/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/com/cloud/storage/StorageManagerImpl.java
@@ -126,6 +126,7 @@ import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.HypervisorGuruManager;
 import com.cloud.network.NetworkManager;
+import com.cloud.offering.ServiceOffering;
 import com.cloud.org.Grouping;
 import com.cloud.org.Grouping.AllocationState;
 import com.cloud.projects.Project.ListProjectResourcesCriteria;
@@ -457,13 +458,13 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         return false;
     }
 
-    protected StoragePoolVO findStoragePool(DiskProfile dskCh, final DataCenterVO dc, HostPodVO pod, Long clusterId, VMInstanceVO vm, final Set<StoragePool> avoid) {
+    protected StoragePoolVO findStoragePool(DiskProfile dskCh, final DataCenterVO dc, HostPodVO pod, Long clusterId, Long hostId, VMInstanceVO vm, final Set<StoragePool> avoid) {
 
         VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
         Enumeration<StoragePoolAllocator> en = _storagePoolAllocators.enumeration();
         while (en.hasMoreElements()) {
             final StoragePoolAllocator allocator = en.nextElement();
-            final List<StoragePool> poolList = allocator.allocateToPool(dskCh, profile, dc.getId(), pod.getId(), clusterId, avoid, 1);
+            final List<StoragePool> poolList = allocator.allocateToPool(dskCh, profile, dc.getId(), pod.getId(), clusterId, hostId, avoid, 1);
             if (poolList != null && !poolList.isEmpty()) {
                 return (StoragePoolVO) poolList.get(0);
             }
@@ -571,7 +572,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         while ((pod = _resourceMgr.findPod(null, null, dc, account.getId(), podsToAvoid)) != null) {
             podsToAvoid.add(pod.first().getId());
             // Determine what storage pool to store the volume in
-            while ((pool = findStoragePool(dskCh, dc, pod.first(), null, null, poolsToAvoid)) != null) {
+            while ((pool = findStoragePool(dskCh, dc, pod.first(), null, null, null, poolsToAvoid)) != null) {
                 poolsToAvoid.add(pool);
                 volumeFolder = pool.getPath();
                 if (s_logger.isDebugEnabled()) {
@@ -741,7 +742,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
     	DiskProfile dskCh = createDiskCharacteristics(volume, template, dc, diskOffering);
     	dskCh.setHyperType(vm.getHypervisorType());
     	// Find a suitable storage to create volume on 
-    	StoragePoolVO destPool = findStoragePool(dskCh, dc, pod, clusterId, vm, avoidPools);
+	StoragePoolVO destPool = findStoragePool(dskCh, dc, pod, clusterId, null, vm, avoidPools);
     	
     	// Copy the volume from secondary storage to the destination storage pool    	  	
     	stateTransitTo(volume, Event.CopyRequested);
@@ -818,7 +819,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                 break;
             }
 
-            pool = findStoragePool(dskCh, dc, pod, clusterId, vm, avoidPools);
+            pool = findStoragePool(dskCh, dc, pod, clusterId, vm.getHostId(), vm, avoidPools);
             if (pool == null) {
                 s_logger.warn("Unable to find storage poll when create volume " + volume.getName());
                 break;
@@ -988,10 +989,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         int wrks = NumbersUtil.parseInt(workers, 10);
         _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("StorageManager-Scavenger"));
 
-        boolean localStorage = Boolean.parseBoolean(configs.get(Config.UseLocalStorage.key()));
-        if (localStorage) {
-            _agentMgr.registerForHostEvents(ComponentLocator.inject(LocalStoragePoolListener.class), true, false, false);
-        }
+        _agentMgr.registerForHostEvents(ComponentLocator.inject(LocalStoragePoolListener.class), true, false, false);
 
         String maxVolumeSizeInGbString = configDao.getValue("storage.max.volume.size");
         _maxVolumeSizeInGb = NumbersUtil.parseLong(maxVolumeSizeInGbString, 2000);
@@ -1713,7 +1711,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         dskCh.setHyperType(dataDiskHyperType);
         DataCenterVO destPoolDataCenter = _dcDao.findById(destPoolDcId);
         HostPodVO destPoolPod = _podDao.findById(destPoolPodId);
-        StoragePoolVO destPool = findStoragePool(dskCh, destPoolDataCenter, destPoolPod, destPoolClusterId, null, new HashSet<StoragePool>());
+        StoragePoolVO destPool = findStoragePool(dskCh, destPoolDataCenter, destPoolPod, destPoolClusterId, null, null, new HashSet<StoragePool>());
         String secondaryStorageURL = getSecondaryStorageURL(volume.getDataCenterId());
 
         if (destPool == null) {
@@ -1894,6 +1892,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
 
         Long zoneId = cmd.getZoneId();
         Long diskOfferingId = null;
+        DiskOfferingVO diskOffering = null;
         Long size = null;
 
         // validate input parameters before creating the volume
@@ -1913,11 +1912,9 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                     throw new InvalidParameterValueException("Disk size must be larger than 0");
                 }
             }
-            if (diskOfferingId == null) {
-                throw new InvalidParameterValueException("Missing parameter(s),either a positive volume size or a valid disk offering id must be specified.");
-            }
+
             // Check that the the disk offering is specified
-            DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
+            diskOffering = _diskOfferingDao.findById(diskOfferingId);
             if ((diskOffering == null) || diskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(diskOffering.getType())) {
                 throw new InvalidParameterValueException("Please specify a valid disk offering.");
             }
@@ -1959,7 +1956,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
                 throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.Status.BackedUp + " state yet and can't be used for volume creation");
             }
 
-            diskOfferingId = (cmd.getDiskOfferingId() != null) ? cmd.getDiskOfferingId() : snapshotCheck.getDiskOfferingId();
+            diskOfferingId = snapshotCheck.getDiskOfferingId();
+            diskOffering = _diskOfferingDao.findById(diskOfferingId);
             zoneId = snapshotCheck.getDataCenterId();
             size = snapshotCheck.getSize(); // ; disk offering is used for tags purposes
 
@@ -1988,12 +1986,27 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
             throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId);
         }
 
-        // Check that there is a shared primary storage pool in the specified zone
+        // If local storage is disabled then creation of volume with local disk offering not allowed
+        if (!zone.isLocalStorageEnabled() && diskOffering.getUseLocalStorage()) {
+            throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it");
+        }
+
+        // Check that there is appropriate primary storage pool in the specified zone
         List<StoragePoolVO> storagePools = _storagePoolDao.listByDataCenterId(zoneId);
-        boolean sharedPoolExists = false;
-        for (StoragePoolVO storagePool : storagePools) {
-            if (storagePool.isShared()) {
-                sharedPoolExists = true;
+        boolean appropriatePoolExists = false;
+        if (!diskOffering.getUseLocalStorage()) {
+            for (StoragePoolVO storagePool : storagePools) {
+                if (storagePool.isShared()) {
+                    appropriatePoolExists = true;
+                    break;
+                }
+            }
+        } else {
+            for (StoragePoolVO storagePool : storagePools) {
+                if (storagePool.isLocal()) {
+                    appropriatePoolExists = true;
+                    break;
+                }
             }
         }
 
@@ -2003,8 +2016,9 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
             throw new InvalidParameterValueException("There is no workable host in data center id " + zoneId + ", please check hosts' agent status and see if they are disabled");
         }
 
-        if (!sharedPoolExists) {
-            throw new InvalidParameterValueException("Please specify a zone that has at least one shared primary storage pool.");
+        if (!appropriatePoolExists) {
+            String storageType = diskOffering.getUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString();
+            throw new InvalidParameterValueException("Volume's disk offering uses " + storageType + " storage, please specify a zone that has at least one " + storageType + " primary storage pool.");
         }
 
         String userSpecifiedName = cmd.getVolumeName();
@@ -3047,7 +3061,11 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
 
         StoragePool destPool = _storagePoolDao.findById(storagePoolId);
         if (destPool == null) {
-            throw new InvalidParameterValueException("Faild to find the destination storage pool: " + storagePoolId);
+            throw new InvalidParameterValueException("Failed to find the destination storage pool: " + storagePoolId);
+        }
+
+        if (!volumeOnSharedStoragePool(vol)) {
+            throw new InvalidParameterValueException("Migration of volume from local storage pool is not supported");
         }
 
         List<Volume> vols = new ArrayList<Volume>();

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java
index 87cb065..f2d749e 100755
--- a/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java
+++ b/server/src/com/cloud/storage/allocator/AbstractStoragePoolAllocator.java
@@ -193,14 +193,14 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
 	
 	
 	@Override
-	public List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile<? extends VirtualMachine> vmProfile, long dcId, long podId, Long clusterId, Set<? extends StoragePool> avoids, int returnUpTo) {
+	public List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile<? extends VirtualMachine> vmProfile, long dcId, long podId, Long clusterId, Long hostId, Set<? extends StoragePool> avoids, int returnUpTo) {
 	    
 	    ExcludeList avoid = new ExcludeList();
 	    for(StoragePool pool : avoids){
 	    	avoid.addPool(pool.getId());
 	    }
 	    
-	    DataCenterDeployment plan = new DataCenterDeployment(dcId, podId, clusterId, null, null, null);
+	    DataCenterDeployment plan = new DataCenterDeployment(dcId, podId, clusterId, hostId, null, null);
 	    return allocateToPool(dskCh, vmProfile, plan, avoid, returnUpTo);
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java
index 006931d..7d33c15 100644
--- a/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java
+++ b/server/src/com/cloud/storage/allocator/FirstFitStoragePoolAllocator.java
@@ -30,6 +30,7 @@ import org.apache.log4j.Logger;
 
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.deploy.DeploymentPlanner.ExcludeList;
+import com.cloud.offering.ServiceOffering;
 import com.cloud.server.StatsCollector;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.dao.DiskOfferingDao;
@@ -82,11 +83,12 @@ public class FirstFitStoragePoolAllocator extends AbstractStoragePoolAllocator {
         	s_logger.debug("Looking for pools in dc: " + dcId + "  pod:" + podId + "  cluster:" + clusterId);
         }
 
-		List<StoragePoolVO> pools = _storagePoolDao.findPoolsByTags(dcId, podId, clusterId, dskCh.getTags(), null);
+        List<StoragePoolVO> pools = _storagePoolDao.findPoolsByTags(dcId, podId, clusterId, dskCh.getTags(), null);
         if (pools.size() == 0) {
-    		if (s_logger.isDebugEnabled()) {
-    			s_logger.debug("No storage pools available for allocation, returning");
-    		}
+            if (s_logger.isDebugEnabled()) {
+                String storageType = dskCh.useLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString();
+                s_logger.debug("No storage pools available for " + storageType + " volume allocation, returning");
+            }
             return suitablePools;
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/storage/allocator/LocalStoragePoolAllocator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/allocator/LocalStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/LocalStoragePoolAllocator.java
index 991baad..445f278 100644
--- a/server/src/com/cloud/storage/allocator/LocalStoragePoolAllocator.java
+++ b/server/src/com/cloud/storage/allocator/LocalStoragePoolAllocator.java
@@ -35,6 +35,8 @@ import com.cloud.offering.ServiceOffering;
 import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.StoragePoolHostVO;
+import com.cloud.storage.StoragePoolVO;
+import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.StoragePoolHostDao;
 import com.cloud.utils.DateUtil;
@@ -101,18 +103,34 @@ public class LocalStoragePoolAllocator extends FirstFitStoragePoolAllocator {
             s_logger.debug("LocalStoragePoolAllocator trying to find storage pool to fit the vm");
         }
 
-        List<StoragePool> availablePool;
-        while (!(availablePool = super.allocateToPool(dskCh, vmProfile, plan, myAvoids, 1)).isEmpty()) {
-            StoragePool pool = availablePool.get(0);
-            myAvoids.addPool(pool.getId());
-            List<StoragePoolHostVO> hostsInSPool = _poolHostDao.listByPoolId(pool.getId());
-            assert (hostsInSPool.size() == 1) : "Local storage pool should be one host per pool";
-
-            s_logger.debug("Found suitable local storage pool " + pool.getId() + ", adding to list");
-            suitablePools.add(pool);
+        // data disk and host identified from deploying vm (attach volume case)
+        if (dskCh.getType() == Volume.Type.DATADISK && plan.getHostId() != null) {
+            List<StoragePoolHostVO> hostPools = _poolHostDao.listByHostId(plan.getHostId());
+            for (StoragePoolHostVO hostPool: hostPools) {
+                StoragePoolVO pool = _storagePoolDao.findById(hostPool.getPoolId());
+                if (pool != null && pool.isLocal()) {
+                    s_logger.debug("Found suitable local storage pool " + pool.getId() + ", adding to list");
+                    suitablePools.add(pool);
+                }
 
-            if (suitablePools.size() == returnUpTo) {
-                break;
+                if (suitablePools.size() == returnUpTo) {
+                    break;
+                }
+            }
+        } else {
+            List<StoragePool> availablePool;
+            while (!(availablePool = super.allocateToPool(dskCh, vmProfile, plan, myAvoids, 1)).isEmpty()) {
+                StoragePool pool = availablePool.get(0);
+                myAvoids.addPool(pool.getId());
+                List<StoragePoolHostVO> hostsInSPool = _poolHostDao.listByPoolId(pool.getId());
+                assert (hostsInSPool.size() == 1) : "Local storage pool should be one host per pool";
+
+                s_logger.debug("Found suitable local storage pool " + pool.getId() + ", adding to list");
+                suitablePools.add(pool);
+
+                if (suitablePools.size() == returnUpTo) {
+                    break;
+                }
             }
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/storage/allocator/StoragePoolAllocator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/allocator/StoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/StoragePoolAllocator.java
index 13f44e7..1c02c6c 100644
--- a/server/src/com/cloud/storage/allocator/StoragePoolAllocator.java
+++ b/server/src/com/cloud/storage/allocator/StoragePoolAllocator.java
@@ -33,7 +33,7 @@ import com.cloud.vm.VirtualMachineProfile;
 public interface StoragePoolAllocator extends Adapter {
 	
 	//keeping since storageMgr is using this API for some existing functionalities
-	List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile<? extends VirtualMachine> vmProfile, long dcId, long podId, Long clusterId, Set<? extends StoragePool> avoids, int returnUpTo);	
+	List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile<? extends VirtualMachine> vmProfile, long dcId, long podId, Long clusterId, Long hostId, Set<? extends StoragePool> avoids, int returnUpTo);
 	
 	String chooseStorageIp(VirtualMachine vm, Host host, Host storage);
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java b/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java
index 38e116a..f1da114 100644
--- a/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java
+++ b/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java
@@ -24,23 +24,29 @@ import javax.naming.ConfigurationException;
 
 import com.cloud.configuration.Config;
 import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.deploy.DeploymentPlanner.ExcludeList;
 import com.cloud.host.Host;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.Volume.Type;
 import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.component.Inject;
 import com.cloud.vm.DiskProfile;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
 
 @Local(value=StoragePoolAllocator.class)
 public class UseLocalForRootAllocator extends LocalStoragePoolAllocator implements StoragePoolAllocator {
-    boolean _useLocalStorage;
+
+    @Inject
+    DataCenterDao _dcDao;
 
     @Override
     public List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) {
-        if (!_useLocalStorage) {
+        DataCenterVO dc = _dcDao.findById(plan.getDataCenterId());
+        if (!dc.isLocalStorageEnabled()) {
             return null;
         }
         
@@ -55,13 +61,6 @@ public class UseLocalForRootAllocator extends LocalStoragePoolAllocator implemen
     @Override
     public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
         super.configure(name, params);
-        
-        ComponentLocator locator = ComponentLocator.getCurrentLocator();
-        ConfigurationDao configDao = locator.getDao(ConfigurationDao.class);
-        Map<String, String> dbParams = configDao.getConfiguration(params);
-        
-        _useLocalStorage = Boolean.parseBoolean(dbParams.get(Config.UseLocalStorage.toString()));
-        
         return true;
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/server/src/com/cloud/test/DatabaseConfig.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/test/DatabaseConfig.java b/server/src/com/cloud/test/DatabaseConfig.java
index a6aa094..4e21870 100755
--- a/server/src/com/cloud/test/DatabaseConfig.java
+++ b/server/src/com/cloud/test/DatabaseConfig.java
@@ -205,8 +205,7 @@ public class DatabaseConfig {
         s_configurationDescriptions.put("expunge.interval", "the interval to wait before running the expunge thread");
         s_configurationDescriptions.put("network.throttling.rate", "default data transfer rate in megabits per second allowed per user");
         s_configurationDescriptions.put("multicast.throttling.rate", "default multicast rate in megabits per second allowed");
-        s_configurationDescriptions.put("use.local.storage", "Indicates whether to use local storage pools or shared storage pools for user VMs");
-        s_configurationDescriptions.put("use.local.storage", "Indicates whether to use local storage pools or shared storage pools for system VMs.");
+        s_configurationDescriptions.put("system.vm.use.local.storage", "Indicates whether to use local storage pools or shared storage pools for system VMs.");
         s_configurationDescriptions.put("snapshot.poll.interval", "The time interval in seconds when the management server polls for snapshots to be scheduled.");
         s_configurationDescriptions.put("snapshot.max.hourly", "Maximum hourly snapshots for a volume");
         s_configurationDescriptions.put("snapshot.max.daily", "Maximum daily snapshots for a volume");
@@ -280,7 +279,6 @@ public class DatabaseConfig {
         s_configurationComponents.put("storage.overwrite.provisioning", "UserVmManager");
         s_configurationComponents.put("init", "none");
         s_configurationComponents.put("system.vm.use.local.storage", "ManagementServer");
-        s_configurationComponents.put("use.local.storage", "ManagementServer");
         s_configurationComponents.put("snapshot.poll.interval", "SnapshotManager");
         s_configurationComponents.put("snapshot.max.hourly", "SnapshotManager");
         s_configurationComponents.put("snapshot.max.daily", "SnapshotManager");
@@ -334,7 +332,6 @@ public class DatabaseConfig {
         s_defaultConfigurationValues.put("event.purge.interval", "86400");
         s_defaultConfigurationValues.put("account.cleanup.interval", "86400");
         s_defaultConfigurationValues.put("system.vm.use.local.storage", "false");
-        s_defaultConfigurationValues.put("use.local.storage", "false");
         s_defaultConfigurationValues.put("init", "false");
         s_defaultConfigurationValues.put("cpu.overprovisioning.factor", "1");
         s_defaultConfigurationValues.put("mem.overprovisioning.factor", "1");

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/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 cc48b2f..a1241da 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -588,6 +588,15 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
             throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume.");
         }
 
+        // If local storage is disabled then attaching a volume with local disk offering not allowed
+        DataCenterVO dataCenter = _dcDao.findById(volume.getDataCenterId());
+        if (!dataCenter.isLocalStorageEnabled()) {
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
+            if (diskOffering.getUseLocalStorage()) {
+                throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it");
+            }
+        }
+
         //permission check
         _accountMgr.checkAccess(caller, null, true, volume, vm);
 
@@ -601,11 +610,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
             }
         }
 
-        //If the volume is Ready, check that the volume is stored on shared storage
-        if (!(Volume.State.Allocated.equals(volume.getState()) || Volume.State.UploadOp.equals(volume.getState())) && !_storageMgr.volumeOnSharedStoragePool(volume)) {
-            throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool.");
-        }
-
         if ( !(Volume.State.Allocated.equals(volume.getState()) || Volume.State.Ready.equals(volume.getState()) || Volume.State.UploadOp.equals(volume.getState())) ) {
             throw new InvalidParameterValueException("Volume state must be in Allocated, Ready or in Uploaded state");
         }
@@ -700,10 +704,11 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
             DiskOfferingVO volumeDiskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
             String[] volumeTags = volumeDiskOffering.getTagsArray();
 
+            boolean isVolumeOnSharedPool = !volumeDiskOffering.getUseLocalStorage();
             StoragePoolVO sourcePool = _storagePoolDao.findById(volume.getPoolId());
-            List<StoragePoolVO> sharedVMPools = _storagePoolDao.findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), volumeTags, true);
+            List<StoragePoolVO> matchingVMPools = _storagePoolDao.findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), volumeTags, isVolumeOnSharedPool);
             boolean moveVolumeNeeded = true;
-            if (sharedVMPools.size() == 0) {
+            if (matchingVMPools.size() == 0) {
                 String poolType;
                 if (vmRootVolumePool.getClusterId() != null) {
                     poolType = "cluster";
@@ -714,15 +719,20 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
                 }
                 throw new CloudRuntimeException("There are no storage pools in the VM's " + poolType + " with all of the volume's tags (" + volumeDiskOffering.getTags() + ").");
             } else {
+                long sourcePoolId = sourcePool.getId();
                 Long sourcePoolDcId = sourcePool.getDataCenterId();
                 Long sourcePoolPodId = sourcePool.getPodId();
                 Long sourcePoolClusterId = sourcePool.getClusterId();
-                for (StoragePoolVO vmPool : sharedVMPools) {
+                for (StoragePoolVO vmPool : matchingVMPools) {
+                    long vmPoolId = vmPool.getId();
                     Long vmPoolDcId = vmPool.getDataCenterId();
                     Long vmPoolPodId = vmPool.getPodId();
                     Long vmPoolClusterId = vmPool.getClusterId();
 
-                    if (sourcePoolDcId == vmPoolDcId && sourcePoolPodId == vmPoolPodId && sourcePoolClusterId == vmPoolClusterId) {
+                    // Moving a volume is not required if storage pools belongs to same cluster in case of shared volume or
+                    // identical storage pool in case of local
+                    if (sourcePoolDcId == vmPoolDcId && sourcePoolPodId == vmPoolPodId && sourcePoolClusterId == vmPoolClusterId
+                            && (isVolumeOnSharedPool || sourcePoolId == vmPoolId)) {
                         moveVolumeNeeded = false;
                         break;
                     }
@@ -730,11 +740,15 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
             }
 
             if (moveVolumeNeeded) {
-                // Move the volume to a storage pool in the VM's zone, pod, or cluster
-                try {
-                    volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType);
-                } catch (ConcurrentOperationException e) {
-                    throw new CloudRuntimeException(e.toString());
+                if (isVolumeOnSharedPool) {
+                    // Move the volume to a storage pool in the VM's zone, pod, or cluster
+                    try {
+                        volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType);
+                    } catch (ConcurrentOperationException e) {
+                        throw new CloudRuntimeException(e.toString());
+                    }
+                } else {
+                    throw new CloudRuntimeException("Failed to attach local data volume " + volume.getName() + " to VM " + vm.getDisplayName() + " as migration of local data volume is not allowed");
                 }
             }
         }
@@ -838,11 +852,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
             throw new InvalidParameterValueException("The specified volume is not attached to a VM.");
         }
 
-        // Check that the volume is stored on shared storage
-        if (volume.getState() != Volume.State.Allocated && !_storageMgr.volumeOnSharedStoragePool(volume)) {
-            throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool.");
-        } 
-
         // Check that the VM is in the correct state
         UserVmVO vm = _vmDao.findById(vmId);
         if (vm.getState() != State.Running && vm.getState() != State.Stopped && vm.getState() != State.Destroyed) {
@@ -3276,6 +3285,25 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
 
     }
 
+    private boolean isVMUsingLocalStorage(VMInstanceVO vm)
+    {
+        boolean usesLocalStorage = false;
+        ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId());
+        if (svcOffering.getUseLocalStorage()) {
+            usesLocalStorage = true;
+        } else {
+            List<VolumeVO> volumes = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK);
+            for (VolumeVO vol : volumes) {
+                DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId());
+                if (diskOffering.getUseLocalStorage()) {
+                    usesLocalStorage = true;
+                    break;
+                }
+            }
+        }
+        return usesLocalStorage;
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true)
     public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {
@@ -3308,8 +3336,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
             throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM only");
         }
 
-        ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId());
-        if (svcOffering.getUseLocalStorage()) {
+        if (isVMUsingLocalStorage(vm)) {
             if (s_logger.isDebugEnabled()) {
                 s_logger.debug(vm + " is using Local Storage, cannot migrate this VM.");
             }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/setup/db/create-schema.sql
----------------------------------------------------------------------
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index b78935e..4ea0a15 100755
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -547,6 +547,7 @@ CREATE TABLE  `cloud`.`data_center` (
   `allocation_state` varchar(32) NOT NULL DEFAULT 'Enabled' COMMENT 'Is this data center enabled for allocation for new resources',
   `zone_token` varchar(255),
   `is_security_group_enabled` tinyint NOT NULL DEFAULT 0 COMMENT '1: enabled, 0: not',
+  `is_local_storage_enabled` tinyint NOT NULL DEFAULT 0 COMMENT 'Is local storage offering enabled for this data center; 1: enabled, 0: not',
   `removed` datetime COMMENT 'date removed if not null',
   PRIMARY KEY  (`id`),
   CONSTRAINT `fk_data_center__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`),

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/setup/db/db/schema-302to40.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-302to40.sql b/setup/db/db/schema-302to40.sql
index d1a5ea9..d70697b 100644
--- a/setup/db/db/schema-302to40.sql
+++ b/setup/db/db/schema-302to40.sql
@@ -246,6 +246,10 @@ DEALLOCATE PREPARE stmt1;
 AlTER TABLE physical_network_service_providers ADD CONSTRAINT `fk_pnetwork_service_providers__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`id`) ON DELETE CASCADE;
 UPDATE `cloud`.`configuration` SET description='In second, timeout for creating volume from snapshot' WHERE name='create.volume.from.snapshot.wait';
 
+ALTER TABLE `cloud`.`data_center` ADD COLUMN `is_local_storage_enabled` tinyint NOT NULL DEFAULT 0 COMMENT 'Is local storage offering enabled for this data center; 1: enabled, 0: not';
+UPDATE `cloud`.`data_center` SET `is_local_storage_enabled` = IF ((SELECT `value` FROM `cloud`.`configuration` WHERE `name`='use.local.storage')='true', 1, 0) WHERE `removed` IS NULL;
+DELETE FROM `cloud`.`configuration` where name='use.local.storage';
+
 ALTER TABLE `cloud`.`hypervisor_capabilities` ADD COLUMN `max_data_volumes_limit` int unsigned DEFAULT 6 COMMENT 'Max. data volumes per VM supported by hypervisor';
 UPDATE `cloud`.`hypervisor_capabilities` SET `max_data_volumes_limit`=13 WHERE `hypervisor_type`='XenServer' AND (`hypervisor_version`='6.0' OR `hypervisor_version`='6.0.2');
 INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'management-server', 'event.purge.interval', '86400', 'The interval (in seconds) to wait before running the event purge thread');

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/ui/scripts/configuration.js
----------------------------------------------------------------------
diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js
index 5e76456..96b6fb9 100644
--- a/ui/scripts/configuration.js
+++ b/ui/scripts/configuration.js
@@ -774,6 +774,15 @@
                     label: 'label.description',
                     validation: { required: true }
                   },
+                  storageType: {
+                    label: 'label.storage.type',
+                    select: function(args) {
+                      var items = [];
+                      items.push({id: 'shared', description: 'shared'});
+                      items.push({id: 'local', description: 'local'});
+                      args.response.success({data: items});
+                    }
+                  },
                   isCustomized: {
                     label: 'label.custom.disk.size',
                     isBoolean: true,
@@ -822,6 +831,7 @@
                 array1.push("&name=" + args.data.name);
                 array1.push("&displaytext=" + todb(args.data.description));
 
+                array1.push("&storageType=" + todb(args.data.storageType));
                 array1.push("&customized=" + (args.data.isCustomized=="on"));
                 if(args.$form.find('.form-item[rel=disksize]').css("display") != "none")
                   array1.push("&disksize=" + args.data.disksize);
@@ -941,7 +951,8 @@
                       }
                     },
                     tags: { label: 'label.storage.tags' },
-                    domain: { label: 'label.domain' }
+                    domain: { label: 'label.domain' },
+                    storagetype: { label: 'label.storage.type' }
                   }
                 ],
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/ui/scripts/storage.js
----------------------------------------------------------------------
diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js
index e75244f..8e1f0bd 100644
--- a/ui/scripts/storage.js
+++ b/ui/scripts/storage.js
@@ -1327,18 +1327,16 @@
       }
       else { //jsonObj.type == "DATADISK"
         if (jsonObj.virtualmachineid != null) {
-          if (jsonObj.storagetype == "shared" && (jsonObj.vmstate == "Running" || jsonObj.vmstate == "Stopped" || jsonObj.vmstate == "Destroyed")) {
+          if (jsonObj.vmstate == "Running" || jsonObj.vmstate == "Stopped" || jsonObj.vmstate == "Destroyed") {
             allowedActions.push("detachDisk");
           }
         }
         else { // Disk not attached
-          allowedActions.push("remove");		
-					if(jsonObj.state == "Ready" && isAdmin()) {
+          allowedActions.push("remove");
+          if(jsonObj.state == "Ready" && isAdmin() && jsonObj.storagetype == "shared") {
             allowedActions.push("migrateToAnotherStorage");
-					}
-          if (jsonObj.storagetype == "shared") {
-            allowedActions.push("attachDisk");
           }
+          allowedActions.push("attachDisk");
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2404d349/ui/scripts/system.js
----------------------------------------------------------------------
diff --git a/ui/scripts/system.js b/ui/scripts/system.js
index 3bf43d3..d1d185e 100644
--- a/ui/scripts/system.js
+++ b/ui/scripts/system.js
@@ -3973,6 +3973,7 @@
                       array1.push("&internaldns1=" + todb(args.data.internaldns1));
                       array1.push("&internaldns2=" + todb(args.data.internaldns2));  //internaldns2 can be empty ("") when passed to API
                       array1.push("&domain=" + todb(args.data.domain));
+                      array1.push("&localstorageenabled=" + todb(args.data.localstorageenabled));
                       $.ajax({
                         url: createURL("updateZone&id=" + args.context.physicalResources[0].id + array1.join("")),
                         dataType: "json",
@@ -4016,6 +4017,16 @@
                         domain: {
                           label: 'label.network.domain',
                           isEditable: true
+                        },
+                        localstorageenabled: {
+                          label: 'label.local.storage.enabled',
+                          converter: function(args) {
+                            if(args)
+                              return "true";
+                            else
+                              return "false";
+                          },
+                          isEditable: true
                         }
                       }
                     ],