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/06/29 01:02:49 UTC

[1/4] SolidFire plug-in and related changes

Updated Branches:
  refs/heads/master 02ab2eb38 -> 99227f7b3


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/ui/scripts/sharedFunctions.js
----------------------------------------------------------------------
diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js
index dd9a7d6..bf6464c 100644
--- a/ui/scripts/sharedFunctions.js
+++ b/ui/scripts/sharedFunctions.js
@@ -321,8 +321,8 @@ cloudStack.converters = {
   toBooleanText: function(booleanValue) {
     if(booleanValue == true)
       return "Yes";
-    else if(booleanValue == false)
-      return "No";
+
+    return "No";
   },
   convertHz: function(hz) {
     if (hz == null)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/ui/scripts/storage.js
----------------------------------------------------------------------
diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js
index 2c03d39..ad0965a 100644
--- a/ui/scripts/storage.js
+++ b/ui/scripts/storage.js
@@ -132,6 +132,16 @@
                         else {
                           $diskSize.hide();
                         }
+                        var $minIops = $form.find('.form-item[rel=minIops]');
+                        var $maxIops = $form.find('.form-item[rel=maxIops]');
+                        if (selectedDiskOfferingObj.iscustomizediops == true) {
+                          $minIops.css('display', 'inline-block');
+                          $maxIops.css('display', 'inline-block');
+                        }
+                        else {
+                          $minIops.hide();
+                          $maxIops.hide();
+                        }
                       });
                     }
                   }
@@ -141,7 +151,19 @@
                     label: 'label.disk.size.gb',
                     validation: { required: true, number: true },
                     isHidden: true
-                  }
+                  },
+                  
+                  minIops: {
+                    label: 'label.disk.iops.min',
+                    validation: { required: false, number: true },
+                    isHidden: true
+                  },
+                  
+                  maxIops: {
+                    label: 'label.disk.iops.max',
+                    validation: { required: false, number: true },
+                    isHidden: true
+                  },
 
                 }
               },
@@ -159,6 +181,20 @@
 									  size: args.data.diskSize
 									});
                 }
+                
+                if (selectedDiskOfferingObj.iscustomizediops == true) {
+                    if (args.data.minIops != "" && args.data.minIops > 0) {
+								  $.extend(data, {
+									  miniops: args.data.minIops
+									});
+					}
+				    
+				    if (args.data.maxIops != "" && args.data.maxIops > 0) {
+								  $.extend(data, {
+									  maxiops: args.data.maxIops
+									});
+					}
+                }
 
                 $.ajax({
                   url: createURL('createVolume'),
@@ -1228,6 +1264,24 @@
                           return cloudStack.converters.convertBytes(args);
                       }
                     },
+                    miniops: {
+                      label: 'label.disk.iops.min',
+                      converter: function(args) {
+                        if(args == null || args == 0)
+                          return "";
+                        else
+                          return args;
+                      }
+                    },
+                    maxiops: {
+                      label: 'label.disk.iops.max',
+                      converter: function(args) {
+                        if(args == null || args == 0)
+                          return "";
+                        else
+                          return args;
+                      }
+                    },
                     virtualmachineid: {
                       label: 'VM ID',
                       converter: function(args) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/ui/scripts/system.js
----------------------------------------------------------------------
diff --git a/ui/scripts/system.js b/ui/scripts/system.js
index 92faf5e..135307b 100644
--- a/ui/scripts/system.js
+++ b/ui/scripts/system.js
@@ -12745,11 +12745,11 @@
                   {
                     id: { label: 'label.id' },
                     state: { label: 'label.state' },
-										tags: {
-										  label: 'label.storage.tags',
-											isEditable: true
-										},
-										podname: { label: 'label.pod' },
+					tags: {
+						label: 'label.storage.tags',
+						isEditable: true
+					},
+					podname: { label: 'label.pod' },
                     clustername: { label: 'label.cluster' },
                     type: { label: 'label.type' },
                     ipaddress: { label: 'label.ip.address' },
@@ -12771,6 +12771,15 @@
                         else
                           return cloudStack.converters.convertBytes(args);
                       }
+                    },
+                    capacityiops: {
+                      label: 'label.disk.iops.total',
+                      converter: function(args) {
+                        if (args == null || args == 0)
+                          return "";
+                        else
+                          return args;
+                      }
                     }
                   }
                 ],

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/utils/src/com/cloud/utils/StringUtils.java
----------------------------------------------------------------------
diff --git a/utils/src/com/cloud/utils/StringUtils.java b/utils/src/com/cloud/utils/StringUtils.java
index 359b169..db32dd4 100644
--- a/utils/src/com/cloud/utils/StringUtils.java
+++ b/utils/src/com/cloud/utils/StringUtils.java
@@ -49,6 +49,14 @@ public class StringUtils {
         return org.apache.commons.lang.StringUtils.join(components, delimiter);
     }
 
+    public static boolean isNotBlank(String str) {
+        if (str != null && str.trim().length() > 0) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * @param tags
      * @return List of tags

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostDatastoreSystemMO.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostDatastoreSystemMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostDatastoreSystemMO.java
index 3dcd724..7c548ff 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostDatastoreSystemMO.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostDatastoreSystemMO.java
@@ -26,14 +26,16 @@ import com.vmware.vim25.CustomFieldStringValue;
 import com.vmware.vim25.DatastoreInfo;
 import com.vmware.vim25.DynamicProperty;
 import com.vmware.vim25.HostNasVolumeSpec;
+import com.vmware.vim25.HostScsiDisk;
 import com.vmware.vim25.ManagedObjectReference;
 import com.vmware.vim25.NasDatastoreInfo;
 import com.vmware.vim25.ObjectContent;
 import com.vmware.vim25.ObjectSpec;
 import com.vmware.vim25.PropertyFilterSpec;
 import com.vmware.vim25.PropertySpec;
-import com.vmware.vim25.SelectionSpec;
 import com.vmware.vim25.TraversalSpec;
+import com.vmware.vim25.VmfsDatastoreCreateSpec;
+import com.vmware.vim25.VmfsDatastoreOption;
 
 public class HostDatastoreSystemMO extends BaseMO {
 
@@ -122,6 +124,22 @@ public class HostDatastoreSystemMO extends BaseMO {
 		return null;
 	}
 
+	public List<HostScsiDisk> queryAvailableDisksForVmfs() throws Exception {
+		return _context.getService().queryAvailableDisksForVmfs(_mor, null);
+	}
+
+	public ManagedObjectReference createVmfsDatastore(String datastoreName, HostScsiDisk hostScsiDisk) throws Exception {
+		// just grab the first instance of VmfsDatastoreOption
+		VmfsDatastoreOption vmfsDatastoreOption = _context.getService().queryVmfsDatastoreCreateOptions(_mor, hostScsiDisk.getDevicePath(), 4).get(0);
+
+		VmfsDatastoreCreateSpec vmfsDatastoreCreateSpec = (VmfsDatastoreCreateSpec)vmfsDatastoreOption.getSpec();
+
+		// set the name of the datastore to be created
+		vmfsDatastoreCreateSpec.getVmfs().setVolumeName(datastoreName);
+
+		return _context.getService().createVmfsDatastore(_mor, vmfsDatastoreCreateSpec);
+	}
+
 	public boolean deleteDatastore(String name) throws Exception {
 		ManagedObjectReference morDatastore = findDatastore(name);
 		if(morDatastore != null) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java
index a866fdc..e7fd922 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java
@@ -149,6 +149,13 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
 		throw new Exception("Could not find host default gateway, host is not properly configured?");
 	}
 
+	public HostStorageSystemMO getHostStorageSystemMO() throws Exception {
+		return new HostStorageSystemMO(_context,
+			(ManagedObjectReference)_context.getVimClient().getDynamicProperty(
+				_mor, "configManager.storageSystem")
+		);
+	}
+
 	public HostDatastoreSystemMO getHostDatastoreSystemMO() throws Exception {
 		return new HostDatastoreSystemMO(_context,
 			(ManagedObjectReference)_context.getVimClient().getDynamicProperty(
@@ -797,14 +804,14 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
 	}
 
 	@Override
-	public void unmountDatastore(String poolUuid) throws Exception {
+	public void unmountDatastore(String uuid) throws Exception {
 
 	    if(s_logger.isTraceEnabled())
-			s_logger.trace("vCenter API trace - unmountDatastore(). target MOR: " + _mor.getValue() + ", poolUuid: " + poolUuid);
+			s_logger.trace("vCenter API trace - unmountDatastore(). target MOR: " + _mor.getValue() + ", uuid: " + uuid);
 
     	HostDatastoreSystemMO hostDatastoreSystemMo = getHostDatastoreSystemMO();
-    	if(!hostDatastoreSystemMo.deleteDatastore(poolUuid)) {
-    		String msg = "Unable to unmount datastore. uuid: " + poolUuid;
+    	if(!hostDatastoreSystemMo.deleteDatastore(uuid)) {
+    		String msg = "Unable to unmount datastore. uuid: " + uuid;
     		s_logger.error(msg);
 
     		if(s_logger.isTraceEnabled())

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostStorageSystemMO.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostStorageSystemMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostStorageSystemMO.java
new file mode 100644
index 0000000..d400185
--- /dev/null
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostStorageSystemMO.java
@@ -0,0 +1,51 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.hypervisor.vmware.mo;
+
+import java.util.List;
+
+import com.cloud.hypervisor.vmware.util.VmwareContext;
+
+import com.vmware.vim25.HostInternetScsiHbaStaticTarget;
+import com.vmware.vim25.HostStorageDeviceInfo;
+import com.vmware.vim25.ManagedObjectReference;
+
+public class HostStorageSystemMO extends BaseMO {
+	public HostStorageSystemMO(VmwareContext context, ManagedObjectReference morHostDatastore) {
+		super(context, morHostDatastore);
+	}
+
+	public HostStorageSystemMO(VmwareContext context, String morType, String morValue) {
+		super(context, morType, morValue);
+	}
+	
+	public HostStorageDeviceInfo getStorageDeviceInfo() throws Exception {
+		return (HostStorageDeviceInfo)_context.getVimClient().getDynamicProperty(_mor, "storageDeviceInfo");
+	}
+	
+	public void addInternetScsiStaticTargets(String iScsiHbaDevice, List<HostInternetScsiHbaStaticTarget> lstTargets) throws Exception {
+		_context.getService().addInternetScsiStaticTargets(_mor, iScsiHbaDevice, lstTargets);
+	}
+	
+	public void removeInternetScsiStaticTargets(String iScsiHbaDevice, List<HostInternetScsiHbaStaticTarget> lstTargets) throws Exception {
+		_context.getService().removeInternetScsiStaticTargets(_mor, iScsiHbaDevice, lstTargets);
+	}
+	
+	public void rescanHba(String iScsiHbaDevice) throws Exception {
+		_context.getService().rescanHba(_mor, iScsiHbaDevice);
+	}
+}


[2/4] SolidFire plug-in and related changes

Posted by mt...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java
----------------------------------------------------------------------
diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java
new file mode 100644
index 0000000..839c5a5
--- /dev/null
+++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java
@@ -0,0 +1,901 @@
+package org.apache.cloudstack.storage.datastore.util;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.BasicClientConnectionManager;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class SolidFireUtil
+{
+	public static final String PROVIDER_NAME = "SolidFire";
+
+    public static final String MANAGEMENT_VIP = "mVip";
+    public static final String STORAGE_VIP = "sVip";
+
+    public static final String MANAGEMENT_PORT = "mPort";
+    public static final String STORAGE_PORT = "sPort";
+
+    public static final String CLUSTER_ADMIN_USERNAME = "clusterAdminUsername";
+    public static final String CLUSTER_ADMIN_PASSWORD = "clusterAdminPassword";
+
+    public static final String ACCOUNT_ID = "accountId";
+
+    public static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername";
+    public static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret";
+
+    public static final String CHAP_TARGET_USERNAME = "chapTargetUsername";
+    public static final String CHAP_TARGET_SECRET = "chapTargetSecret";
+
+	public static long createSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
+			String strSfVolumeName, long lSfAccountId, long lTotalSize, boolean bEnable512e,
+			long lMinIops, long lMaxIops, long lBurstIops) throws Exception
+	{
+		final Gson gson = new GsonBuilder().create();
+		
+		VolumeToCreate volumeToCreate = new VolumeToCreate(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e,
+			lMinIops, lMaxIops, lBurstIops);
+		
+		String strVolumeToCreateJson = gson.toJson(volumeToCreate);
+		
+		String strVolumeCreateResultJson = executeJsonRpc(strVolumeToCreateJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+		
+		VolumeCreateResult volumeCreateResult = gson.fromJson(strVolumeCreateResultJson, VolumeCreateResult.class);
+		
+		verifyResult(volumeCreateResult.result, strVolumeCreateResultJson, gson);
+		
+		return volumeCreateResult.result.volumeID;
+	}
+	
+	public static void deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) throws Exception
+	{
+		final Gson gson = new GsonBuilder().create();
+		
+		VolumeToDelete volumeToDelete = new VolumeToDelete(lVolumeId);
+		
+		String strVolumeToDeleteJson = gson.toJson(volumeToDelete);
+		
+		executeJsonRpc(strVolumeToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+	}
+
+   public static void purgeSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) throws Exception
+    {
+        final Gson gson = new GsonBuilder().create();
+
+        VolumeToPurge volumeToPurge = new VolumeToPurge(lVolumeId);
+
+        String strVolumeToPurgeJson = gson.toJson(volumeToPurge);
+
+        executeJsonRpc(strVolumeToPurgeJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+    }
+
+	public static SolidFireVolume getSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) throws Exception
+	{
+		final Gson gson = new GsonBuilder().create();
+		
+		VolumeToGet volumeToGet = new VolumeToGet(lVolumeId);
+		
+		String strVolumeToGetJson = gson.toJson(volumeToGet);
+		
+		String strVolumeGetResultJson = executeJsonRpc(strVolumeToGetJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+		
+		VolumeGetResult volumeGetResult = gson.fromJson(strVolumeGetResultJson, VolumeGetResult.class);
+		
+		verifyResult(volumeGetResult.result, strVolumeGetResultJson, gson);
+		
+		String strVolumeName = getVolumeName(volumeGetResult, lVolumeId);
+		String strVolumeIqn = getVolumeIqn(volumeGetResult, lVolumeId);
+		long lAccountId = getVolumeAccountId(volumeGetResult, lVolumeId);
+		String strVolumeStatus = getVolumeStatus(volumeGetResult, lVolumeId);
+		
+		return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus);
+	}
+
+	public static List<SolidFireVolume> getSolidFireVolumesForAccountId(String strSfMvip, int iSfPort,
+	        String strSfAdmin, String strSfPassword, long lAccountId) throws Exception
+	{
+        final Gson gson = new GsonBuilder().create();
+
+        VolumesToGetForAccount volumesToGetForAccount = new VolumesToGetForAccount(lAccountId);
+
+        String strVolumesToGetForAccountJson = gson.toJson(volumesToGetForAccount);
+
+        String strVolumesGetForAccountResultJson = executeJsonRpc(strVolumesToGetForAccountJson, strSfMvip, iSfPort,
+                strSfAdmin, strSfPassword);
+
+        VolumeGetResult volumeGetResult = gson.fromJson(strVolumesGetForAccountResultJson, VolumeGetResult.class);
+
+        verifyResult(volumeGetResult.result, strVolumesGetForAccountResultJson, gson);
+
+        List<SolidFireVolume> sfVolumes = new ArrayList<SolidFireVolume>();
+
+        for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) {
+            sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status));
+        }
+
+        return sfVolumes;
+	}
+
+	private static final String ACTIVE = "active";
+
+	public static class SolidFireVolume
+	{
+	    private final long _id;
+		private final String _name;
+		private final String _iqn;
+		private final long _accountId;
+		private final String _status;
+		
+		public SolidFireVolume(long id, String name, String iqn,
+		        long accountId, String status)
+		{
+			_id = id;
+			_name = name;
+			_iqn = "/" + iqn + "/0";
+			_accountId = accountId;
+			_status = status;
+		}
+		
+		public long getId()
+		{
+			return _id;
+		}
+		
+		public String getName()
+		{
+			return _name;
+		}
+		
+		public String getIqn()
+		{
+			return _iqn;
+		}
+		
+		public long getAccountId()
+		{
+		    return _accountId;
+		}
+		
+		public boolean isActive()
+		{
+		    return ACTIVE.equalsIgnoreCase(_status);
+		}
+		
+		@Override
+        public int hashCode() {
+            return (int)_id;
+        }
+        
+        @Override
+        public String toString() {
+            return _name;
+        }
+        
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof SolidFireVolume)) {
+                return false;
+            }
+            
+            SolidFireVolume sfv = (SolidFireVolume)obj;
+            
+            if (_id == sfv._id && _name.equals(sfv._name) &&
+                _iqn.equals(sfv._iqn) && isActive() == sfv.isActive()) {
+                return true;
+            }
+            
+            return false;
+        }
+	}
+	
+	public static long createSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
+	        String strAccountName) throws Exception
+	{
+		final Gson gson = new GsonBuilder().create();
+		
+		AccountToAdd accountToAdd = new AccountToAdd(strAccountName);
+		
+		String strAccountAddJson = gson.toJson(accountToAdd);
+		
+		String strAccountAddResultJson = executeJsonRpc(strAccountAddJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+		
+		AccountAddResult accountAddResult = gson.fromJson(strAccountAddResultJson, AccountAddResult.class);
+		
+		verifyResult(accountAddResult.result, strAccountAddResultJson, gson);
+		
+		return accountAddResult.result.accountID;
+	}
+	
+	public static void deleteSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
+	        long lAccountId) throws Exception
+	{
+		final Gson gson = new GsonBuilder().create();
+		
+		AccountToRemove accountToRemove = new AccountToRemove(lAccountId);
+		
+		String strAccountToRemoveJson = gson.toJson(accountToRemove);
+		
+		executeJsonRpc(strAccountToRemoveJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+	}
+	
+	public static SolidFireAccount getSolidFireAccountById(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
+	        long lSfAccountId) throws Exception
+	{
+		final Gson gson = new GsonBuilder().create();
+		
+		AccountToGetById accountToGetById = new AccountToGetById(lSfAccountId);
+		
+		String strAccountToGetByIdJson = gson.toJson(accountToGetById);
+		
+		String strAccountGetByIdResultJson = executeJsonRpc(strAccountToGetByIdJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+		
+		AccountGetResult accountGetByIdResult = gson.fromJson(strAccountGetByIdResultJson, AccountGetResult.class);
+		
+		verifyResult(accountGetByIdResult.result, strAccountGetByIdResultJson, gson);
+		
+		String strSfAccountName = accountGetByIdResult.result.account.username;
+		String strSfAccountInitiatorSecret = accountGetByIdResult.result.account.initiatorSecret;
+		String strSfAccountTargetSecret = accountGetByIdResult.result.account.targetSecret;
+		
+		return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret);
+	}
+
+	public static SolidFireAccount getSolidFireAccountByName(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
+	        String strSfAccountName) throws Exception
+    {
+        final Gson gson = new GsonBuilder().create();
+
+        AccountToGetByName accountToGetByName = new AccountToGetByName(strSfAccountName);
+
+        String strAccountToGetByNameJson = gson.toJson(accountToGetByName);
+
+        String strAccountGetByNameResultJson = executeJsonRpc(strAccountToGetByNameJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+
+        AccountGetResult accountGetByNameResult = gson.fromJson(strAccountGetByNameResultJson, AccountGetResult.class);
+
+        verifyResult(accountGetByNameResult.result, strAccountGetByNameResultJson, gson);
+
+        long lSfAccountId = accountGetByNameResult.result.account.accountID;
+        String strSfAccountInitiatorSecret = accountGetByNameResult.result.account.initiatorSecret;
+        String strSfAccountTargetSecret = accountGetByNameResult.result.account.targetSecret;
+
+        return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret);
+    }
+	
+	public static class SolidFireAccount
+	{
+		private final long _id;
+		private final String _name;
+		private final String _initiatorSecret;
+		private final String _targetSecret;
+		
+		public SolidFireAccount(long id, String name, String initiatorSecret, String targetSecret)
+		{
+			_id = id;
+			_name = name;
+			_initiatorSecret = initiatorSecret;
+			_targetSecret = targetSecret;
+		}
+		
+		public long getId()
+		{
+			return _id;
+		}
+		
+		public String getName()
+		{
+			return _name;
+		}
+		
+		public String getInitiatorSecret()
+		{
+			return _initiatorSecret;
+		}
+		
+		public String getTargetSecret()
+		{
+			return _targetSecret;
+		}
+		
+		@Override
+		public int hashCode() {
+		    return (int)_id;
+		}
+		
+		@Override
+		public String toString() {
+		    return _name;
+		}
+		
+		@Override
+		public boolean equals(Object obj) {
+		    if (!(obj instanceof SolidFireAccount)) {
+		        return false;
+		    }
+		    
+		    SolidFireAccount sfa = (SolidFireAccount)obj;
+		    
+		    if (_id == sfa._id && _name.equals(sfa._name) &&
+		        _initiatorSecret.equals(sfa._initiatorSecret) &&
+		        _targetSecret.equals(sfa._targetSecret)) {
+		        return true;
+		    }
+		    
+		    return false;
+		}
+	}
+
+    public static List<SolidFireVolume> getDeletedVolumes(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword) throws Exception
+    {
+        final Gson gson = new GsonBuilder().create();
+
+        ListDeletedVolumes listDeletedVolumes = new ListDeletedVolumes();
+
+        String strListDeletedVolumesJson = gson.toJson(listDeletedVolumes);
+
+        String strListDeletedVolumesResultJson = executeJsonRpc(strListDeletedVolumesJson, strSfMvip, iSfPort,
+                strSfAdmin, strSfPassword);
+
+        VolumeGetResult volumeGetResult = gson.fromJson(strListDeletedVolumesResultJson, VolumeGetResult.class);
+
+        verifyResult(volumeGetResult.result, strListDeletedVolumesResultJson, gson);
+
+        List<SolidFireVolume> deletedVolumes = new ArrayList<SolidFireVolume> ();
+
+        for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) {
+            deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status));
+        }
+
+        return deletedVolumes;
+    }
+	
+	public static long createSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strVagName) throws Exception
+	{
+		final Gson gson = new GsonBuilder().create();
+		
+		VagToCreate vagToCreate = new VagToCreate(strVagName);
+		
+		String strVagCreateJson = gson.toJson(vagToCreate);
+		
+		String strVagCreateResultJson = executeJsonRpc(strVagCreateJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+		
+		VagCreateResult vagCreateResult = gson.fromJson(strVagCreateResultJson, VagCreateResult.class);
+		
+		verifyResult(vagCreateResult.result, strVagCreateResultJson, gson);
+		
+		return vagCreateResult.result.volumeAccessGroupID;
+	}
+	
+	public static void deleteSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId) throws Exception
+	{
+		final Gson gson = new GsonBuilder().create();
+		
+		VagToDelete vagToDelete = new VagToDelete(lVagId);
+		
+		String strVagToDeleteJson = gson.toJson(vagToDelete);
+		
+		executeJsonRpc(strVagToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
+	}
+	
+	@SuppressWarnings("unused")
+	private static final class VolumeToCreate
+	{
+		private final String method = "CreateVolume";
+		private final VolumeToCreateParams params;
+		
+		private VolumeToCreate(final String strVolumeName, final long lAccountId, final long lTotalSize,
+				final boolean bEnable512e, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
+		{
+			params = new VolumeToCreateParams(strVolumeName, lAccountId, lTotalSize, bEnable512e,
+					lMinIOPS, lMaxIOPS, lBurstIOPS);
+		}
+		
+		private static final class VolumeToCreateParams
+		{
+			private final String name;
+			private final long accountID;
+			private final long totalSize;
+			private final boolean enable512e;
+			private final VolumeToCreateParamsQoS qos;
+			
+			private VolumeToCreateParams(final String strVolumeName, final long lAccountId, final long lTotalSize,
+					final boolean bEnable512e, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
+			{
+				name = strVolumeName;
+				accountID = lAccountId;
+				totalSize = lTotalSize;
+				enable512e = bEnable512e;
+				
+				qos = new VolumeToCreateParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS);
+			}
+			
+			private static final class VolumeToCreateParamsQoS
+			{
+				private final long minIOPS;
+				private final long maxIOPS;
+				private final long burstIOPS;
+				
+				private VolumeToCreateParamsQoS(final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
+				{
+					minIOPS = lMinIOPS;
+					maxIOPS = lMaxIOPS;
+					burstIOPS = lBurstIOPS;
+				}
+			}
+		}
+	}
+	
+	@SuppressWarnings("unused")
+	private static final class VolumeToDelete
+	{
+		private final String method = "DeleteVolume";
+		private final VolumeToDeleteParams params;
+		
+		private VolumeToDelete(final long lVolumeId)
+		{
+			params = new VolumeToDeleteParams(lVolumeId);
+		}
+		
+		private static final class VolumeToDeleteParams
+		{
+			private long volumeID;
+			
+			private VolumeToDeleteParams(final long lVolumeId)
+			{
+				volumeID = lVolumeId;
+			}
+		}
+	}
+
+    @SuppressWarnings("unused")
+    private static final class ListDeletedVolumes
+    {
+        private final String method = "ListDeletedVolumes";
+    }
+
+    @SuppressWarnings("unused")
+    private static final class VolumeToPurge
+    {
+        private final String method = "PurgeDeletedVolume";
+        private final VolumeToPurgeParams params;
+
+        private VolumeToPurge(final long lVolumeId)
+        {
+            params = new VolumeToPurgeParams(lVolumeId);
+        }
+
+        private static final class VolumeToPurgeParams
+        {
+            private long volumeID;
+
+            private VolumeToPurgeParams(final long lVolumeId)
+            {
+                volumeID = lVolumeId;
+            }
+        }
+    }
+
+	@SuppressWarnings("unused")
+	private static final class VolumeToGet
+	{
+		private final String method = "ListActiveVolumes";
+		private final VolumeToGetParams params;
+		
+		private VolumeToGet(final long lVolumeId)
+		{
+			params = new VolumeToGetParams(lVolumeId);
+		}
+		
+		private static final class VolumeToGetParams
+		{
+			private final long startVolumeID;
+			private final long limit = 1;
+			
+			private VolumeToGetParams(final long lVolumeId)
+			{
+				startVolumeID = lVolumeId;
+			}
+		}
+	}
+	
+    @SuppressWarnings("unused")
+    private static final class VolumesToGetForAccount
+    {
+        private final String method = "ListVolumesForAccount";
+        private final VolumesToGetForAccountParams params;
+
+        private VolumesToGetForAccount(final long lAccountId)
+        {
+            params = new VolumesToGetForAccountParams(lAccountId);
+        }
+
+        private static final class VolumesToGetForAccountParams
+        {
+            private final long accountID;
+
+            private VolumesToGetForAccountParams(final long lAccountId)
+            {
+                accountID = lAccountId;
+            }
+        }
+    }
+
+	@SuppressWarnings("unused")
+	private static final class AccountToAdd
+	{
+		private final String method = "AddAccount";
+		private final AccountToAddParams params;
+		
+		private AccountToAdd(final String strAccountName)
+		{
+			params = new AccountToAddParams(strAccountName);
+		}
+		
+		private static final class AccountToAddParams
+		{
+			private final String username;
+			
+			private AccountToAddParams(final String strAccountName)
+			{
+				username = strAccountName;
+			}
+		}
+	}
+	
+	@SuppressWarnings("unused")
+	private static final class AccountToRemove
+	{
+		private final String method = "RemoveAccount";
+		private final AccountToRemoveParams params;
+		
+		private AccountToRemove(final long lAccountId)
+		{
+			params = new AccountToRemoveParams(lAccountId);
+		}
+		
+		private static final class AccountToRemoveParams
+		{
+			private long accountID;
+			
+			private AccountToRemoveParams(final long lAccountId)
+			{
+				accountID = lAccountId;
+			}
+		}
+	}
+	
+	@SuppressWarnings("unused")
+	private static final class AccountToGetById
+	{
+		private final String method = "GetAccountByID";
+		private final AccountToGetByIdParams params;
+		
+		private AccountToGetById(final long lAccountId)
+		{
+			params = new AccountToGetByIdParams(lAccountId);
+		}
+		
+		private static final class AccountToGetByIdParams
+		{
+			private final long accountID;
+			
+			private AccountToGetByIdParams(final long lAccountId)
+			{
+				accountID = lAccountId;
+			}
+		}
+	}
+	
+    @SuppressWarnings("unused")
+    private static final class AccountToGetByName
+    {
+        private final String method = "GetAccountName";
+        private final AccountToGetByNameParams params;
+
+        private AccountToGetByName(final String strUsername)
+        {
+            params = new AccountToGetByNameParams(strUsername);
+        }
+
+        private static final class AccountToGetByNameParams
+        {
+            private final String username;
+
+            private AccountToGetByNameParams(final String strUsername)
+            {
+                username = strUsername;
+            }
+        }
+    }
+
+	@SuppressWarnings("unused")
+	private static final class VagToCreate
+	{
+		private final String method = "CreateVolumeAccessGroup";
+		private final VagToCreateParams params;
+		
+		private VagToCreate(final String strVagName)
+		{
+			params = new VagToCreateParams(strVagName);
+		}
+		
+		private static final class VagToCreateParams
+		{
+			private final String name;
+			
+			private VagToCreateParams(final String strVagName)
+			{
+				name = strVagName;
+			}
+		}
+	}
+	
+	@SuppressWarnings("unused")
+	private static final class VagToDelete
+	{
+		private final String method = "DeleteVolumeAccessGroup";
+		private final VagToDeleteParams params;
+		
+		private VagToDelete(final long lVagId)
+		{
+			params = new VagToDeleteParams(lVagId);
+		}
+		
+		private static final class VagToDeleteParams
+		{
+			private long volumeAccessGroupID;
+			
+			private VagToDeleteParams(final long lVagId)
+			{
+				volumeAccessGroupID = lVagId;
+			}
+		}
+	}
+	
+	private static final class VolumeCreateResult
+	{
+		private Result result;
+		
+		private static final class Result
+		{
+			private long volumeID;
+		}
+	}
+	
+	private static final class VolumeGetResult
+	{
+		private Result result;
+		
+		private static final class Result
+		{
+			private Volume[] volumes;
+			
+			private static final class Volume
+			{
+				private long volumeID;
+				private String name;
+				private String iqn;
+				private long accountID;
+				private String status;
+			}
+		}
+	}
+	
+	private static final class AccountAddResult
+	{
+		private Result result;
+		
+		private static final class Result
+		{
+			private long accountID;
+		}
+	}
+	
+	private static final class AccountGetResult
+	{
+		private Result result;
+		
+		private static final class Result
+		{
+			private Account account;
+			
+			private static final class Account
+			{
+			    private long accountID;
+				private String username;
+				private String initiatorSecret;
+				private String targetSecret;
+			}
+		}
+	}
+	
+	private static final class VagCreateResult
+	{
+		private Result result;
+		
+		private static final class Result
+		{
+			private long volumeAccessGroupID;
+		}
+	}
+	
+	private static final class JsonError
+	{
+		private Error error;
+		
+		private static final class Error
+		{
+			private String message;
+		}
+	}
+	
+	private static DefaultHttpClient getHttpClient(int iPort) throws NoSuchAlgorithmException, KeyManagementException {
+        SSLContext sslContext = SSLContext.getInstance("SSL");
+        X509TrustManager tm = new X509TrustManager() {
+            public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
+            }
+
+            public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
+            }
+
+            public X509Certificate[] getAcceptedIssuers() {
+                return null;
+            }
+        };
+        
+        sslContext.init(null, new TrustManager[] { tm }, new SecureRandom());
+        
+        SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+        SchemeRegistry registry = new SchemeRegistry();
+        
+        registry.register(new Scheme("https", iPort, socketFactory));
+        
+        BasicClientConnectionManager mgr = new BasicClientConnectionManager(registry);
+        DefaultHttpClient client = new DefaultHttpClient();
+        
+        return new DefaultHttpClient(mgr, client.getParams());
+	}
+	
+	private static String executeJsonRpc(String strJsonToExecute, String strMvip, int iPort,
+			String strAdmin, String strPassword) throws Exception
+	{
+	    DefaultHttpClient httpClient = null;
+	    StringBuilder sb = new StringBuilder();
+	    
+	    try
+	    {
+    		StringEntity input = new StringEntity(strJsonToExecute);
+    		
+    		input.setContentType("application/json");
+    		
+    		httpClient = getHttpClient(iPort);
+    		
+    		URI uri = new URI("https://" + strMvip + ":" + iPort + "/json-rpc/1.0");
+    		AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_SCHEME);
+    		UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(strAdmin, strPassword);
+    		
+    		httpClient.getCredentialsProvider().setCredentials(authScope, credentials);
+    		
+    		HttpPost postRequest = new HttpPost(uri);
+    		
+    		postRequest.setEntity(input);
+    		
+    		HttpResponse response = httpClient.execute(postRequest);
+    		
+    		if (!isSuccess(response.getStatusLine().getStatusCode()))
+    		{
+    			throw new RuntimeException("Failed on JSON-RPC API call. HTTP error code = " + response.getStatusLine().getStatusCode());
+    		}
+    		
+    		BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
+    		
+    		String strOutput;
+    		
+    		
+    		while ((strOutput = br.readLine()) != null)
+    		{
+    			sb.append(strOutput);
+    		}
+		} finally {
+	        if (httpClient != null) {
+	            try {
+	                httpClient.getConnectionManager().shutdown();
+	            } catch (Throwable t) {}
+	        }
+	    }
+		
+		return sb.toString();
+	}
+	
+	private static boolean isSuccess(int iCode) {
+	    return iCode >= 200 && iCode < 300;
+	}
+	
+	private static void verifyResult(Object obj, String strJson, Gson gson) throws IllegalStateException
+	{
+		if (obj != null)
+		{
+			return;
+		}
+		
+		JsonError jsonError = gson.fromJson(strJson, JsonError.class);
+		
+		if (jsonError != null)
+		{
+			throw new IllegalStateException(jsonError.error.message);
+		}
+		
+		throw new IllegalStateException("Problem with the following JSON: " + strJson);
+	}
+	
+	private static String getVolumeName(VolumeGetResult volumeGetResult, long lVolumeId) throws Exception
+	{
+		if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
+			volumeGetResult.result.volumes[0].volumeID == lVolumeId)
+		{
+			return volumeGetResult.result.volumes[0].name;
+		}
+		
+		throw new Exception("Could not determine the name of the volume, " +
+		        "but the volume was created with an ID of " + lVolumeId + ".");
+	}
+	
+	private static String getVolumeIqn(VolumeGetResult volumeGetResult, long lVolumeId) throws Exception
+	{
+		if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
+			volumeGetResult.result.volumes[0].volumeID == lVolumeId)
+		{
+			return volumeGetResult.result.volumes[0].iqn;
+		}
+		
+		throw new Exception("Could not determine the IQN of the volume, " +
+				"but the volume was created with an ID of " + lVolumeId + ".");
+	}
+
+    private static long getVolumeAccountId(VolumeGetResult volumeGetResult, long lVolumeId) throws Exception
+    {
+        if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
+            volumeGetResult.result.volumes[0].volumeID == lVolumeId)
+        {
+            return volumeGetResult.result.volumes[0].accountID;
+        }
+
+        throw new Exception("Could not determine the volume's account ID, " +
+                "but the volume was created with an ID of " + lVolumeId + ".");
+    }
+
+    private static String getVolumeStatus(VolumeGetResult volumeGetResult, long lVolumeId) throws Exception
+    {
+        if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
+            volumeGetResult.result.volumes[0].volumeID == lVolumeId)
+        {
+            return volumeGetResult.result.volumes[0].status;
+        }
+
+        throw new Exception("Could not determine the status of the volume, " +
+                "but the volume was created with an ID of " + lVolumeId + ".");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
index 7022ee6..385ca36 100644
--- a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
@@ -67,6 +67,8 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase<DiskOfferingJoinVO,
         diskOfferingResponse.setDisplayText(offering.getDisplayText());
         diskOfferingResponse.setCreated(offering.getCreated());
         diskOfferingResponse.setDiskSize(offering.getDiskSize() / (1024 * 1024 * 1024));
+        diskOfferingResponse.setMinIops(offering.getMinIops());
+        diskOfferingResponse.setMaxIops(offering.getMaxIops());
 
         diskOfferingResponse.setDomain(offering.getDomainName());
         diskOfferingResponse.setDomainId(offering.getDomainUuid());
@@ -74,6 +76,7 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase<DiskOfferingJoinVO,
 
         diskOfferingResponse.setTags(offering.getTags());
         diskOfferingResponse.setCustomized(offering.isCustomized());
+        diskOfferingResponse.setCustomizedIops(offering.isCustomizedIops());
         diskOfferingResponse.setStorageType(offering.isUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString());
         diskOfferingResponse.setBytesReadRate(offering.getBytesReadRate());
         diskOfferingResponse.setBytesWriteRate(offering.getBytesWriteRate());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java
index 68d9113..503a563 100644
--- a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java
@@ -83,6 +83,7 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase<StoragePoolJoinVO, Lo
         long allocatedSize = pool.getUsedCapacity() + pool.getReservedCapacity();
         poolResponse.setDiskSizeTotal(pool.getCapacityBytes());
         poolResponse.setDiskSizeAllocated(allocatedSize);
+        poolResponse.setCapacityIops(pool.getCapacityIops());
 
         // TODO: StatsCollector does not persist data
         StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId());
@@ -144,6 +145,7 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase<StoragePoolJoinVO, Lo
         long allocatedSize = ApiDBUtils.getStorageCapacitybyPool(pool.getId(), capacityType);
         poolResponse.setDiskSizeTotal(pool.getCapacityBytes());
         poolResponse.setDiskSizeAllocated(allocatedSize);
+        poolResponse.setCapacityIops(pool.getCapacityIops());
 
         // TODO: StatsCollector does not persist data
         StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
index ed2732e..1c18c96 100644
--- a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
@@ -101,6 +101,9 @@ public class VolumeJoinDaoImpl extends GenericDaoBase<VolumeJoinVO, Long> implem
         // Show the virtual size of the volume
         volResponse.setSize(volume.getSize());
 
+        volResponse.setMinIops(volume.getMinIops());
+        volResponse.setMaxIops(volume.getMaxIops());
+
         volResponse.setCreated(volume.getCreated());
         volResponse.setState(volume.getState().toString());
         if (volume.getState() == Volume.State.UploadOp) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
index 2336a48..58e8370 100644
--- a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
@@ -61,6 +61,15 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity,
     @Column(name="customized")
     private boolean customized;
 
+    @Column(name="customized_iops")
+    private Boolean customizedIops;
+
+    @Column(name="min_iops")
+    private Long minIops;
+
+    @Column(name="max_iops")
+    private Long maxIops;
+
     @Column(name="sort_key")
     int sortKey;
 
@@ -179,6 +188,30 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity,
         this.customized = customized;
     }
 
+    public Boolean isCustomizedIops() {
+        return customizedIops;
+    }
+
+    public void setCustomizedIops(Boolean customizedIops) {
+        this.customizedIops = customizedIops;
+    }
+
+    public Long getMinIops() {
+        return minIops;
+    }
+
+    public void setMinIops(Long minIops) {
+        this.minIops = minIops;
+    }
+
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
+    public void setMaxIops(Long maxIops) {
+        this.maxIops = maxIops;
+    }
+
     public boolean isDisplayOffering() {
         return displayOffering;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/api/query/vo/StoragePoolJoinVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/vo/StoragePoolJoinVO.java b/server/src/com/cloud/api/query/vo/StoragePoolJoinVO.java
index c0d5ee9..69f2204 100644
--- a/server/src/com/cloud/api/query/vo/StoragePoolJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/StoragePoolJoinVO.java
@@ -60,7 +60,6 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
     @Column(name="host_address")
     private String hostAddress;
 
-
     @Column(name="status")
     @Enumerated(value=EnumType.STRING)
     private StoragePoolStatus status;
@@ -109,7 +108,6 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
     @Column(name="pod_name")
     private String podName;
 
-
     @Column(name="tag")
     private String tag;
 
@@ -119,7 +117,6 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
     @Column(name="disk_reserved_capacity")
     private long reservedCapacity;
 
-
     @Column(name="job_id")
     private Long jobId;
 
@@ -133,6 +130,8 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
     @Enumerated(value = EnumType.STRING)
     private ScopeType scope;
 
+    @Column(name="capacity_iops")
+    private Long capacityIops;
 
     @Column(name = "hypervisor")
     @Enumerated(value = EnumType.STRING)
@@ -243,6 +242,14 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
         this.capacityBytes = capacityBytes;
     }
 
+    public Long getCapacityIops() {
+        return capacityIops;
+    }
+
+    public void setCapacityIops(Long capacityIops) {
+        this.capacityIops = capacityIops;
+    }
+
     public long getClusterId() {
         return clusterId;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
index 1f07f52..701e195 100644
--- a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
@@ -58,6 +58,12 @@ public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity {
     @Column(name = "size")
     long size;
 
+    @Column(name = "min_iops")
+    Long minIops;
+
+    @Column(name = "max_iops")
+    Long maxIops;
+
     @Column(name = "state")
     @Enumerated(value = EnumType.STRING)
     private Volume.State state;
@@ -337,14 +343,27 @@ public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity {
         this.size = size;
     }
 
+    public Long getMinIops() {
+        return minIops;
+    }
 
+    public void setMinIops(Long minIops) {
+        this.minIops = minIops;
+    }
+
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
+    public void setMaxIops(Long maxIops) {
+        this.maxIops = maxIops;
+    }
 
     public Volume.State getState() {
         return state;
     }
 
 
-
     public void setState(Volume.State state) {
         this.state = state;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 98eae37..1b99b63 100755
--- a/server/src/com/cloud/configuration/ConfigurationManager.java
+++ b/server/src/com/cloud/configuration/ConfigurationManager.java
@@ -102,14 +102,18 @@ public interface ConfigurationManager extends ConfigurationService, Manager {
      * @param isCustomized
      * @param localStorageRequired
      * @param isDisplayOfferingEnabled
+     * @param isCustomizedIops (is admin allowing users to set custom iops?)
+     * @param minIops
+     * @param maxIops
      * @param bytesReadRate
      * @param bytesWriteRate
      * @param iopsReadRate
      * @param iopsWriteRate
      * @return newly created disk offering
      */
-    DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled,
-            Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate);
+    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);
 
     /**
      * Creates a new pod

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 3840c12..2089f82 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -2297,8 +2297,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
     @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, boolean localStorageRequired, boolean isDisplayOfferingEnabled,
-            Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) {
+    public 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 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.");
@@ -2314,8 +2315,44 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             isCustomized = true;
         }
 
+        if (isCustomizedIops != null) {
+            bytesReadRate = null;
+            bytesWriteRate = null;
+            iopsReadRate = null;
+            iopsWriteRate = null;
+
+            if (isCustomizedIops) {
+            	minIops = null;
+            	maxIops = null;
+            }
+            else {
+                if (minIops == null && maxIops == null) {
+                    minIops = 0L;
+                    maxIops = 0L;
+                }
+                else {
+                	if (minIops == null || minIops <= 0) {
+                	    throw new InvalidParameterValueException("The min IOPS must be greater than 0.");
+        	        }
+
+                	if (maxIops == null) {
+        	        	maxIops = 0L;
+        	        }
+
+                	if (minIops > maxIops) {
+                		throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS.");
+                	}
+                }
+            }
+        }
+        else {
+            minIops = null;
+            maxIops = null;
+        }
+
         tags = cleanupTags(tags);
-        DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized);
+        DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized,
+        		isCustomizedIops, minIops, maxIops);
         newDiskOffering.setUseLocalStorage(localStorageRequired);
         newDiskOffering.setDisplayOffering(isDisplayOfferingEnabled);
 
@@ -2355,7 +2392,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         Long domainId = cmd.getDomainId();
 
         if (!isCustomized && numGibibytes == null) {
-            throw new InvalidParameterValueException("Disksize is required for non-customized disk offering");
+            throw new InvalidParameterValueException("Disksize is required for a non-customized disk offering");
         }
 
         boolean localStorageRequired = false;
@@ -2369,11 +2406,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             }
         }
 
+        Boolean isCustomizedIops = cmd.isCustomizedIops();
+        Long minIops = cmd.getMinIops();
+        Long maxIops = cmd.getMaxIops();
         Long bytesReadRate = cmd.getBytesReadRate();
         Long bytesWriteRate = cmd.getBytesWriteRate();
         Long iopsReadRate = cmd.getIopsReadRate();
         Long iopsWriteRate = cmd.getIopsWriteRate();
-        return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized, localStorageRequired, isDisplayOfferingEnabled, bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate);
+
+        return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized,
+        		localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops,
+        		bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/server/ConfigurationServerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java
index 1ddfcfa..9e79b76 100755
--- a/server/src/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/com/cloud/server/ConfigurationServerImpl.java
@@ -932,7 +932,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
         diskSize = diskSize * 1024 * 1024 * 1024;
         tags = cleanupTags(tags);
 
-        DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized);
+        DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized, null, null, null);
         newDiskOffering.setUniqueName("Cloud.Com-" + name);
         newDiskOffering.setSystemUse(isSystemUse);
         newDiskOffering = _diskOfferingDao.persistDeafultDiskOffering(newDiskOffering);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/storage/StorageManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/StorageManager.java b/server/src/com/cloud/storage/StorageManager.java
index 29c7ebc..df69092 100755
--- a/server/src/com/cloud/storage/StorageManager.java
+++ b/server/src/com/cloud/storage/StorageManager.java
@@ -99,28 +99,23 @@ public interface StorageManager extends StorageService {
 
     void cleanupSecondaryStorage(boolean recurring);
 
-
 	HypervisorType getHypervisorTypeFromFormat(ImageFormat format);
 
+    boolean storagePoolHasEnoughIops(List<Volume> volume, StoragePool pool);
+
     boolean storagePoolHasEnoughSpace(List<Volume> volume, StoragePool pool);
 
-    
     boolean registerHostListener(String providerUuid, HypervisorHostListener listener);
 
     StoragePool findStoragePool(DiskProfile dskCh, DataCenterVO dc,
             HostPodVO pod, Long clusterId, Long hostId, VMInstanceVO vm,
             Set<StoragePool> avoid);
 
-
     void connectHostToSharedPool(long hostId, long poolId)
             throws StorageUnavailableException;
 
     void createCapacityEntry(long poolId);
 
-
-
-
-
     DataStore createLocalStorage(Host host, StoragePoolInfo poolInfo) throws ConnectionException;
 
     BigDecimal getStorageOverProvisioningFactor(Long dcId);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 b3e8b96..241f6e6 100755
--- a/server/src/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/com/cloud/storage/StorageManagerImpl.java
@@ -694,9 +694,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
                 throw new InvalidParameterValueException(
                         "Missing parameter hypervisor. Hypervisor type is required to create zone wide primary storage.");
             }
-            if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.VMware) {
+            if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.VMware &&
+                hypervisorType != HypervisorType.Any) {
                 throw new InvalidParameterValueException(
-                        "zone wide storage pool is not suported for hypervisor type " + hypervisor);
+                        "zone wide storage pool is not supported for hypervisor type " + hypervisor);
             }
         }
 
@@ -734,6 +735,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
         params.put("name", cmd.getStoragePoolName());
         params.put("details", details);
         params.put("providerName", storeProvider.getName());
+        params.put("managed", cmd.isManaged());
+        params.put("capacityBytes", cmd.getCapacityBytes());
+        params.put("capacityIops", cmd.getCapacityIops());
 
         DataStoreLifeCycle lifeCycle = storeProvider.getDataStoreLifeCycle();
         DataStore store = null;
@@ -1561,7 +1565,41 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
     }
 
     @Override
-    public boolean storagePoolHasEnoughSpace(List<Volume> volumes, StoragePool pool) {
+    public boolean storagePoolHasEnoughIops(List<Volume> requestedVolumes,
+            StoragePool pool) {
+        if (requestedVolumes == null || requestedVolumes.isEmpty() || pool == null)
+            return false;
+
+        long currentIops = 0;
+
+        List<VolumeVO> volumesInPool = _volumeDao.findByPoolId(pool.getId(), null);
+
+        for (VolumeVO volumeInPool : volumesInPool) {
+            Long minIops = volumeInPool.getMinIops();
+
+            if (minIops != null && minIops > 0) {
+                currentIops += minIops;
+            }
+        }
+
+        long requestedIops = 0;
+
+        for (Volume requestedVolume : requestedVolumes) {
+            Long minIops = requestedVolume.getMinIops();
+
+            if (minIops != null && minIops > 0) {
+                requestedIops += minIops;
+            }
+        }
+
+        long futureIops = currentIops + requestedIops;
+
+        return futureIops <= pool.getCapacityIops();
+    }
+
+    @Override
+    public boolean storagePoolHasEnoughSpace(List<Volume> volumes,
+            StoragePool pool) {
         if (volumes == null || volumes.isEmpty())
             return false;
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/storage/VolumeManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeManager.java b/server/src/com/cloud/storage/VolumeManager.java
index c84bb67..2e44a3c 100644
--- a/server/src/com/cloud/storage/VolumeManager.java
+++ b/server/src/com/cloud/storage/VolumeManager.java
@@ -45,7 +45,6 @@ import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
 
 public interface VolumeManager extends VolumeApiService {
-
     VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId,
             Long destPoolClusterId, HypervisorType dataDiskHyperType)
             throws ConcurrentOperationException;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/storage/VolumeManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java
index 4e7b335..a293da5 100644
--- a/server/src/com/cloud/storage/VolumeManagerImpl.java
+++ b/server/src/com/cloud/storage/VolumeManagerImpl.java
@@ -55,6 +55,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
@@ -65,6 +66,7 @@ import org.apache.cloudstack.storage.command.AttachCommand;
 import org.apache.cloudstack.storage.command.CommandResult;
 import org.apache.cloudstack.storage.command.DettachCommand;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
@@ -227,6 +229,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
     @Inject
     protected StoragePoolHostDao _storagePoolHostDao;
     @Inject
+    StoragePoolDetailsDao storagePoolDetailsDao;
+    @Inject
     protected AlertManager _alertMgr;
     @Inject
     protected TemplateDataStoreDao _vmTemplateStoreDao = null;
@@ -507,7 +511,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         VolumeVO newVol = new VolumeVO(oldVol.getVolumeType(),
                 oldVol.getName(), oldVol.getDataCenterId(),
                 oldVol.getDomainId(), oldVol.getAccountId(),
-                oldVol.getDiskOfferingId(), oldVol.getSize());
+                oldVol.getDiskOfferingId(), oldVol.getSize(),
+                oldVol.getMinIops(), oldVol.getMaxIops(), oldVol.get_iScsiName());
         if (templateId != null) {
             newVol.setTemplateId(templateId);
         } else {
@@ -680,9 +685,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         pool = storageMgr.findStoragePool(dskCh, dc, pod, clusterId, vm.getHostId(),
                 vm, avoidPools);
         if (pool == null) {
-            s_logger.warn("Unable to find storage poll when create volume "
+            s_logger.warn("Unable to find storage pool when create volume "
                     + volume.getName());
-            throw new CloudRuntimeException("Unable to find storage poll when create volume" + volume.getName());
+            throw new CloudRuntimeException("Unable to find storage pool when create volume" + volume.getName());
         }
 
         if (s_logger.isDebugEnabled()) {
@@ -731,8 +736,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         Transaction txn = Transaction.currentTxn();
         txn.start();
 
-        VolumeVO volume = new VolumeVO(volumeName, zoneId, -1L, -1L, -1,
-                new Long(-1), null, null, 0, Volume.Type.DATADISK);
+        VolumeVO volume = new VolumeVO(volumeName, zoneId, -1, -1, -1,
+                new Long(-1), null, null, 0, null, null, null, Volume.Type.DATADISK);
         volume.setPoolId(null);
         volume.setDataCenterId(zoneId);
         volume.setPodId(null);
@@ -835,6 +840,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         Long diskOfferingId = null;
         DiskOfferingVO diskOffering = null;
         Long size = null;
+        Long minIops = null;
+        Long maxIops = null;
         // Volume VO used for extracting the source template id
         VolumeVO parentVolume = null;
 
@@ -896,6 +903,37 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
                 size = diskOffering.getDiskSize();
             }
 
+            Boolean isCustomizedIops = diskOffering.isCustomizedIops();
+
+            if (isCustomizedIops != null) {
+                if (isCustomizedIops) {
+                	minIops = cmd.getMinIops();
+                	maxIops = cmd.getMaxIops();
+
+                	if (minIops == null && maxIops == null) {
+                	    minIops = 0L;
+                	    maxIops = 0L;
+                	}
+                	else {
+                        if (minIops == null || minIops <= 0) {
+                            throw new InvalidParameterValueException("The min IOPS must be greater than 0.");
+                        }
+
+                    	if (maxIops == null) {
+            	        	maxIops = 0L;
+            	        }
+
+                    	if (minIops > maxIops) {
+                    		throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS.");
+                    	}
+                	}
+                }
+                else {
+                    minIops = diskOffering.getMinIops();
+                    maxIops = diskOffering.getMaxIops();
+                }
+            }
+
             if (!validateVolumeSizeRange(size)) {// convert size from mb to gb
                                                  // for validation
                 throw new InvalidParameterValueException(
@@ -970,8 +1008,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         Transaction txn = Transaction.currentTxn();
         txn.start();
 
-        VolumeVO volume = new VolumeVO(userSpecifiedName, -1L, -1L, -1, -1,
-                new Long(-1), null, null, 0, Volume.Type.DATADISK);
+        VolumeVO volume = new VolumeVO(userSpecifiedName, -1, -1, -1, -1,
+                new Long(-1), null, null, 0, null, null, null, Volume.Type.DATADISK);
         volume.setPoolId(null);
         volume.setDataCenterId(zoneId);
         volume.setPodId(null);
@@ -980,6 +1018,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
                 .getDomainId()));
         volume.setDiskOfferingId(diskOfferingId);
         volume.setSize(size);
+        volume.setMinIops(minIops);
+        volume.setMaxIops(maxIops);
         volume.setInstanceId(null);
         volume.setUpdated(new Date());
         volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller
@@ -1171,7 +1211,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
 
         UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
 
-        PrimaryDataStoreInfo pool = (PrimaryDataStoreInfo)dataStoreMgr.getDataStore(volume.getPoolId(), DataStoreRole.Primary);
         long currentSize = volume.getSize();
 
         /*
@@ -1358,7 +1397,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
             size = (size * 1024 * 1024 * 1024);
         }
         VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(),
-                owner.getDomainId(), owner.getId(), offering.getId(), size);
+                owner.getDomainId(), owner.getId(), offering.getId(), size,
+                offering.getMinIops(), offering.getMaxIops(), null);
         if (vm != null) {
             vol.setInstanceId(vm.getId());
         }
@@ -1398,7 +1438,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         Long size = _tmpltMgr.getTemplateSize(template.getId(), vm.getDataCenterId());
 
         VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(),
-                owner.getDomainId(), owner.getId(), offering.getId(), size);
+                owner.getDomainId(), owner.getId(), offering.getId(), size,
+                offering.getMinIops(), offering.getMaxIops(), null);
         vol.setFormat(this.getSupportedImageFormatForCluster(template.getHypervisorType()));
         if (vm != null) {
             vol.setInstanceId(vm.getId());
@@ -1542,8 +1583,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         return !storeForRootStoreScope.isSameScope(storeForDataStoreScope);
     }
 
-    private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volume, Long deviceId) {
-        String errorMsg = "Failed to attach volume: " + volume.getName()
+    private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volumeToAttach, Long deviceId) {
+        String errorMsg = "Failed to attach volume: " + volumeToAttach.getName()
                 + " to VM: " + vm.getHostName();
         boolean sendCommand = (vm.getState() == State.Running);
         AttachAnswer answer = null;
@@ -1557,12 +1598,37 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
             }
         }
 
+        StoragePoolVO volumeToAttachStoragePool = null;
+
         if (sendCommand) {
-            DataTO volTO = volFactory.getVolume(volume.getId()).getTO();
-            DiskTO disk = new DiskTO(volTO, deviceId, volume.getVolumeType());
+            volumeToAttachStoragePool = _storagePoolDao.findById(volumeToAttach.getPoolId());
+            long storagePoolId = volumeToAttachStoragePool.getId();
+
+            DataTO volTO = volFactory.getVolume(volumeToAttach.getId()).getTO();
+            DiskTO disk = new DiskTO(volTO, deviceId, null, volumeToAttach.getVolumeType());
+
             AttachCommand cmd = new AttachCommand(disk, vm.getInstanceName());
+
+            cmd.setManaged(volumeToAttachStoragePool.isManaged());
+
+            cmd.setStorageHost(volumeToAttachStoragePool.getHostAddress());
+            cmd.setStoragePort(volumeToAttachStoragePool.getPort());
+
+            cmd.set_iScsiName(volumeToAttach.get_iScsiName());
+
+            VolumeInfo volumeInfo = volFactory.getVolume(volumeToAttach.getId());
+            DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
+            ChapInfo chapInfo = volService.getChapInfo(volumeInfo, dataStore);
+
+            if (chapInfo != null) {
+                cmd.setChapInitiatorUsername(chapInfo.getInitiatorUsername());
+                cmd.setChapInitiatorPassword(chapInfo.getInitiatorSecret());
+                cmd.setChapTargetUsername(chapInfo.getTargetUsername());
+                cmd.setChapTargetPassword(chapInfo.getTargetSecret());
+            }
+
             try {
-                answer = (AttachAnswer) _agentMgr.send(hostId, cmd);
+                answer = (AttachAnswer)_agentMgr.send(hostId, cmd);
             } catch (Exception e) {
                 throw new CloudRuntimeException(errorMsg + " due to: "
                         + e.getMessage());
@@ -1573,19 +1639,29 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
             // Mark the volume as attached
             if (sendCommand) {
                 DiskTO disk = answer.getDisk();
-                _volsDao.attachVolume(volume.getId(), vm.getId(),
+                _volsDao.attachVolume(volumeToAttach.getId(), vm.getId(),
                         disk.getDiskSeq());
+
+                volumeToAttach = _volsDao.findById(volumeToAttach.getId());
+
+                if (volumeToAttachStoragePool.isManaged() &&
+                	volumeToAttach.getPath() == null) {
+                	volumeToAttach.setPath(answer.getDisk().getVdiUuid());
+
+                	_volsDao.update(volumeToAttach.getId(), volumeToAttach);
+                }
             } else {
-                _volsDao.attachVolume(volume.getId(), vm.getId(), deviceId);
+                _volsDao.attachVolume(volumeToAttach.getId(), vm.getId(), deviceId);
             }
+
             // insert record for disk I/O statistics
-            VmDiskStatisticsVO diskstats = _vmDiskStatsDao.findBy(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volume.getId());
+            VmDiskStatisticsVO diskstats = _vmDiskStatsDao.findBy(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volumeToAttach.getId());
             if (diskstats == null) {
-               diskstats = new VmDiskStatisticsVO(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volume.getId());
+               diskstats = new VmDiskStatisticsVO(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volumeToAttach.getId());
                _vmDiskStatsDao.persist(diskstats);
             }
 
-            return _volsDao.findById(volume.getId());
+            return _volsDao.findById(volumeToAttach.getId());
         } else {
             if (answer != null) {
                 String details = answer.getDetails();
@@ -1912,9 +1988,17 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         Answer answer = null;
 
         if (sendCommand) {
+            StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId());
+
             DataTO volTO = volFactory.getVolume(volume.getId()).getTO();
-            DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), volume.getVolumeType());
+            DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), null, volume.getVolumeType());
+
             DettachCommand cmd = new DettachCommand(disk, vm.getInstanceName());
+
+            cmd.setManaged(volumePool.isManaged());
+
+            cmd.set_iScsiName(volume.get_iScsiName());
+
             try {
                 answer = _agentMgr.send(vm.getHostId(), cmd);
             } catch (Exception e) {
@@ -1926,6 +2010,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         if (!sendCommand || (answer != null && answer.getResult())) {
             // Mark the volume as detached
             _volsDao.detachVolume(volume.getId());
+
             return _volsDao.findById(volumeId);
         } else {
 
@@ -1940,11 +2025,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         }
     }
 
-
-
-
-
-
     @DB
     protected VolumeVO switchVolume(VolumeVO existingVolume,
             VirtualMachineProfile<? extends VirtualMachine> vm)
@@ -2232,7 +2312,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
 
         for (VolumeVO vol : vols) {
             DataTO volTO = volFactory.getVolume(vol.getId()).getTO();
-            DiskTO disk = new DiskTO(volTO, vol.getDeviceId(), vol.getVolumeType());
+            DiskTO disk = new DiskTO(volTO, vol.getDeviceId(), null, vol.getVolumeType());
             vm.addDisk(disk);
         }
 
@@ -2240,7 +2320,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
             UserVmVO userVM = (UserVmVO) vm.getVirtualMachine();
             if (userVM.getIsoId() != null) {
                 DataTO dataTO = tmplFactory.getTemplate(userVM.getIsoId(), DataStoreRole.Image, userVM.getDataCenterId()).getTO();
-                DiskTO iso = new DiskTO(dataTO, 3L, Volume.Type.ISO);
+                DiskTO iso = new DiskTO(dataTO, 3L, null, Volume.Type.ISO);
                 vm.addDisk(iso);
             }
         }
@@ -2458,7 +2538,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
                 vol = result.first();
             }
             DataTO volumeTO = volFactory.getVolume(vol.getId()).getTO();
-            DiskTO disk = new DiskTO(volumeTO, vol.getDeviceId(), vol.getVolumeType());
+            DiskTO disk = new DiskTO(volumeTO, vol.getDeviceId(), null, vol.getVolumeType());
             vm.addDisk(disk);
         }
     }
@@ -2745,7 +2825,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
 
     @Override
     public String getVmNameFromVolumeId(long volumeId) {
-        Long instanceId;
         VolumeVO volume = _volsDao.findById(volumeId);
         return getVmNameOnVolume(volume);
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/src/com/cloud/template/TemplateManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java
index f70d44d..ca644af 100755
--- a/server/src/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/com/cloud/template/TemplateManagerImpl.java
@@ -1023,7 +1023,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
         }
 
         DataTO isoTO = tmplt.getTO();
-        DiskTO disk = new DiskTO(isoTO, null, Volume.Type.ISO);
+        DiskTO disk = new DiskTO(isoTO, null, null, Volume.Type.ISO);
         Command cmd = null;
         if (attach) {
             cmd = new AttachCommand(disk, vmName);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 ef0259d..63f77b6 100755
--- a/server/src/com/cloud/test/DatabaseConfig.java
+++ b/server/src/com/cloud/test/DatabaseConfig.java
@@ -979,7 +979,7 @@ public class DatabaseConfig {
             newTags.delete(newTags.length() - 1, newTags.length());
             tags = newTags.toString();
         }
-        DiskOfferingVO diskOffering = new DiskOfferingVO(domainId, name, displayText, diskSpace , tags, false);
+        DiskOfferingVO diskOffering = new DiskOfferingVO(domainId, name, displayText, diskSpace, tags, false, null, null, null);
         diskOffering.setUseLocalStorage(local);
 
         Long bytesReadRate = Long.parseLong(_currentObjectParams.get("bytesReadRate"));

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 3cef182..a59fa5b 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -2910,12 +2910,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 
             TemplateObjectTO iso = (TemplateObjectTO)template.getTO();
             iso.setGuestOsType(displayName);
-            DiskTO disk = new DiskTO(iso, 3L, Volume.Type.ISO);
+            DiskTO disk = new DiskTO(iso, 3L, null, Volume.Type.ISO);
             profile.addDisk(disk);
         } else {
             TemplateObjectTO iso = new TemplateObjectTO();
             iso.setFormat(ImageFormat.ISO);
-            DiskTO disk = new DiskTO(iso, 3L, Volume.Type.ISO);
+            DiskTO disk = new DiskTO(iso, 3L, null, Volume.Type.ISO);
             profile.addDisk(disk);
         }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
index 95230a5..7a61978 100755
--- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
@@ -655,8 +655,9 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
      * @see com.cloud.configuration.ConfigurationManager#createDiskOffering(java.lang.Long, java.lang.String, java.lang.String, java.lang.Long, java.lang.String, boolean, boolean, boolean)
      */
     @Override
-    public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled,
-            Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) {
+    public 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) {
         // TODO Auto-generated method stub
         return null;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/setup/db/db/schema-410to420.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql
index 2d0f8de..0c1d753 100644
--- a/setup/db/db/schema-410to420.sql
+++ b/setup/db/db/schema-410to420.sql
@@ -429,6 +429,20 @@ ALTER TABLE `cloud`.`nics` ADD COLUMN `display_nic` tinyint(1) NOT NULL DEFAULT
 
 ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `display_offering` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should disk offering be displayed to the end user';
 
+ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `customized_iops` tinyint(1) unsigned COMMENT 'Should customized IOPS be displayed to the end user';
+
+ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `min_iops` bigint(20) unsigned COMMENT 'Minimum IOPS';
+
+ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `max_iops` bigint(20) unsigned COMMENT 'Maximum IOPS';
+
+ALTER TABLE `cloud`.`volumes` ADD COLUMN `min_iops` bigint(20) unsigned COMMENT 'Minimum IOPS';
+
+ALTER TABLE `cloud`.`volumes` ADD COLUMN `max_iops` bigint(20) unsigned COMMENT 'Maximum IOPS';
+
+ALTER TABLE `cloud`.`storage_pool` ADD COLUMN `managed` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Should CloudStack manage this storage';
+
+ALTER TABLE `cloud`.`storage_pool` ADD COLUMN `capacity_iops` bigint(20) unsigned DEFAULT NULL COMMENT 'IOPS CloudStack can provision from this storage pool';
+
 ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `bytes_read_rate` bigint(20);
 
 ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `bytes_write_rate` bigint(20);
@@ -871,6 +885,8 @@ CREATE VIEW `cloud`.`volume_view` AS
         volumes.device_id,
         volumes.volume_type,
         volumes.size,
+        volumes.min_iops,
+        volumes.max_iops,
         volumes.created,
         volumes.state,
         volumes.attached,
@@ -981,6 +997,7 @@ CREATE VIEW `cloud`.`storage_pool_view` AS
         storage_pool.created,
         storage_pool.removed,
         storage_pool.capacity_bytes,
+        storage_pool.capacity_iops,
         storage_pool.scope,
         storage_pool.hypervisor,
         cluster.id cluster_id,
@@ -1521,9 +1538,12 @@ CREATE VIEW `cloud`.`disk_offering_view` AS
         disk_offering.name,
         disk_offering.display_text,
         disk_offering.disk_size,
+        disk_offering.min_iops,
+        disk_offering.max_iops,
         disk_offering.created,
         disk_offering.tags,
         disk_offering.customized,
+        disk_offering.customized_iops,
         disk_offering.removed,
         disk_offering.use_local_storage,
         disk_offering.system_use,
@@ -1736,6 +1756,8 @@ CREATE VIEW `cloud`.`volume_view` AS
         volumes.device_id,
         volumes.volume_type,
         volumes.size,
+        volumes.min_iops,
+        volumes.max_iops,
         volumes.created,
         volumes.state,
         volumes.attached,

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/tools/marvin/marvin/cloudstackConnection.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/cloudstackConnection.py b/tools/marvin/marvin/cloudstackConnection.py
index 9d60ff9..8129396 100644
--- a/tools/marvin/marvin/cloudstackConnection.py
+++ b/tools/marvin/marvin/cloudstackConnection.py
@@ -203,7 +203,7 @@ class cloudConnection(object):
                             i = i + 1
         return cmdname, isAsync, requests
 
-    def marvin_request(self, cmd, response_type=None, method='GET'):
+    def marvin_request(self, cmd, response_type=None, method='GET', data=''):
         """
         Requester for marvin command objects
         @param cmd: marvin's command from cloudstackAPI

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/ui/dictionary.jsp
----------------------------------------------------------------------
diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp
index 7809cdb..af64228 100644
--- a/ui/dictionary.jsp
+++ b/ui/dictionary.jsp
@@ -25,6 +25,9 @@ under the License.
 <% long now = System.currentTimeMillis(); %>
 <script language="javascript">
 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.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" />',
@@ -472,6 +475,7 @@ dictionary = {
 'label.disable.vpn': '<fmt:message key="label.disable.vpn" />',
 'label.disabling.vpn.access': '<fmt:message key="label.disabling.vpn.access" />',
 'label.disk.allocated': '<fmt:message key="label.disk.allocated" />',
+'label.disk.iops.total': '<fmt:message key="label.disk.iops.total" />',
 'label.disk.bytes.read.rate': '<fmt:message key="label.disk.bytes.read.rate" />',
 'label.disk.bytes.write.rate': '<fmt:message key="label.disk.bytes.write.rate" />',
 'label.disk.iops.write.rate': '<fmt:message key="label.disk.iops.write.rate" />',
@@ -1029,6 +1033,7 @@ dictionary = {
 'label.storage': '<fmt:message key="label.storage" />',
 'label.storage.tags': '<fmt:message key="label.storage.tags" />',
 'label.storage.type': '<fmt:message key="label.storage.type" />',
+'label.qos.type': '<fmt:message key="label.qos.type" />',
 'label.subdomain.access': '<fmt:message key="label.subdomain.access" />',
 'label.submit': '<fmt:message key="label.submit" />',
 'label.submitted.by': '<fmt:message key="label.submitted.by" />',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/ui/scripts/configuration.js
----------------------------------------------------------------------
diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js
index 7f0e1a5..ab70c3d 100644
--- a/ui/scripts/configuration.js
+++ b/ui/scripts/configuration.js
@@ -1015,6 +1015,86 @@
                     dependsOn: 'isCustomized',
                     validation: { required: true, number: true }
                   },
+                  qosType: {
+                    label: 'label.qos.type',
+                    docID: 'helpDiskOfferingQoSType',
+                    select: function(args) {
+                      var items = [];
+                      items.push({id: '', description: ''});
+                      items.push({id: 'hypervisor', description: 'hypervisor'});
+                      items.push({id: 'storage', description: 'storage'});
+                      args.response.success({data: items});
+                      
+                      args.$select.change(function() {
+                      	var $form = $(this).closest('form');
+                        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 $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]');
+                        var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]');
+                        var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]');
+                        var $diskIopsWriteRate = $form.find('.form-item[rel=diskIopsWriteRate]');
+                        
+                        var qosId = $(this).val();
+                        
+                        if (qosId == 'storage') { // Storage QoS
+                          $diskBytesReadRate.hide();
+                          $diskBytesWriteRate.hide();
+                          $diskIopsReadRate.hide();
+                          $diskIopsWriteRate.hide();
+                          
+                          $isCustomizedIops.css('display', 'inline-block');
+
+                          if ($isCustomizedIops == true) {
+                            $minIops.css('display', 'inline-block');
+                            $maxIops.css('display', 'inline-block');
+                          }
+                          else {
+                            $minIops.hide();
+                            $maxIops.hide();
+                          }
+                        }
+                        else if (qosId == 'hypervisor') { // Hypervisor Qos
+                          $isCustomizedIops.hide();
+                          $minIops.hide();
+                          $maxIops.hide();
+                          
+                          $diskBytesReadRate.css('display', 'inline-block');
+                          $diskBytesWriteRate.css('display', 'inline-block');
+                          $diskIopsReadRate.css('display', 'inline-block');
+                          $diskIopsWriteRate.css('display', 'inline-block');
+                        }
+                        else { // No Qos
+                          $diskBytesReadRate.hide();
+                          $diskBytesWriteRate.hide();
+                          $diskIopsReadRate.hide();
+                          $diskIopsWriteRate.hide();
+                          $isCustomizedIops.hide();
+                          $minIops.hide();
+                          $maxIops.hide();
+                        }
+                      });
+                    }
+                  },
+                  isCustomizedIops: {
+                    label: 'label.custom.disk.iops',
+                    docID: 'helpDiskOfferingCustomDiskIops',
+                    isBoolean: true,
+                    isReverse: true,
+                    isChecked: false
+                  },
+                  minIops: {
+                    label: 'label.disk.iops.min',
+                    docID: 'helpDiskOfferingDiskIopsMin',
+                    dependsOn: 'isCustomizedIops',
+                    validation: { required: false, number: true }
+                  },
+                  maxIops: {
+                    label: 'label.disk.iops.max',
+                    docID: 'helpDiskOfferingDiskIopsMax',
+                    dependsOn: 'isCustomizedIops',
+                    validation: { required: false, number: true }
+                  },
                   diskBytesReadRate: {
                       label: 'label.disk.bytes.read.rate',
                       validation: {
@@ -1080,18 +1160,65 @@
 
               action: function(args) {
                 var data = {
-								  isMirrored: false,
+								    isMirrored: false,
 									name: args.data.name,
 									displaytext: args.data.description,
 									storageType: args.data.storageType,
 									customized: (args.data.isCustomized=="on")
-								};																
-               
+								};
+               	
                 if(args.$form.find('.form-item[rel=disksize]').css("display") != "none") {
 								  $.extend(data, {
 									  disksize: args.data.disksize
-									});		
-								}
+									});
+				}
+				
+				if (args.data.qosType == 'storage') {
+					var customIops = args.data.isCustomizedIops == "on";
+					
+					$.extend(data, {
+						customizediops: customIops
+					});
+					
+					if (!customIops) {
+				   	   if (args.data.minIops != null && args.data.minIops.length > 0) {
+					   	   $.extend(data, {
+							   miniops: args.data.minIops
+						   });
+						}
+
+						if(args.data.maxIops != null && args.data.maxIops.length > 0) {
+					   	   $.extend(data, {
+					       	   maxiops: args.data.maxIops
+					   	   });
+					   	}
+					}
+				}
+				else if (args.data.qosType == 'hypervisor') {
+					if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
+                        $.extend(data, {
+                            bytesreadrate: args.data.diskBytesReadRate
+                        });
+                    }
+                    
+                	if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) {
+                        $.extend(data, {
+                            byteswriterate: args.data.diskBytesWriteRate
+                        });
+                    }
+                
+                	if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) {
+                        $.extend(data, {
+                            iopsreadrate: args.data.diskIopsReadRate
+                        });
+                    }
+                
+                	if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) {
+                        $.extend(data, {
+                            iopswriterate: args.data.diskIopsWriteRate
+                        });
+                    }
+				}
 
                 if(args.data.tags != null && args.data.tags.length > 0) {
 								  $.extend(data, {
@@ -1104,26 +1231,6 @@
 									  domainid: args.data.domainId
 									});		
 								}
-                if(args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
-                                                                  $.extend(data, {
-                                                                          bytesreadrate: args.data.diskBytesReadRate
-                                                                        });
-                                                                }
-                if(args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) {
-                                                                  $.extend(data, {
-                                                                          byteswriterate: args.data.diskBytesWriteRate
-                                                                        });
-                                                                }
-                if(args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) {
-                                                                  $.extend(data, {
-                                                                          iopsreadrate: args.data.diskIopsReadRate
-                                                                        });
-                                                                }
-                if(args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) {
-                                                                  $.extend(data, {
-                                                                          iopswriterate: args.data.diskIopsWriteRate
-                                                                        });
-                                                                }
 
                 $.ajax({
                   url: createURL('createDiskOffering'),
@@ -1236,6 +1343,28 @@
                           return "N/A";
                       }
                     },
+                    iscustomizediops: {
+                      label: 'label.custom.disk.iops',
+                      converter: cloudStack.converters.toBooleanText
+                    },
+                    miniops: {
+                      label: 'label.disk.iops.min',
+                      converter: function(args) {
+                        if(args > 0)
+                          return args;
+                        else
+                          return "N/A";
+                      }
+                    },
+                    maxiops: {
+                      label: 'label.disk.iops.max',
+                      converter: function(args) {
+                        if(args > 0)
+                          return args;
+                        else
+                          return "N/A";
+                      }
+                    },
                     diskBytesReadRate: { label: 'label.disk.bytes.write.rate' },
                     diskBytesWriteRate: { label: 'label.disk.bytes.write.rate' },
                     diskIopsReadRate: { label: 'label.disk.iops.write.rate' },

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/ui/scripts/docs.js
----------------------------------------------------------------------
diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js
index 5aa352a..635e619 100755
--- a/ui/scripts/docs.js
+++ b/ui/scripts/docs.js
@@ -270,6 +270,10 @@ cloudStack.docs = {
   desc: 'Type of disk for the VM. Local is attached to the hypervisor host where the VM is running. Shared is storage accessible via NFS.',
   externalLink: ''
   },
+  helpDiskOfferingQoSType: {
+  desc: 'Type of Quality of Service desired, if any.',
+  externalLink: ''
+  },
   helpDiskOfferingCustomDiskSize: {
   desc: 'If checked, the user can set their own disk size. If not checked, the root administrator must define a value in Disk Size.',
   externalLink: ''
@@ -278,6 +282,18 @@ cloudStack.docs = {
   desc: 'Appears only if Custom Disk Size is not selected. Define the volume size in GB.',
   externalLink: ''
   },
+  helpDiskOfferingCustomDiskIops: {
+  desc: 'If checked, the user can set Min and Max IOPS. If not checked, the root administrator can define such values.',
+  externalLink: ''
+  },
+  helpDiskOfferingDiskIopsMin: {
+  desc: 'Appears only if Custom IOPS is not selected. Define the minimum volume IOPS.',
+  externalLink: ''
+  },
+  helpDiskOfferingDiskIopsMax: {
+  desc: 'Appears only if Custom IOPS is not selected. Define the maximum volume IOPS.',
+  externalLink: ''
+  },
   helpDiskOfferingStorageTags: {
   desc: 'Comma-separated list of attributes that should be associated with the primary storage for this disk. For example "ssd,blue".',
   externalLink: ''


[3/4] SolidFire plug-in and related changes

Posted by mt...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
index 5c51585..52f4190 100755
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
@@ -36,6 +36,9 @@ import java.util.Map;
 import java.util.Random;
 import java.util.TimeZone;
 import java.util.UUID;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Map.Entry;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
@@ -216,6 +219,8 @@ import com.cloud.hypervisor.vmware.mo.HostMO;
 import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
 import com.cloud.hypervisor.vmware.mo.NetworkDetails;
 import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
+import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO;
+import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO;
 import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
 import com.cloud.hypervisor.vmware.mo.VirtualSwitchType;
 import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
@@ -229,7 +234,6 @@ import com.cloud.network.HAProxyConfigurator;
 import com.cloud.network.LoadBalancerConfigurator;
 import com.cloud.network.Networks;
 import com.cloud.network.Networks.BroadcastDomainType;
-import com.cloud.network.Networks.IsolationType;
 import com.cloud.network.Networks.TrafficType;
 import com.cloud.network.VmwareTrafficLabel;
 import com.cloud.network.rules.FirewallRule;
@@ -272,8 +276,16 @@ import com.vmware.vim25.GuestInfo;
 import com.vmware.vim25.HostCapability;
 import com.vmware.vim25.HostFirewallInfo;
 import com.vmware.vim25.HostFirewallRuleset;
-import com.vmware.vim25.HostNetworkTrafficShapingPolicy;
-import com.vmware.vim25.HostPortGroupSpec;
+import com.vmware.vim25.HostHostBusAdapter;
+import com.vmware.vim25.HostInternetScsiTargetTransport;
+import com.vmware.vim25.HostScsiTopology;
+import com.vmware.vim25.HostInternetScsiHba;
+import com.vmware.vim25.HostInternetScsiHbaAuthenticationProperties;
+import com.vmware.vim25.HostInternetScsiHbaStaticTarget;
+import com.vmware.vim25.HostScsiDisk;
+import com.vmware.vim25.HostScsiTopologyInterface;
+import com.vmware.vim25.HostScsiTopologyLun;
+import com.vmware.vim25.HostScsiTopologyTarget;
 import com.vmware.vim25.ManagedObjectReference;
 import com.vmware.vim25.ObjectContent;
 import com.vmware.vim25.OptionValue;
@@ -304,10 +316,6 @@ import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator;
 import com.vmware.vim25.VirtualMachineRuntimeInfo;
 import com.vmware.vim25.VirtualSCSISharing;
 
-import java.util.HashSet;
-import java.util.Set;
-import java.util.Map.Entry;
-
 public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService {
     private static final Logger s_logger = Logger.getLogger(VmwareResource.class);
 
@@ -593,7 +601,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
 
         try {
             VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext());
-            ManagedObjectReference morDc = hyperHost.getHyperHostDatacenter();
             // find VM through datacenter (VM is not at the target host yet)
             VirtualMachineMO vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
             if (vmMo == null) {
@@ -3244,7 +3251,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             HashMap<String, State> newStates = getVmStates();
 
             List<String> requestedVmNames = cmd.getVmNames();
-            List<String> vmNames = new ArrayList();
+            List<String> vmNames = new ArrayList<String>();
 
             if (requestedVmNames != null) {
                 for (String vmName : requestedVmNames) {
@@ -3750,8 +3757,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             s_logger.info("Executing resource MigrateVolumeCommand: " + _gson.toJson(cmd));
         }
 
-        VmwareContext context = getServiceContext();
-        VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
         final String vmName = volMgr.getVmNameFromVolumeId(cmd.getVolumeId());
 
         VirtualMachineMO vmMo = null;
@@ -3903,6 +3908,45 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
         }
     }
 
+    protected ManagedObjectReference handleDatastoreAndVmdk(AttachVolumeCommand cmd) throws Exception {
+        ManagedObjectReference morDs = null;
+
+        VmwareContext context = getServiceContext();
+        VmwareHypervisorHost hyperHost = getHyperHost(context);
+
+        String iqn = cmd.get_iScsiName();
+
+        if (cmd.getAttach()) {
+            morDs = createVmfsDatastore(hyperHost, iqn,
+                        cmd.getStorageHost(), cmd.getStoragePort(), iqn,
+                        cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword(),
+                        cmd.getChapTargetUsername(), cmd.getChapTargetPassword());
+
+            DatastoreMO dsMo = new DatastoreMO(context, morDs);
+
+            String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), dsMo.getName());
+
+            if (!dsMo.fileExists(volumeDatastorePath)) {
+                String dummyVmName = getWorkerName(context, cmd, 0);
+
+                VirtualMachineMO vmMo = prepareVolumeHostDummyVm(hyperHost, dsMo, dummyVmName);
+
+                if (vmMo == null) {
+                    throw new Exception("Unable to create a dummy VM for volume creation");
+                }
+
+                vmMo.createDisk(volumeDatastorePath, (int)(dsMo.getSummary().getFreeSpace() / (1024L * 1024L)),
+                		morDs, vmMo.getScsiDeviceControllerKey());
+                vmMo.detachDisk(volumeDatastorePath, false);
+        	}
+        }
+        else {
+        	deleteVmfsDatastore(hyperHost, iqn, cmd.getStorageHost(), cmd.getStoragePort(), iqn);
+        }
+
+    	return morDs;
+    }
+
     protected Answer execute(AttachVolumeCommand cmd) {
         if (s_logger.isInfoEnabled()) {
             s_logger.info("Executing resource AttachVolumeCommand: " + _gson.toJson(cmd));
@@ -3922,7 +3966,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                 throw new Exception(msg);
             }
 
-            ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getPoolUuid());
+            ManagedObjectReference morDs = null;
+
+            if (cmd.getAttach() && cmd.isManaged()) {
+            	morDs = handleDatastoreAndVmdk(cmd);
+            }
+            else {
+            	morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getPoolUuid());
+            }
+
             if (morDs == null) {
                 String msg = "Unable to find the mounted datastore to execute AttachVolumeCommand, vmName: " + cmd.getVmName();
                 s_logger.error(msg);
@@ -3933,12 +3985,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             String datastoreVolumePath = dsMo.searchFileInSubFolders(cmd.getVolumePath() + ".vmdk", true);
             assert (datastoreVolumePath != null) : "Virtual disk file must exist in specified datastore for attach/detach operations.";
 
-            AttachVolumeAnswer answer = new AttachVolumeAnswer(cmd, cmd.getDeviceId());
+            AttachVolumeAnswer answer = new AttachVolumeAnswer(cmd, cmd.getDeviceId(), datastoreVolumePath);
             if (cmd.getAttach()) {
                 vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs);
             } else {
                 vmMo.removeAllSnapshots();
                 vmMo.detachDisk(datastoreVolumePath, false);
+
+                if (cmd.isManaged()) {
+                	handleDatastoreAndVmdk(cmd);
+                }
             }
 
             return answer;
@@ -3954,6 +4010,198 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
         }
     }
 
+    private ManagedObjectReference createVmfsDatastore(VmwareHypervisorHost hyperHost, String datastoreName, String storageIpAddress,
+            int storagePortNumber, String iqn, String chapName, String chapSecret, String mutualChapName, String mutualChapSecret) throws Exception {
+        VmwareContext context = getServiceContext();
+        ManagedObjectReference morCluster = hyperHost.getHyperHostCluster();
+        ClusterMO cluster = new ClusterMO(context, morCluster);
+        List<Pair<ManagedObjectReference, String>> lstHosts = cluster.getClusterHosts();
+
+        HostInternetScsiHbaStaticTarget target = new HostInternetScsiHbaStaticTarget();
+
+        target.setAddress(storageIpAddress);
+        target.setPort(storagePortNumber);
+        target.setIScsiName(iqn);
+
+        HostInternetScsiHbaAuthenticationProperties auth = new HostInternetScsiHbaAuthenticationProperties();
+
+        String strAuthType = "chapRequired";
+
+        auth.setChapAuthEnabled(true);
+        auth.setChapInherited(false);
+        auth.setChapAuthenticationType(strAuthType);
+        auth.setChapName(chapName);
+        auth.setChapSecret(chapSecret);
+        auth.setMutualChapInherited(false);
+        auth.setMutualChapAuthenticationType(strAuthType);
+        auth.setMutualChapName(mutualChapName);
+        auth.setMutualChapSecret(mutualChapSecret);
+
+        target.setAuthenticationProperties(auth);
+
+        final List<HostInternetScsiHbaStaticTarget> lstTargets = new ArrayList<HostInternetScsiHbaStaticTarget>();
+
+        lstTargets.add(target);
+
+        HostDatastoreSystemMO hostDatastoreSystem = null;
+        HostStorageSystemMO hostStorageSystem = null;
+
+        final List<Thread> threads = new ArrayList<Thread>();
+        final List<Exception> exceptions = new ArrayList<Exception>();
+
+        for (Pair<ManagedObjectReference, String> hostPair : lstHosts) {
+            HostMO host = new HostMO(context, hostPair.first());
+            hostDatastoreSystem = host.getHostDatastoreSystemMO();
+            hostStorageSystem = host.getHostStorageSystemMO();
+
+            boolean iScsiHbaConfigured = false;
+
+            for (HostHostBusAdapter hba : hostStorageSystem.getStorageDeviceInfo().getHostBusAdapter()) {
+                if (hba instanceof HostInternetScsiHba) {
+                    // just finding an instance of HostInternetScsiHba means that we have found at least one configured iSCSI HBA
+                    // at least one iSCSI HBA must be configured before a CloudStack user can use this host for iSCSI storage
+                    iScsiHbaConfigured = true;
+
+                    final String iScsiHbaDevice = hba.getDevice();
+
+                    final HostStorageSystemMO hss = hostStorageSystem;
+
+                    threads.add(new Thread() {
+                        public void run() {
+                            try {
+                                hss.addInternetScsiStaticTargets(iScsiHbaDevice, lstTargets);
+
+                                hss.rescanHba(iScsiHbaDevice);
+                            }
+                            catch (Exception ex) {
+                                synchronized (exceptions) {
+                                    exceptions.add(ex);
+                                }
+                            }
+                        }
+                    });
+                }
+            }
+
+            if (!iScsiHbaConfigured) {
+                throw new Exception("An iSCSI HBA must be configured before a host can use iSCSI storage.");
+            }
+        }
+
+        for (Thread thread : threads) {
+            thread.start();
+        }
+
+        for (Thread thread : threads) {
+            thread.join();
+        }
+
+        if (exceptions.size() > 0) {
+            throw new Exception(exceptions.get(0).getMessage());
+        }
+
+        ManagedObjectReference morDs = hostDatastoreSystem.findDatastore(iqn);
+
+        if (morDs != null) {
+            return morDs;
+        }
+
+        List<HostScsiDisk> lstHostScsiDisks = hostDatastoreSystem.queryAvailableDisksForVmfs();
+
+        HostScsiDisk hostScsiDisk = getHostScsiDisk(hostStorageSystem.getStorageDeviceInfo().getScsiTopology(), lstHostScsiDisks, iqn);
+
+        if (hostScsiDisk == null) {
+            throw new Exception("A relevant SCSI disk could not be located to use to create a datastore.");
+        }
+
+        return hostDatastoreSystem.createVmfsDatastore(datastoreName, hostScsiDisk);
+    }
+
+    // the purpose of this method is to find the HostScsiDisk in the passed-in array that exists (if any) because
+    // we added the static iqn to an iSCSI HBA
+    private static HostScsiDisk getHostScsiDisk(HostScsiTopology hst, List<HostScsiDisk> lstHostScsiDisks, String iqn) {
+        for (HostScsiTopologyInterface adapter : hst.getAdapter()) {
+            if (adapter.getTarget() != null) {
+                for (HostScsiTopologyTarget target : adapter.getTarget()) {
+                    if (target.getTransport() instanceof HostInternetScsiTargetTransport) {
+                        String iScsiName = ((HostInternetScsiTargetTransport)target.getTransport()).getIScsiName();
+
+                        if (iqn.equals(iScsiName)) {
+                            for (HostScsiDisk hostScsiDisk : lstHostScsiDisks) {
+                                for (HostScsiTopologyLun hstl : target.getLun()) {
+                                    if (hstl.getScsiLun().contains(hostScsiDisk.getUuid())) {
+                                        return hostScsiDisk;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private void deleteVmfsDatastore(VmwareHypervisorHost hyperHost, String volumeUuid,
+            String storageIpAddress, int storagePortNumber, String iqn) throws Exception {
+        // hyperHost.unmountDatastore(volumeUuid);
+
+        VmwareContext context = getServiceContext();
+        ManagedObjectReference morCluster = hyperHost.getHyperHostCluster();
+        ClusterMO cluster = new ClusterMO(context, morCluster);
+        List<Pair<ManagedObjectReference, String>> lstHosts = cluster.getClusterHosts();
+
+        HostInternetScsiHbaStaticTarget target = new HostInternetScsiHbaStaticTarget();
+
+        target.setAddress(storageIpAddress);
+        target.setPort(storagePortNumber);
+        target.setIScsiName(iqn);
+
+        final List<HostInternetScsiHbaStaticTarget> lstTargets = new ArrayList<HostInternetScsiHbaStaticTarget>();
+
+        lstTargets.add(target);
+
+        final List<Thread> threads = new ArrayList<Thread>();
+        final List<Exception> exceptions = new ArrayList<Exception>();
+
+        for (Pair<ManagedObjectReference, String> hostPair : lstHosts) {
+            final HostMO host = new HostMO(context, hostPair.first());
+            final HostStorageSystemMO hostStorageSystem = host.getHostStorageSystemMO();
+
+            for (HostHostBusAdapter hba : hostStorageSystem.getStorageDeviceInfo().getHostBusAdapter()) {
+                if (hba instanceof HostInternetScsiHba) {
+                    final String iScsiHbaDevice = hba.getDevice();
+
+                    Thread thread = new Thread() {
+                        public void run() {
+                            try {
+                                hostStorageSystem.removeInternetScsiStaticTargets(iScsiHbaDevice, lstTargets);
+
+                                hostStorageSystem.rescanHba(iScsiHbaDevice);
+                            }
+                            catch (Exception ex) {
+                                exceptions.add(ex);
+                            }
+                        }
+                    };
+
+                    threads.add(thread);
+
+                    thread.start();
+                }
+            }
+        }
+
+        for (Thread thread : threads) {
+            thread.join();
+        }
+
+        if (exceptions.size() > 0) {
+            throw new Exception(exceptions.get(0).getMessage());
+        }
+    }
+
     protected Answer execute(AttachIsoCommand cmd) {
         if (s_logger.isInfoEnabled()) {
             s_logger.info("Executing resource AttachIsoCommand: " + _gson.toJson(cmd));

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
index cbbec7c..d9c357d 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
@@ -4172,11 +4172,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
                             for (VIF vif : vifs) {
                                 networks.add(vif.getNetwork(conn));
                             }
-                            List<VDI> vdis = getVdis(conn, vm);
                             vm.destroy(conn);
-                            for( VDI vdi : vdis ){
-                                umount(conn, vdi);
-                            }
                             state = State.Stopped;
                             SR sr = getISOSRbyVmName(conn, cmd.getVmName());
                             removeSR(conn, sr);
@@ -4479,7 +4475,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
         throw new CloudRuntimeException("Could not find available VIF slot in VM with name: " + vmName);
     }
 
-    protected VDI mount(Connection conn, StoragePoolType pooltype, String volumeFolder, String volumePath) {
+    protected VDI mount(Connection conn, StoragePoolType poolType, String volumeFolder, String volumePath) {
         return getVDIbyUuid(conn, volumePath);
     }
 
@@ -5549,7 +5545,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
             if (pool.getType() == StoragePoolType.NetworkFilesystem) {
                 getNfsSR(conn, pool);
             } else if (pool.getType() == StoragePoolType.IscsiLUN) {
-                getIscsiSR(conn, pool);
+                getIscsiSR(conn, pool.getUuid(), pool.getHost(), pool.getPath(), null, null, new Boolean[1]);
             } else if (pool.getType() == StoragePoolType.PreSetup) {
             } else {
                 return new Answer(cmd, false, "The pool type: " + pool.getType().name() + " is not supported.");
@@ -6229,7 +6225,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
 
     public Answer execute(ResizeVolumeCommand cmd) {
         Connection conn = getConnection();
-        StorageFilerTO pool = cmd.getPool();
         String volid = cmd.getPath();
         long newSize = cmd.getNewSize();
 
@@ -6367,19 +6362,18 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
         }
     }
 
-    protected SR getIscsiSR(Connection conn, StorageFilerTO pool) {
-        synchronized (pool.getUuid().intern()) {
+    protected SR getIscsiSR(Connection conn, String srNameLabel, String target, String path,
+    		String chapInitiatorUsername, String chapInitiatorPassword, Boolean[] created) {
+        synchronized (srNameLabel.intern()) {
             Map<String, String> deviceConfig = new HashMap<String, String>();
             try {
-                String target = pool.getHost();
-                String path = pool.getPath();
                 if (path.endsWith("/")) {
                     path = path.substring(0, path.length() - 1);
                 }
 
                 String tmp[] = path.split("/");
                 if (tmp.length != 3) {
-                    String msg = "Wrong iscsi path " + pool.getPath() + " it should be /targetIQN/LUN";
+                    String msg = "Wrong iscsi path " + path + " it should be /targetIQN/LUN";
                     s_logger.warn(msg);
                     throw new CloudRuntimeException(msg);
                 }
@@ -6387,7 +6381,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
                 String lunid = tmp[2].trim();
                 String scsiid = "";
 
-                Set<SR> srs = SR.getByNameLabel(conn, pool.getUuid());
+                Set<SR> srs = SR.getByNameLabel(conn, srNameLabel);
                 for (SR sr : srs) {
                     if (!SRType.LVMOISCSI.equals(sr.getType(conn))) {
                         continue;
@@ -6412,19 +6406,24 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
                     }
                     if (target.equals(dc.get("target")) && targetiqn.equals(dc.get("targetIQN")) && lunid.equals(dc.get("lunid"))) {
                         throw new CloudRuntimeException("There is a SR using the same configuration target:" + dc.get("target") +  ",  targetIQN:"
-                                + dc.get("targetIQN")  + ", lunid:" + dc.get("lunid") + " for pool " + pool.getUuid() + "on host:" + _host.uuid);
+                                + dc.get("targetIQN")  + ", lunid:" + dc.get("lunid") + " for pool " + srNameLabel + "on host:" + _host.uuid);
                     }
                 }
                 deviceConfig.put("target", target);
                 deviceConfig.put("targetIQN", targetiqn);
 
+                if (StringUtils.isNotBlank(chapInitiatorUsername) &&
+                    StringUtils.isNotBlank(chapInitiatorPassword)) {
+                    deviceConfig.put("chapuser", chapInitiatorUsername);
+                    deviceConfig.put("chappassword", chapInitiatorPassword);
+                }
+
                 Host host = Host.getByUuid(conn, _host.uuid);
                 Map<String, String> smConfig = new HashMap<String, String>();
                 String type = SRType.LVMOISCSI.toString();
-                String poolId = Long.toString(pool.getId());
                 SR sr = null;
                 try {
-                    sr = SR.create(conn, host, deviceConfig, new Long(0), pool.getUuid(), poolId, type, "user", true,
+                    sr = SR.create(conn, host, deviceConfig, new Long(0), srNameLabel, srNameLabel, type, "user", true,
                             smConfig);
                 } catch (XenAPIException e) {
                     String errmsg = e.toString();
@@ -6463,19 +6462,30 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
                 if( result.indexOf("<UUID>") != -1) {
                     pooluuid = result.substring(result.indexOf("<UUID>") + 6, result.indexOf("</UUID>")).trim();
                 }
-                if( pooluuid == null || pooluuid.length() != 36) {
-                    sr = SR.create(conn, host, deviceConfig, new Long(0), pool.getUuid(), poolId, type, "user", true,
+
+                if (pooluuid == null || pooluuid.length() != 36)
+                {
+                    sr = SR.create(conn, host, deviceConfig, new Long(0), srNameLabel, srNameLabel, type, "user", true,
                             smConfig);
+
+                    created[0] = true; // note that the SR was created (as opposed to introduced)
                 } else {
-                    sr = SR.introduce(conn, pooluuid, pool.getUuid(), poolId,
-                            type, "user", true, smConfig);
-                    Pool.Record pRec = XenServerConnectionPool.getPoolRecord(conn);
-                    PBD.Record rec = new PBD.Record();
-                    rec.deviceConfig = deviceConfig;
-                    rec.host = pRec.master;
-                    rec.SR = sr;
-                    PBD pbd = PBD.create(conn, rec);
-                    pbd.plug(conn);
+                    sr = SR.introduce(conn, pooluuid, srNameLabel, srNameLabel,
+                        type, "user", true, smConfig);
+
+                    Set<Host> setHosts = Host.getAll(conn);
+
+                    for (Host currentHost : setHosts) {
+                        PBD.Record rec = new PBD.Record();
+
+                        rec.deviceConfig = deviceConfig;
+                        rec.host = currentHost;
+                        rec.SR = sr;
+
+                        PBD pbd = PBD.create(conn, rec);
+
+                        pbd.plug(conn);
+                    }
                 }
                 sr.scan(conn);
                 return sr;
@@ -6636,6 +6646,52 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
         }
     }
 
+    // for about 1 GiB of physical size, about 4 MiB seems to be used for metadata
+    private long getMetadata(long physicalSize) {
+    	return (long)(physicalSize * 0.00390625); // 1 GiB / 4 MiB = 0.00390625
+    }
+
+    protected VDI handleSrAndVdiAttach(String iqn, String storageHostName,
+            String chapInitiatorName, String chapInitiatorPassword) throws Exception {
+        VDI vdi = null;
+
+        Connection conn = getConnection();
+
+        Boolean[] created = { false };
+
+        SR sr = getIscsiSR(conn, iqn,
+                    storageHostName, iqn,
+                    chapInitiatorName, chapInitiatorPassword, created);
+
+        // if created[0] is true, this means the SR was actually created...as opposed to introduced
+        if (created[0]) {
+            VDI.Record vdir = new VDI.Record();
+
+            vdir.nameLabel = iqn;
+            vdir.SR = sr;
+            vdir.type = Types.VdiType.USER;
+            vdir.virtualSize = sr.getPhysicalSize(conn) - sr.getPhysicalUtilisation(conn) - getMetadata(sr.getPhysicalSize(conn));
+
+            if (vdir.virtualSize < 0) {
+            	throw new Exception("VDI virtual size cannot be less than 0.");
+            }
+
+            vdi = VDI.create(conn, vdir);
+        }
+        else {
+            vdi = sr.getVDIs(conn).iterator().next();
+        }
+
+    	return vdi;
+    }
+
+    protected void handleSrAndVdiDetach(String iqn) throws Exception {
+        Connection conn = getConnection();
+
+        SR sr = getStorageRepository(conn, iqn);
+
+        removeSR(conn, sr);
+    }
 
     protected AttachVolumeAnswer execute(final AttachVolumeCommand cmd) {
         Connection conn = getConnection();
@@ -6652,7 +6708,16 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
 
         try {
             // Look up the VDI
-            VDI vdi = mount(conn, cmd.getPooltype(), cmd.getVolumeFolder(),cmd.getVolumePath());
+            VDI vdi = null;
+
+            if (cmd.getAttach() && cmd.isManaged()) {
+                vdi = handleSrAndVdiAttach(cmd.get_iScsiName(), cmd.getStorageHost(),
+                        cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword());
+            }
+            else {
+                vdi = getVDIbyUuid(conn, cmd.getVolumePath());
+            }
+
             // Look up the VM
             VM vm = getVM(conn, vmName);
             /* For HVM guest, if no pv driver installed, no attach/detach */
@@ -6704,7 +6769,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
                 // Update the VDI's label to include the VM name
                 vdi.setNameLabel(conn, vmName + "-DATA");
 
-                return new AttachVolumeAnswer(cmd, Long.parseLong(diskNumber));
+                return new AttachVolumeAnswer(cmd, Long.parseLong(diskNumber), vdi.getUuid(conn));
             } else {
                 // Look up all VBDs for this VDI
                 Set<VBD> vbds = vdi.getVBDs(conn);
@@ -6723,7 +6788,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
                 // Update the VDI's label to be "detached"
                 vdi.setNameLabel(conn, "detached");
 
-                umount(conn, vdi);
+                if (cmd.isManaged()) {
+                    handleSrAndVdiDetach(cmd.get_iScsiName());
+                }
 
                 return new AttachVolumeAnswer(cmd);
             }
@@ -7606,30 +7673,30 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
         }
     }
 
-    protected SR getStorageRepository(Connection conn, String uuid) {
+    protected SR getStorageRepository(Connection conn, String srNameLabel) {
         Set<SR> srs;
         try {
-            srs = SR.getByNameLabel(conn, uuid);
+            srs = SR.getByNameLabel(conn, srNameLabel);
         } catch (XenAPIException e) {
-            throw new CloudRuntimeException("Unable to get SR " + uuid + " due to " + e.toString(), e);
+            throw new CloudRuntimeException("Unable to get SR " + srNameLabel + " due to " + e.toString(), e);
         } catch (Exception e) {
-            throw new CloudRuntimeException("Unable to get SR " + uuid + " due to " + e.getMessage(), e);
+            throw new CloudRuntimeException("Unable to get SR " + srNameLabel + " due to " + e.getMessage(), e);
         }
 
         if (srs.size() > 1) {
-            throw new CloudRuntimeException("More than one storage repository was found for pool with uuid: " + uuid);
+            throw new CloudRuntimeException("More than one storage repository was found for pool with uuid: " + srNameLabel);
         } else if (srs.size() == 1) {
             SR sr = srs.iterator().next();
             if (s_logger.isDebugEnabled()) {
-                s_logger.debug("SR retrieved for " + uuid);
+                s_logger.debug("SR retrieved for " + srNameLabel);
             }
 
             if (checkSR(conn, sr)) {
                 return sr;
             }
-            throw new CloudRuntimeException("SR check failed for storage pool: " + uuid + "on host:" + _host.uuid);
+            throw new CloudRuntimeException("SR check failed for storage pool: " + srNameLabel + "on host:" + _host.uuid);
         } else {
-            throw new CloudRuntimeException("Can not see storage pool: " + uuid + " from on host:" + _host.uuid);
+            throw new CloudRuntimeException("Can not see storage pool: " + srNameLabel + " from on host:" + _host.uuid);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java
index 399e234..e6358f2 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java
@@ -55,7 +55,6 @@ import org.apache.xmlrpc.XmlRpcException;
 
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.CreateStoragePoolCommand;
-import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
 import com.cloud.agent.api.to.DataObjectType;
 import com.cloud.agent.api.to.DataStoreTO;
 import com.cloud.agent.api.to.DataTO;
@@ -171,7 +170,16 @@ public class XenServerStorageProcessor implements StorageProcessor {
         try {
             Connection conn = this.hypervisorResource.getConnection();
             // Look up the VDI
-            VDI vdi = this.hypervisorResource.mount(conn, null, null, data.getPath());
+            VDI vdi = null;
+
+            if (cmd.isManaged()) {
+                vdi = this.hypervisorResource.handleSrAndVdiAttach(cmd.get_iScsiName(), cmd.getStorageHost(),
+                        cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword());
+            }
+            else {
+                vdi = this.hypervisorResource.mount(conn, null, null, data.getPath());
+            }
+
             // Look up the VM
             VM vm = this.hypervisorResource.getVM(conn, vmName);
             /* For HVM guest, if no pv driver installed, no attach/detach */
@@ -223,7 +231,7 @@ public class XenServerStorageProcessor implements StorageProcessor {
 
             // Update the VDI's label to include the VM name
             vdi.setNameLabel(conn, vmName + "-DATA");
-            DiskTO newDisk = new DiskTO(disk.getData(), Long.parseLong(diskNumber), disk.getType());
+            DiskTO newDisk = new DiskTO(disk.getData(), Long.parseLong(diskNumber), vdi.getUuid(conn), disk.getType());
             return new AttachAnswer(newDisk);
 
         } catch (XenAPIException e) {
@@ -350,6 +358,10 @@ public class XenServerStorageProcessor implements StorageProcessor {
 
             this.hypervisorResource.umount(conn, vdi);
 
+            if (cmd.isManaged()) {
+                this.hypervisorResource.handleSrAndVdiDetach(cmd.get_iScsiName());
+            }
+
             return new DettachAnswer(disk);
         } catch(Exception e) {
             s_logger.warn("Failed dettach volume: " + data.getPath());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java
----------------------------------------------------------------------
diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java
index 8d7c965..a233407 100644
--- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java
+++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java
@@ -95,7 +95,12 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
     }
 
     @Override
-    public void createAsync(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
+    public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
+        return null;
+    }
+
+    @Override
+    public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
         String errMsg = null;
         Answer answer = null;
         if (data.getType() == DataObjectType.VOLUME) {
@@ -118,7 +123,7 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
     }
 
     @Override
-    public void deleteAsync(DataObject data, AsyncCompletionCallback<CommandResult> callback) {
+    public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CommandResult> callback) {
         DeleteCommand cmd = new DeleteCommand(data.getTO());
 
         CommandResult result = new CommandResult();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java
----------------------------------------------------------------------
diff --git a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java
index 643c933..78f2263 100644
--- a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java
+++ b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java
@@ -54,6 +54,11 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
         return null;
     }
 
+    @Override
+    public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
+        return null;
+    }
+
     private class CreateVolumeContext<T> extends AsyncRpcConext<T> {
         private final DataObject volume;
         public CreateVolumeContext(AsyncCompletionCallback<T> callback, DataObject volume) {
@@ -77,7 +82,7 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
     }
 
     @Override
-    public void deleteAsync(DataObject vo, AsyncCompletionCallback<CommandResult> callback) {
+    public void deleteAsync(DataStore dataStore, DataObject vo, AsyncCompletionCallback<CommandResult> callback) {
         /*
          * DeleteCommand cmd = new DeleteCommand(vo.getUri());
          * 
@@ -146,7 +151,7 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
      */
 
     @Override
-    public void createAsync(DataObject vol, AsyncCompletionCallback<CreateCmdResult> callback) {
+    public void createAsync(DataStore dataStore, DataObject vol, AsyncCompletionCallback<CreateCmdResult> callback) {
         EndPoint ep = selector.select(vol);
         CreateObjectCommand createCmd = new CreateObjectCommand(null);
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/storage/volume/solidfire/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/storage/volume/solidfire/pom.xml b/plugins/storage/volume/solidfire/pom.xml
index 9db0685..81af8ac 100644
--- a/plugins/storage/volume/solidfire/pom.xml
+++ b/plugins/storage/volume/solidfire/pom.xml
@@ -12,7 +12,7 @@
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>cloud-plugin-storage-volume-solidfire</artifactId>
-  <name>Apache CloudStack Plugin - Storage Volume solidfire</name>
+  <name>Apache CloudStack Plugin - Storage Volume SolidFire Provider</name>
   <parent>
     <groupId>org.apache.cloudstack</groupId>
     <artifactId>cloudstack-plugins</artifactId>
@@ -31,6 +31,11 @@
       <version>${cs.mysql.version}</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+      <version>${cs.gson.version}</version>
+    </dependency>
   </dependencies>
   <build>
     <defaultGoal>install</defaultGoal>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 960378c..329f27f 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,13 +16,46 @@
 // under the License.
 package org.apache.cloudstack.storage.datastore.driver;
 
-import com.cloud.agent.api.to.DataStoreTO;
-import com.cloud.agent.api.to.DataTO;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.*;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.storage.command.CommandResult;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.to.DataObjectType;
+import com.cloud.agent.api.to.DataStoreTO;
+import com.cloud.agent.api.to.DataTO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.exception.StorageUnavailableException;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.dao.VolumeDetailsDao;
+import com.cloud.user.AccountVO;
+import com.cloud.user.AccountDetailsDao;
+import com.cloud.user.AccountDetailVO;
+import com.cloud.user.dao.AccountDao;
 
 public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
+    private static final Logger s_logger = Logger.getLogger(SolidfirePrimaryDataStoreDriver.class);
+
+    @Inject private PrimaryDataStoreDao _storagePoolDao;
+    @Inject private StoragePoolDetailsDao _storagePoolDetailsDao;
+    @Inject private VolumeDao _volumeDao;
+    @Inject private VolumeDetailsDao _volumeDetailsDao;
+    @Inject private DataCenterDao _zoneDao;
+    @Inject private AccountDao _accountDao;
+    @Inject private AccountDetailsDao _accountDetailsDao;
 
     @Override
     public DataTO getTO(DataObject data) {
@@ -34,12 +67,450 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         return null;
     }
 
+    private static class SolidFireConnection {
+        private final String _managementVip;
+        private final int _managementPort;
+        private final String _clusterAdminUsername;
+        private final String _clusterAdminPassword;
+
+        public SolidFireConnection(String managementVip, int managementPort,
+                String clusterAdminUsername, String clusterAdminPassword) {
+            _managementVip = managementVip;
+            _managementPort = managementPort;
+            _clusterAdminUsername = clusterAdminUsername;
+            _clusterAdminPassword = clusterAdminPassword;
+        }
+
+        public String getManagementVip() {
+            return _managementVip;
+        }
+
+        public int getManagementPort() {
+            return _managementPort;
+        }
+
+        public String getClusterAdminUsername() {
+            return _clusterAdminUsername;
+        }
+
+        public String getClusterAdminPassword() {
+            return _clusterAdminPassword;
+        }
+    }
+
+    private SolidFireConnection getSolidFireConnection(long storagePoolId) {
+        StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_VIP);
+
+        String mVip = storagePoolDetail.getValue();
+
+        storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_PORT);
+
+        int mPort = Integer.parseInt(storagePoolDetail.getValue());
+
+        storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_USERNAME);
+
+        String clusterAdminUsername = storagePoolDetail.getValue();
+
+        storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_PASSWORD);
+
+        String clusterAdminPassword = storagePoolDetail.getValue();
+
+        return new SolidFireConnection(mVip, mPort, clusterAdminUsername, clusterAdminPassword);
+    }
+
+    private SolidFireUtil.SolidFireAccount createSolidFireAccount(String sfAccountName,
+            SolidFireConnection sfConnection) {
+        try {
+            String mVip = sfConnection.getManagementVip();
+            int mPort = sfConnection.getManagementPort();
+            String clusterAdminUsername = sfConnection.getClusterAdminUsername();
+            String clusterAdminPassword = sfConnection.getClusterAdminPassword();
+
+            long accountNumber = SolidFireUtil.createSolidFireAccount(mVip, mPort,
+                clusterAdminUsername, clusterAdminPassword, sfAccountName);
+
+            return SolidFireUtil.getSolidFireAccountById(mVip, mPort,
+                clusterAdminUsername, clusterAdminPassword, accountNumber);
+        }
+        catch (Exception ex) {
+            throw new IllegalArgumentException(ex.getMessage());
+        }
+    }
+
+    private void updateCsDbWithAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount) {
+        AccountDetailVO accountDetails = new AccountDetailVO(csAccountId,
+                SolidFireUtil.ACCOUNT_ID,
+                String.valueOf(sfAccount.getId()));
+
+        _accountDetailsDao.persist(accountDetails);
+
+        accountDetails = new AccountDetailVO(csAccountId,
+                SolidFireUtil.CHAP_INITIATOR_USERNAME,
+                String.valueOf(sfAccount.getName()));
+
+        _accountDetailsDao.persist(accountDetails);
+
+        accountDetails = new AccountDetailVO(csAccountId,
+                SolidFireUtil.CHAP_INITIATOR_SECRET,
+                String.valueOf(sfAccount.getInitiatorSecret()));
+
+        _accountDetailsDao.persist(accountDetails);
+
+        accountDetails = new AccountDetailVO(csAccountId,
+                SolidFireUtil.CHAP_TARGET_USERNAME,
+                sfAccount.getName());
+
+        _accountDetailsDao.persist(accountDetails);
+
+        accountDetails = new AccountDetailVO(csAccountId,
+                SolidFireUtil.CHAP_TARGET_SECRET,
+                sfAccount.getTargetSecret());
+
+        _accountDetailsDao.persist(accountDetails);
+    }
+
+    private class ChapInfoImpl implements ChapInfo {
+        private final String _initiatorUsername;
+        private final String _initiatorSecret;
+        private final String _targetUsername;
+        private final String _targetSecret;
+
+        public ChapInfoImpl(String initiatorUsername, String initiatorSecret,
+                String targetUsername, String targetSecret) {
+            _initiatorUsername = initiatorUsername;
+            _initiatorSecret = initiatorSecret;
+            _targetUsername = targetUsername;
+            _targetSecret = targetSecret;
+        }
+
+        public String getInitiatorUsername() {
+            return _initiatorUsername;
+        }
+
+        public String getInitiatorSecret() {
+            return _initiatorSecret;
+        }
+
+        public String getTargetUsername() {
+            return _targetUsername;
+        }
+
+        public String getTargetSecret() {
+            return _targetSecret;
+        }
+    }
+
+    @Override
+    public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
+        long accountId = volumeInfo.getAccountId();
+
+        AccountDetailVO accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_INITIATOR_USERNAME);
+
+        String chapInitiatorUsername = accountDetail.getValue();
+
+        accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_INITIATOR_SECRET);
+
+        String chapInitiatorSecret = accountDetail.getValue();
+
+        accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_USERNAME);
+
+        String chapTargetUsername = accountDetail.getValue();
+
+        accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_SECRET);
+
+        String chapTargetSecret = accountDetail.getValue();
+
+        return new ChapInfoImpl(chapInitiatorUsername, chapInitiatorSecret,
+                chapTargetUsername, chapTargetSecret);
+    }
+
+    private SolidFireUtil.SolidFireVolume createSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection)
+            throws StorageUnavailableException, Exception
+    {
+        String mVip = sfConnection.getManagementVip();
+        int mPort = sfConnection.getManagementPort();
+        String clusterAdminUsername = sfConnection.getClusterAdminUsername();
+        String clusterAdminPassword = sfConnection.getClusterAdminPassword();
+
+        AccountDetailVO accountDetail = _accountDetailsDao.findDetail(volumeInfo.getAccountId(), SolidFireUtil.ACCOUNT_ID);
+        long sfAccountId = Long.parseLong(accountDetail.getValue());
+
+        final Iops iops;
+
+        Long minIops = volumeInfo.getMinIops();
+        Long maxIops = volumeInfo.getMaxIops();
+
+        if (minIops == null || minIops <= 0 ||
+            maxIops == null || maxIops <= 0) {
+            iops = new Iops(100, 15000);
+        }
+        else {
+            iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops());
+        }
+
+    	long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
+    	        volumeInfo.getName(), sfAccountId, volumeInfo.getSize(), true,
+    	        iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
+
+    	return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
+    }
+
+    private static class Iops
+    {
+    	private final long _minIops;
+    	private final long _maxIops;
+    	private final long _burstIops;
+
+    	public Iops(long minIops, long maxIops) throws Exception
+    	{
+    	    if (minIops <= 0 || maxIops <= 0) {
+    	        throw new Exception("The 'Min IOPS' and 'Max IOPS' values must be greater than 0.");
+    	    }
+
+            if (minIops > maxIops) {
+                throw new Exception("The 'Min IOPS' value cannot exceed the 'Max IOPS' value.");
+            }
+
+            _minIops = minIops;
+            _maxIops = maxIops;
+
+            _burstIops = getBurstIops(_maxIops);
+    	}
+
+    	public long getMinIops()
+    	{
+    		return _minIops;
+    	}
+
+    	public long getMaxIops()
+    	{
+    		return _maxIops;
+    	}
+
+    	public long getBurstIops()
+    	{
+    		return _burstIops;
+    	}
+
+        private static long getBurstIops(long maxIops)
+        {
+        	return (long)(maxIops * 1.5);
+        }
+    }
+
+    private void deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection)
+            throws StorageUnavailableException, Exception
+    {
+        Long storagePoolId = volumeInfo.getPoolId();
+
+        if (storagePoolId == null) {
+            return; // this volume was never assigned to a storage pool, so no SAN volume should exist for it
+        }
+
+        String mVip = sfConnection.getManagementVip();
+        int mPort = sfConnection.getManagementPort();
+        String clusterAdminUsername = sfConnection.getClusterAdminUsername();
+        String clusterAdminPassword = sfConnection.getClusterAdminPassword();
+
+        long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
+
+        SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
+    }
+
+    private String getSfAccountName(String csAccountUuid, long csAccountId) {
+        return "CloudStack_" + csAccountUuid + "_" + getRandomNumber() + "_" + csAccountId;
+    }
+
+    private static long getRandomNumber()
+    {
+        return Math.round(Math.random() * 1000000000);
+    }
+
+    private boolean sfAccountExists(String sfAccountName, SolidFireConnection sfConnection) throws Exception {
+        String mVip = sfConnection.getManagementVip();
+        int mPort = sfConnection.getManagementPort();
+        String clusterAdminUsername = sfConnection.getClusterAdminUsername();
+        String clusterAdminPassword = sfConnection.getClusterAdminPassword();
+
+        try {
+            SolidFireUtil.getSolidFireAccountByName(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountName);
+        }
+        catch (Exception ex) {
+            return false;
+        }
+
+        return true;
+    }
+
     @Override
-    public void createAsync(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
+    public void createAsync(DataStore dataStore, DataObject dataObject,
+            AsyncCompletionCallback<CreateCmdResult> callback) {
+        String iqn = null;
+        String errMsg = null;
+
+        if (dataObject.getType() == DataObjectType.VOLUME) {
+            try {
+                VolumeInfo volumeInfo = (VolumeInfo)dataObject;
+                AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
+                String sfAccountName = getSfAccountName(account.getUuid(), account.getAccountId());
+
+                long storagePoolId = dataStore.getId();
+                SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
+
+                if (!sfAccountExists(sfAccountName, sfConnection)) {
+                    SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName,
+                            sfConnection);
+
+                    updateCsDbWithAccountInfo(account.getId(), sfAccount);
+                }
+
+                SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(volumeInfo, sfConnection);
+
+                iqn = sfVolume.getIqn();
+
+                VolumeVO volume = this._volumeDao.findById(volumeInfo.getId());
+
+                volume.set_iScsiName(iqn);
+                volume.setFolder(String.valueOf(sfVolume.getId()));
+                volume.setPoolType(StoragePoolType.IscsiLUN);
+                volume.setPoolId(storagePoolId);
+
+                _volumeDao.update(volume.getId(), volume);
+
+                StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId());
+
+                long capacityBytes = storagePool.getCapacityBytes();
+                long usedBytes = storagePool.getUsedBytes();
+
+                usedBytes += volumeInfo.getSize();
+
+                if (usedBytes > capacityBytes) {
+                    usedBytes = capacityBytes;
+                }
+
+                storagePool.setUsedBytes(usedBytes);
+
+                _storagePoolDao.update(storagePoolId, storagePool);
+            } catch (StorageUnavailableException e) {
+                s_logger.error("Failed to create volume (StorageUnavailableException)", e);
+                errMsg = e.toString();
+            } catch (Exception e) {
+                s_logger.error("Failed to create volume (Exception)", e);
+                errMsg = e.toString();
+            }
+        }
+        else {
+            errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
+        }
+
+        // path = iqn
+        // size is pulled from DataObject instance, if errMsg is null
+        CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg));
+
+        result.setResult(errMsg);
+
+        callback.complete(result);
+    }
+
+    private void deleteSolidFireAccount(long sfAccountId, SolidFireConnection sfConnection) throws Exception {
+        String mVip = sfConnection.getManagementVip();
+        int mPort = sfConnection.getManagementPort();
+        String clusterAdminUsername = sfConnection.getClusterAdminUsername();
+        String clusterAdminPassword = sfConnection.getClusterAdminPassword();
+
+        List<SolidFireUtil.SolidFireVolume> sfVolumes = SolidFireUtil.getDeletedVolumes(mVip, mPort,
+                clusterAdminUsername, clusterAdminPassword);
+
+        // if there are volumes for this account in the trash, delete them (so the account can be deleted)
+        if (sfVolumes != null) {
+            for (SolidFireUtil.SolidFireVolume sfVolume : sfVolumes) {
+                if (sfVolume.getAccountId() == sfAccountId) {
+                    SolidFireUtil.purgeSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolume.getId());
+                }
+            }
+        }
+
+        SolidFireUtil.deleteSolidFireAccount(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountId);
+    }
+
+    private boolean sfAccountHasVolume(long sfAccountId, SolidFireConnection sfConnection) throws Exception {
+        String mVip = sfConnection.getManagementVip();
+        int mPort = sfConnection.getManagementPort();
+        String clusterAdminUsername = sfConnection.getClusterAdminUsername();
+        String clusterAdminPassword = sfConnection.getClusterAdminPassword();
+
+        List<SolidFireUtil.SolidFireVolume> sfVolumes = SolidFireUtil.getSolidFireVolumesForAccountId(mVip, mPort,
+                clusterAdminUsername, clusterAdminPassword, sfAccountId);
+
+        if (sfVolumes != null) {
+            for (SolidFireUtil.SolidFireVolume sfVolume : sfVolumes) {
+                if (sfVolume.isActive()) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
     }
 
     @Override
-    public void deleteAsync(DataObject data, AsyncCompletionCallback<CommandResult> callback) {
+    public void deleteAsync(DataStore dataStore, DataObject dataObject,
+            AsyncCompletionCallback<CommandResult> callback) {
+        String errMsg = null;
+
+        if (dataObject.getType() == DataObjectType.VOLUME) {
+            try {
+                VolumeInfo volumeInfo = (VolumeInfo)dataObject;
+                AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
+                AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID);
+                long sfAccountId = Long.parseLong(accountDetails.getValue());
+
+                long storagePoolId = dataStore.getId();
+                SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
+
+                deleteSolidFireVolume(volumeInfo, sfConnection);
+
+                _volumeDao.deleteVolumesByInstance(volumeInfo.getId());
+
+                if (!sfAccountHasVolume(sfAccountId, sfConnection)) {
+                    // delete the account from the SolidFire SAN
+                    deleteSolidFireAccount(sfAccountId, sfConnection);
+
+                    // delete the info in the account_details table
+                    // that's related to the SolidFire account
+                    _accountDetailsDao.deleteDetails(account.getAccountId());
+                }
+
+                StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
+
+                long usedBytes = storagePool.getUsedBytes();
+
+                usedBytes -= volumeInfo.getSize();
+
+                if (usedBytes < 0) {
+                    usedBytes = 0;
+                }
+
+                storagePool.setUsedBytes(usedBytes);
+
+                _storagePoolDao.update(storagePoolId, storagePool);
+            } catch (StorageUnavailableException e) {
+                s_logger.error("Failed to create volume (StorageUnavailableException)", e);
+                errMsg = e.toString();
+            } catch (Exception e) {
+                s_logger.error("Failed to create volume (Exception)", e);
+                errMsg = e.toString();
+            }
+        }
+        else {
+            errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync";
+        }
+
+        CommandResult result = new CommandResult();
+
+        result.setResult(errMsg);
+
+        callback.complete(result);
     }
 
     @Override
@@ -62,5 +533,4 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
     @Override
     public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {
     }
-
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java
----------------------------------------------------------------------
diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java
new file mode 100644
index 0000000..2e25cd5
--- /dev/null
+++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.storage.datastore.lifecycle;
+
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
+import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
+import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
+import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.agent.api.StoragePoolInfo;
+import com.cloud.storage.StoragePoolAutomation;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle {
+    @Inject PrimaryDataStoreDao storagePoolDao;
+    @Inject PrimaryDataStoreHelper dataStoreHelper;
+    @Inject StoragePoolAutomation storagePoolAutomation;
+    @Inject StoragePoolDetailsDao storagePoolDetailsDao;
+    @Inject DataCenterDao zoneDao;
+    
+    private static final int DEFAULT_MANAGEMENT_PORT = 443;
+    private static final int DEFAULT_STORAGE_PORT = 3260;
+    
+    // invoked to add primary storage that is based on the SolidFire plug-in
+    @Override
+    public DataStore initialize(Map<String, Object> dsInfos) {
+    	String url = (String)dsInfos.get("url");
+    	Long zoneId = (Long)dsInfos.get("zoneId");
+        String storagePoolName = (String) dsInfos.get("name");
+        String providerName = (String)dsInfos.get("providerName");
+        Long capacityBytes = (Long)dsInfos.get("capacityBytes");
+        Long capacityIops = (Long)dsInfos.get("capacityIops");
+        String tags = (String)dsInfos.get("tags");
+        Map<String, String> details = (Map<String, String>)dsInfos.get("details");
+    	
+    	String storageVip = getStorageVip(url);
+    	int storagePort = getStoragePort(url);
+    	
+    	DataCenterVO zone = zoneDao.findById(zoneId);
+    	
+    	String uuid = SolidFireUtil.PROVIDER_NAME + "_" + zone.getUuid() + "_" + storageVip;
+
+        if (capacityBytes == null || capacityBytes <= 0) {
+            throw new IllegalArgumentException("'capacityBytes' must be present and greater than 0.");
+        }
+    	
+    	if (capacityIops == null || capacityIops <= 0) {
+    	    throw new IllegalArgumentException("'capacityIops' must be present and greater than 0.");
+    	}
+    	
+        PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
+        
+        parameters.setHost(storageVip);
+        parameters.setPort(storagePort);
+        parameters.setPath(getModifiedUrl(url));
+        parameters.setType(StoragePoolType.Iscsi);
+        parameters.setUuid(uuid);
+        parameters.setZoneId(zoneId);
+        parameters.setName(storagePoolName);
+        parameters.setProviderName(providerName);
+        parameters.setManaged(true);
+        parameters.setCapacityBytes(capacityBytes);
+        parameters.setUsedBytes(0);
+        parameters.setCapacityIops(capacityIops);
+        parameters.setHypervisorType(HypervisorType.Any);
+        parameters.setTags(tags);
+        parameters.setDetails(details);
+        
+        String managementVip = getManagementVip(url);
+        int managementPort = getManagementPort(url);
+        
+        details.put(SolidFireUtil.MANAGEMENT_VIP, managementVip);
+        details.put(SolidFireUtil.MANAGEMENT_PORT, String.valueOf(managementPort));
+        
+        String clusterAdminUsername = getValue(SolidFireUtil.CLUSTER_ADMIN_USERNAME, url);
+        String clusterAdminPassword = getValue(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, url);
+        
+        details.put(SolidFireUtil.CLUSTER_ADMIN_USERNAME, clusterAdminUsername);
+        details.put(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, clusterAdminPassword);
+        
+        // this adds a row in the cloud.storage_pool table for this SolidFire cluster
+    	return dataStoreHelper.createPrimaryDataStore(parameters);
+    }
+    
+    // remove the clusterAdmin and password key/value pairs
+    private String getModifiedUrl(String originalUrl)
+    {
+    	StringBuilder sb = new StringBuilder();
+    	
+    	String delimiter = ";";
+    	
+    	StringTokenizer st = new StringTokenizer(originalUrl, delimiter);
+    	
+    	while (st.hasMoreElements()) {
+			String token = st.nextElement().toString();
+			
+			if (!token.startsWith(SolidFireUtil.CLUSTER_ADMIN_USERNAME) &&
+				!token.startsWith(SolidFireUtil.CLUSTER_ADMIN_PASSWORD)) {
+				sb.append(token).append(delimiter);
+			}
+    	}
+    	
+    	String modifiedUrl = sb.toString();
+    	int lastIndexOf = modifiedUrl.lastIndexOf(delimiter);
+    	
+    	if (lastIndexOf == (modifiedUrl.length() - delimiter.length())) {
+    		return modifiedUrl.substring(0, lastIndexOf);
+    	}
+    	
+    	return modifiedUrl;
+    }
+    
+    private String getManagementVip(String url)
+    {
+    	return getVip(SolidFireUtil.MANAGEMENT_VIP, url);
+    }
+    
+    private String getStorageVip(String url)
+    {
+    	return getVip(SolidFireUtil.STORAGE_VIP, url);
+    }
+    
+    private int getManagementPort(String url)
+    {
+    	return getPort(SolidFireUtil.MANAGEMENT_VIP, url, DEFAULT_MANAGEMENT_PORT);
+    }
+    
+    private int getStoragePort(String url)
+    {
+    	return getPort(SolidFireUtil.STORAGE_VIP, url, DEFAULT_STORAGE_PORT);
+    }
+    
+    private String getVip(String keyToMatch, String url)
+    {
+    	String delimiter = ":";
+    	
+    	String storageVip = getValue(keyToMatch, url);
+    	
+    	int index = storageVip.indexOf(delimiter);
+    	
+    	if (index != -1)
+    	{
+    		return storageVip.substring(0, index);
+    	}
+    	
+    	return storageVip;
+    }
+    
+    private int getPort(String keyToMatch, String url, int defaultPortNumber)
+    {
+    	String delimiter = ":";
+    	
+    	String storageVip = getValue(keyToMatch, url);
+    	
+    	int index = storageVip.indexOf(delimiter);
+    	
+    	int portNumber = defaultPortNumber;
+    	
+    	if (index != -1) {
+    		String port = storageVip.substring(index + delimiter.length());
+    		
+    		try {
+    			portNumber = Integer.parseInt(port);
+    		}
+    		catch (NumberFormatException ex) {
+    			throw new IllegalArgumentException("Invalid URL format (port is not an integer)");
+    		}
+    	}
+    	
+    	return portNumber;
+    }
+    
+    private String getValue(String keyToMatch, String url)
+    {
+    	String delimiter1 = ";";
+    	String delimiter2 = "=";
+    	
+    	StringTokenizer st = new StringTokenizer(url, delimiter1);
+    	
+    	while (st.hasMoreElements()) {
+			String token = st.nextElement().toString();
+			
+			int index = token.indexOf(delimiter2);
+			
+			if (index == -1)
+			{
+				throw new RuntimeException("Invalid URL format");
+			}
+			
+			String key = token.substring(0, index);
+			
+			if (key.equalsIgnoreCase(keyToMatch)) {
+				String valueToReturn = token.substring(index + delimiter2.length());
+				
+				return valueToReturn;
+			}
+		}
+    	
+    	throw new RuntimeException("Key not found in URL");
+    }
+    
+    // do not implement this method for SolidFire's plug-in
+    @Override
+    public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {
+        return true; // should be ignored for zone-wide-only plug-ins like SolidFire's
+    }
+    
+    // do not implement this method for SolidFire's plug-in
+    @Override
+    public boolean attachCluster(DataStore store, ClusterScope scope) {
+    	return true; // should be ignored for zone-wide-only plug-ins like SolidFire's
+    }
+    
+    @Override
+    public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) {
+    	dataStoreHelper.attachZone(dataStore);
+    	
+        return true;
+    }
+
+    
+    @Override
+    public boolean maintain(DataStore dataStore) {
+        storagePoolAutomation.maintain(dataStore);
+        dataStoreHelper.maintain(dataStore);
+        
+        return true;
+    }
+    
+    @Override
+    public boolean cancelMaintain(DataStore store) {
+        dataStoreHelper.cancelMaintain(store);
+        storagePoolAutomation.cancelMaintain(store);
+        
+        return true;
+    }
+    
+    // invoked to delete primary storage that is based on the SolidFire plug-in
+    @Override
+    public boolean deleteDataStore(DataStore store) {
+        return dataStoreHelper.deletePrimaryDataStore(store);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java
----------------------------------------------------------------------
diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java
index 2965e8f..28864ea 100644
--- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java
+++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java
@@ -1,62 +1,91 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 package org.apache.cloudstack.storage.datastore.provider;
 
 import java.util.Map;
 import java.util.Set;
+import java.util.HashSet;
 
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle;
 import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider;
+import org.apache.cloudstack.storage.datastore.driver.SolidfirePrimaryDataStoreDriver;
+import org.apache.cloudstack.storage.datastore.lifecycle.SolidFirePrimaryDataStoreLifeCycle;
+import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
 import org.springframework.stereotype.Component;
 
+import com.cloud.utils.component.ComponentContext;
+
 @Component
 public class SolidfirePrimaryDataStoreProvider implements PrimaryDataStoreProvider {
-    private final String name = "Solidfire Primary Data Store Provider";
+    protected DataStoreLifeCycle lifecycle;
+    protected PrimaryDataStoreDriver driver;
+    protected HypervisorHostListener listener;
+
+    SolidfirePrimaryDataStoreProvider() {
+
+    }
 
     @Override
     public String getName() {
-        return name;
+        return SolidFireUtil.PROVIDER_NAME;
     }
 
     @Override
     public DataStoreLifeCycle getDataStoreLifeCycle() {
-        return null;
+        return lifecycle;
     }
 
     @Override
-    public DataStoreDriver getDataStoreDriver() {
-        return null;
+    public PrimaryDataStoreDriver getDataStoreDriver() {
+        return driver;
     }
 
     @Override
     public HypervisorHostListener getHostListener() {
-        return null;
+        return listener;
     }
 
     @Override
     public boolean configure(Map<String, Object> params) {
-        return false;
+        lifecycle = ComponentContext.inject(SolidFirePrimaryDataStoreLifeCycle.class);
+        driver = ComponentContext.inject(SolidfirePrimaryDataStoreDriver.class);
+        listener = ComponentContext.inject(new HypervisorHostListener() {
+            public boolean hostConnect(long hostId, long poolId) {
+                return true;
+            }
+
+            public boolean hostDisconnected(long hostId, long poolId) {
+                return true;
+            }
+        });
+
+        return true;
     }
 
     @Override
     public Set<DataStoreProviderType> getTypes() {
-        return null;
-    }
+        Set<DataStoreProviderType> types =  new HashSet<DataStoreProviderType>();
 
+        types.add(DataStoreProviderType.PRIMARY);
+
+        return types;
+    }
 }


[4/4] git commit: updated refs/heads/master to 99227f7

Posted by mt...@apache.org.
SolidFire plug-in and related changes

SolidFire plug-in

SolidFire plug-in related


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

Branch: refs/heads/master
Commit: 99227f7b3e824caeb89035982793ad510e460249
Parents: 02ab2eb
Author: Mike Tutkowski <mi...@solidfire.com>
Authored: Fri Jun 28 14:05:12 2013 -0600
Committer: Mike Tutkowski <mi...@solidfire.com>
Committed: Fri Jun 28 16:59:21 2013 -0600

----------------------------------------------------------------------
 api/src/com/cloud/agent/api/to/DiskTO.java      |  12 +-
 api/src/com/cloud/offering/DiskOffering.java    |  16 +-
 api/src/com/cloud/storage/StoragePool.java      |   2 +
 api/src/com/cloud/storage/Volume.java           |   6 +
 .../org/apache/cloudstack/api/ApiConstants.java |   6 +
 .../admin/offering/CreateDiskOfferingCmd.java   |  27 +-
 .../admin/storage/CreateStoragePoolCmd.java     |  24 +
 .../command/user/volume/CreateVolumeCmd.java    |  14 +
 .../api/response/DiskOfferingResponse.java      |  33 +
 .../api/response/StoragePoolResponse.java       |  11 +
 .../cloudstack/api/response/VolumeResponse.java |  16 +
 .../classes/resources/messages.properties       |   7 +-
 client/pom.xml                                  |   5 +
 client/tomcatconf/applicationContext.xml.in     |   1 +
 .../com/cloud/agent/api/AttachVolumeAnswer.java |  16 +-
 .../cloud/agent/api/AttachVolumeCommand.java    | 114 ++-
 .../api/agent/test/AttachVolumeAnswerTest.java  |   6 +-
 .../api/agent/test/AttachVolumeCommandTest.java |  10 +-
 .../agent/test/BackupSnapshotCommandTest.java   |   5 +
 .../api/agent/test/CheckNetworkAnswerTest.java  |   5 +
 .../api/agent/test/SnapshotCommandTest.java     |   4 +
 .../engine/subsystem/api/storage/ChapInfo.java  |  26 +
 .../subsystem/api/storage/DataStoreDriver.java  |  14 +-
 .../api/storage/PrimaryDataStoreDriver.java     |   6 +-
 .../api/storage/PrimaryDataStoreParameters.java |  50 +
 .../subsystem/api/storage/VolumeService.java    |   2 +
 .../storage/command/AttachCommand.java          |  71 ++
 .../storage/command/DettachCommand.java         |  17 +
 .../storage/datastore/db/StoragePoolVO.java     |  26 +-
 .../src/com/cloud/storage/DiskOfferingVO.java   |  46 +-
 .../schema/src/com/cloud/storage/VolumeVO.java  |  57 +-
 .../src/com/cloud/storage/dao/VolumeDao.java    |   2 +
 .../com/cloud/storage/dao/VolumeDaoImpl.java    |  13 +
 .../storage/image/TemplateServiceImpl.java      |   4 +-
 .../storage/image/store/ImageStoreImpl.java     |   2 +-
 .../storage/allocator/StorageAllocatorTest.java |   3 +-
 .../cloudstack/storage/test/SnapshotTest.java   |   2 +-
 .../cloudstack/storage/test/VolumeTest.java     |   2 +-
 .../storage/test/VolumeTestVmware.java          |   2 +-
 .../storage/test/volumeServiceTest.java         |   2 +-
 .../storage/snapshot/SnapshotServiceImpl.java   |   2 +-
 .../allocator/ZoneWideStoragePoolAllocator.java |  36 +-
 .../datastore/DataObjectManagerImpl.java        |   4 +-
 .../datastore/PrimaryDataStoreEntityImpl.java   |   6 +
 .../storage/image/BaseImageStoreDriverImpl.java |   4 +-
 .../datastore/PrimaryDataStoreHelper.java       |   5 +
 .../storage/datastore/PrimaryDataStoreImpl.java |   5 +
 .../cloudstack/storage/volume/VolumeObject.java |  15 +
 .../storage/volume/VolumeServiceImpl.java       |  19 +-
 .../kvm/resource/LibvirtComputingResource.java  |   2 +-
 .../agent/manager/MockStorageManagerImpl.java   |   2 +-
 .../vmware/resource/VmwareResource.java         | 274 +++++-
 .../xen/resource/CitrixResourceBase.java        | 143 ++-
 .../xen/resource/XenServerStorageProcessor.java |  18 +-
 .../CloudStackPrimaryDataStoreDriverImpl.java   |   9 +-
 .../SamplePrimaryDataStoreDriverImpl.java       |   9 +-
 plugins/storage/volume/solidfire/pom.xml        |   7 +-
 .../driver/SolidfirePrimaryDataStoreDriver.java | 480 +++++++++-
 .../SolidFirePrimaryDataStoreLifeCycle.java     | 274 ++++++
 .../SolidfirePrimaryDataStoreProvider.java      |  81 +-
 .../storage/datastore/util/SolidFireUtil.java   | 901 +++++++++++++++++++
 .../api/query/dao/DiskOfferingJoinDaoImpl.java  |   3 +
 .../api/query/dao/StoragePoolJoinDaoImpl.java   |   2 +
 .../cloud/api/query/dao/VolumeJoinDaoImpl.java  |   3 +
 .../cloud/api/query/vo/DiskOfferingJoinVO.java  |  33 +
 .../cloud/api/query/vo/StoragePoolJoinVO.java   |  13 +-
 .../com/cloud/api/query/vo/VolumeJoinVO.java    |  21 +-
 .../configuration/ConfigurationManager.java     |   8 +-
 .../configuration/ConfigurationManagerImpl.java |  53 +-
 .../cloud/server/ConfigurationServerImpl.java   |   2 +-
 .../src/com/cloud/storage/StorageManager.java   |   9 +-
 .../com/cloud/storage/StorageManagerImpl.java   |  44 +-
 server/src/com/cloud/storage/VolumeManager.java |   1 -
 .../com/cloud/storage/VolumeManagerImpl.java    | 139 ++-
 .../com/cloud/template/TemplateManagerImpl.java |   2 +-
 server/src/com/cloud/test/DatabaseConfig.java   |   2 +-
 server/src/com/cloud/vm/UserVmManagerImpl.java  |   4 +-
 .../cloud/vpc/MockConfigurationManagerImpl.java |   5 +-
 setup/db/db/schema-410to420.sql                 |  22 +
 tools/marvin/marvin/cloudstackConnection.py     |   2 +-
 ui/dictionary.jsp                               |   5 +
 ui/scripts/configuration.js                     | 179 +++-
 ui/scripts/docs.js                              |  16 +
 ui/scripts/sharedFunctions.js                   |   4 +-
 ui/scripts/storage.js                           |  56 +-
 ui/scripts/system.js                            |  19 +-
 utils/src/com/cloud/utils/StringUtils.java      |   8 +
 .../vmware/mo/HostDatastoreSystemMO.java        |  20 +-
 .../com/cloud/hypervisor/vmware/mo/HostMO.java  |  15 +-
 .../vmware/mo/HostStorageSystemMO.java          |  51 ++
 90 files changed, 3432 insertions(+), 292 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/api/src/com/cloud/agent/api/to/DiskTO.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/agent/api/to/DiskTO.java b/api/src/com/cloud/agent/api/to/DiskTO.java
index 7b32f00..556ccd4 100644
--- a/api/src/com/cloud/agent/api/to/DiskTO.java
+++ b/api/src/com/cloud/agent/api/to/DiskTO.java
@@ -23,14 +23,16 @@ import com.cloud.storage.Volume;
 public class DiskTO {
     private DataTO data;
     private Long diskSeq;
+    private String vdiUuid;
     private Volume.Type type;
     public DiskTO() {
         
     }
     
-    public DiskTO(DataTO data, Long diskSeq, Volume.Type type) {
+    public DiskTO(DataTO data, Long diskSeq, String vdiUuid, Volume.Type type) {
         this.data = data;
         this.diskSeq = diskSeq;
+        this.vdiUuid = vdiUuid;
         this.type = type;
     }
 
@@ -50,6 +52,14 @@ public class DiskTO {
         this.diskSeq = diskSeq;
     }
 
+    public String getVdiUuid() {
+        return vdiUuid;
+    }
+
+    public void setVdiUuid(String vdiUuid) {
+        this.vdiUuid = vdiUuid;
+    }
+
     public Volume.Type getType() {
         return type;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 ae4528c..9c196e0 100644
--- a/api/src/com/cloud/offering/DiskOffering.java
+++ b/api/src/com/cloud/offering/DiskOffering.java
@@ -47,12 +47,24 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId
 
     Date getCreated();
 
-    long getDiskSize();
-
     boolean isCustomized();
 
     void setDiskSize(long diskSize);
 
+    long getDiskSize();
+
+    void setCustomizedIops(Boolean customizedIops);
+
+    Boolean isCustomizedIops();
+
+    void setMinIops(Long minIops);
+
+    Long getMinIops();
+
+    void setMaxIops(Long maxIops);
+
+    Long getMaxIops();
+
     void setBytesReadRate(Long bytesReadRate);
 
     Long getBytesReadRate();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/api/src/com/cloud/storage/StoragePool.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/storage/StoragePool.java b/api/src/com/cloud/storage/StoragePool.java
index 8f8b864..6e9af12 100644
--- a/api/src/com/cloud/storage/StoragePool.java
+++ b/api/src/com/cloud/storage/StoragePool.java
@@ -60,6 +60,8 @@ public interface StoragePool extends Identity, InternalIdentity {
      */
     long getUsedBytes();
 
+    Long getCapacityIops();
+
     Long getClusterId();
 
     /**

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 f5ed4e2..342dfd3 100755
--- a/api/src/com/cloud/storage/Volume.java
+++ b/api/src/com/cloud/storage/Volume.java
@@ -122,6 +122,12 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
      */
     Long getSize();
 
+    Long getMinIops();
+
+    Long getMaxIops();
+
+    String get_iScsiName();
+
     /**
      * @return the vm instance id
      */

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 809e023..dd876f7 100755
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -51,6 +51,9 @@ public class ApiConstants {
     public static final String CPU_OVERCOMMIT_RATIO="cpuovercommitratio";
     public static final String CREATED = "created";
     public static final String CUSTOMIZED = "customized";
+    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 DESCRIPTION = "description";
     public static final String DESTINATION_ZONE_ID = "destzoneid";
     public static final String DETAILS = "details";
@@ -326,6 +329,9 @@ public class ApiConstants {
     public static final String SERVICE_CAPABILITY_LIST = "servicecapabilitylist";
     public static final String CAN_CHOOSE_SERVICE_CAPABILITY = "canchooseservicecapability";
     public static final String PROVIDER = "provider";
+    public static final String MANAGED = "managed";
+    public static final String CAPACITY_BYTES = "capacitybytes";
+    public static final String CAPACITY_IOPS = "capacityiops";
     public static final String NETWORK_SPEED = "networkspeed";
     public static final String BROADCAST_DOMAIN_RANGE = "broadcastdomainrange";
     public static final String ISOLATION_METHODS = "isolationmethods";

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 a2c5f77..4741591 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
@@ -52,7 +52,7 @@ public class CreateDiskOfferingCmd extends BaseCmd {
     @Parameter(name=ApiConstants.TAGS, type=CommandType.STRING, description="tags for the disk offering", length=4096)
     private String tags;
 
-    @Parameter(name=ApiConstants.CUSTOMIZED, type=CommandType.BOOLEAN, description="whether disk offering is custom or not")
+    @Parameter(name=ApiConstants.CUSTOMIZED, type=CommandType.BOOLEAN, description="whether disk offering size is custom or not")
     private Boolean customized;
 
     @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class,
@@ -62,6 +62,9 @@ public class CreateDiskOfferingCmd extends BaseCmd {
     @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();
 
+    @Parameter(name=ApiConstants.DISPLAY_OFFERING, type=CommandType.BOOLEAN, description="an optional field, whether to display the offering to the end user or not.")
+    private Boolean displayOffering;
+
     @Parameter(name=ApiConstants.BYTES_READ_RATE, type=CommandType.LONG, required=false, description="bytes read rate of the disk offering")
     private Long bytesReadRate;
 
@@ -74,8 +77,14 @@ public class CreateDiskOfferingCmd extends BaseCmd {
     @Parameter(name=ApiConstants.IOPS_WRITE_RATE, type=CommandType.LONG, required=false, description="io requests write rate of the disk offering")
     private Long iopsWriteRate;
 
-    @Parameter(name=ApiConstants.DISPLAY_OFFERING, type=CommandType.BOOLEAN, description="an optional field, whether to display the offering to the end user or not.")
-    private Boolean displayOffering;
+    @Parameter(name=ApiConstants.CUSTOMIZED_IOPS, type=CommandType.BOOLEAN, required=false, description="whether disk offering iops is custom or not")
+    private Boolean customizedIops;
+
+    @Parameter(name=ApiConstants.MIN_IOPS, type=CommandType.LONG, required=false, description="min iops of the disk offering")
+    private Long minIops;
+
+    @Parameter(name=ApiConstants.MAX_IOPS, type=CommandType.LONG, required=false, description="max iops of the disk offering")
+    private Long maxIops;
 
 /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
@@ -101,6 +110,18 @@ public class CreateDiskOfferingCmd extends BaseCmd {
         return customized;
     }
 
+    public Boolean isCustomizedIops() {
+        return customizedIops;
+    }
+
+    public Long getMinIops() {
+        return minIops;
+    }
+
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
     public Long getDomainId(){
         return domainId;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/api/src/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java b/api/src/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java
index 74eb2b9..f5750b9 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java
@@ -80,6 +80,18 @@ public class CreateStoragePoolCmd extends BaseCmd {
             required=false, description="the scope of the storage: cluster or zone")
     private String scope;
 
+    @Parameter(name=ApiConstants.MANAGED, type=CommandType.BOOLEAN,
+            required=false, description="whether the storage should be managed by CloudStack")
+    private Boolean managed;
+
+    @Parameter(name=ApiConstants.CAPACITY_IOPS, type=CommandType.LONG,
+            required=false, description="IOPS CloudStack can provision from this storage pool")
+    private Long capacityIops;
+
+    @Parameter(name=ApiConstants.CAPACITY_BYTES, type=CommandType.LONG,
+            required=false, description="bytes CloudStack can provision from this storage pool")
+    private Long capacityBytes;
+
     @Parameter(name=ApiConstants.HYPERVISOR, type=CommandType.STRING, required=false,
             description="hypervisor type of the hosts in zone that will be attached to this storage pool. KVM, VMware supported as of now.")
     private String hypervisor;
@@ -124,6 +136,18 @@ public class CreateStoragePoolCmd extends BaseCmd {
         return this.scope;
     }
 
+    public Boolean isManaged() {
+    	return managed;
+    }
+
+    public Long getCapacityIops() {
+        return capacityIops;
+    }
+
+    public Long getCapacityBytes() {
+        return capacityBytes;
+    }
+
     public String getHypervisor() {
         return hypervisor;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java
index 6f0bf3a..f293a03 100644
--- a/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java
@@ -68,6 +68,12 @@ public class CreateVolumeCmd extends BaseAsyncCreateCmd {
     @Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, description="Arbitrary volume size")
     private Long size;
 
+    @Parameter(name=ApiConstants.MIN_IOPS, type=CommandType.LONG, description="min iops")
+    private Long minIops;
+
+    @Parameter(name=ApiConstants.MAX_IOPS, type=CommandType.LONG, description="max iops")
+    private Long maxIops;
+
     @Parameter(name=ApiConstants.SNAPSHOT_ID, type=CommandType.UUID, entityType=SnapshotResponse.class,
             description="the snapshot ID for the disk volume. Either diskOfferingId or snapshotId must be passed in.")
     private Long snapshotId;
@@ -104,6 +110,14 @@ public class CreateVolumeCmd extends BaseAsyncCreateCmd {
         return size;
     }
 
+    public Long getMinIops() {
+        return minIops;
+    }
+
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
     public Long getSnapshotId() {
         return snapshotId;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
index 35cf21a..4291d85 100644
--- a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
@@ -52,6 +52,15 @@ public class DiskOfferingResponse extends BaseResponse {
     @SerializedName("iscustomized") @Param(description="true if disk offering uses custom size, false otherwise")
     private Boolean customized;
 
+    @SerializedName("iscustomizediops") @Param(description="true if disk offering uses custom iops, false otherwise")
+    private Boolean customizedIops;
+
+    @SerializedName(ApiConstants.MIN_IOPS) @Param(description="the min iops of the disk offering")
+    private Long minIops;
+
+    @SerializedName(ApiConstants.MAX_IOPS) @Param(description="the max iops of the disk offering")
+    private Long maxIops;
+
     @SerializedName(ApiConstants.TAGS) @Param(description="the tags for the disk offering")
     private String tags;
 
@@ -154,6 +163,30 @@ public class DiskOfferingResponse extends BaseResponse {
         this.customized = customized;
     }
 
+    public Boolean isCustomizedIops() {
+        return customizedIops;
+    }
+
+    public void setCustomizedIops(Boolean customizedIops) {
+        this.customizedIops = customizedIops;
+    }
+
+    public Long getMinIops() {
+        return minIops;
+    }
+
+    public void setMinIops(Long minIops) {
+        this.minIops = minIops;
+    }
+
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
+    public void setMaxIops(Long maxIops) {
+        this.maxIops = maxIops;
+    }
+
     public String getStorageType() {
         return storageType;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java b/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java
index 0050000..7321d98 100644
--- a/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java
@@ -74,6 +74,9 @@ public class StoragePoolResponse extends BaseResponse {
     @SerializedName("disksizeused") @Param(description="the host's currently used disk size")
     private Long diskSizeUsed;
 
+    @SerializedName("capacityiops") @Param(description="IOPS CloudStack can provision from this storage pool")
+    private Long capacityIops;
+
     @SerializedName("tags") @Param(description="the tags for the storage pool")
     private String tags;
 
@@ -237,6 +240,14 @@ public class StoragePoolResponse extends BaseResponse {
         this.diskSizeUsed = diskSizeUsed;
     }
 
+    public Long getCapacityIops() {
+        return capacityIops;
+    }
+
+    public void setCapacityIops(Long capacityIops) {
+        this.capacityIops = capacityIops;
+    }
+
     public String getTags() {
         return tags;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
index b643de1..338fcaa 100644
--- a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
@@ -76,6 +76,14 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity
     @Param(description = "size of the disk volume")
     private Long size;
 
+    @SerializedName(ApiConstants.MIN_IOPS)
+    @Param(description = "min iops of the disk volume")
+    private Long minIops;
+
+    @SerializedName(ApiConstants.MAX_IOPS)
+    @Param(description = "max iops of the disk volume")
+    private Long maxIops;
+
     @SerializedName(ApiConstants.CREATED)
     @Param(description = "the date the disk volume was created")
     private Date created;
@@ -241,6 +249,14 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity
         this.size = size;
     }
 
+    public void setMinIops(Long minIops) {
+        this.minIops = minIops;
+    }
+
+    public void setMaxIops(Long maxIops) {
+        this.maxIops = maxIops;
+    }
+
     public void setCreated(Date created) {
         this.created = created;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 ad8d29d..b1a09b1 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -14,6 +14,10 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+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.view.secondary.ips=View secondary IPs
 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.
 message.select.affinity.groups=Please select any affinity groups you want this VM to belong to:
@@ -395,7 +399,7 @@ label.code=Code
 label.community=Community
 label.compute.and.storage=Compute and Storage
 label.compute.offering=Compute offering
-label.compute.offerings=Compute offerings
+label.compute.offerings=Compute Offerings
 label.compute=Compute
 label.configuration=Configuration
 label.configure.network.ACLs=Configure Network ACLs
@@ -1046,6 +1050,7 @@ label.stopped.vms=Stopped VMs
 label.storage.tags=Storage Tags
 label.storage.traffic=Storage Traffic
 label.storage.type=Storage Type
+label.qos.type=QoS Type
 label.storage=Storage
 label.subdomain.access=Subdomain Access
 label.submit=Submit

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/client/pom.xml
----------------------------------------------------------------------
diff --git a/client/pom.xml b/client/pom.xml
index b8182c2..d1eeb3b 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -22,6 +22,11 @@
   <dependencies>
     <dependency>
       <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-plugin-storage-volume-solidfire</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
       <artifactId>cloud-server</artifactId>
       <version>${project.version}</version>
     </dependency>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/client/tomcatconf/applicationContext.xml.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in
index 82ce9e9..7052fd7 100644
--- a/client/tomcatconf/applicationContext.xml.in
+++ b/client/tomcatconf/applicationContext.xml.in
@@ -806,6 +806,7 @@
   <bean id="cloudStackImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.CloudStackImageStoreProviderImpl" />
   <bean id="s3ImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.S3ImageStoreProviderImpl" />
   <bean id="swiftImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.SwiftImageStoreProviderImpl" />  
+  <bean id="solidFireDataStoreProvider" class="org.apache.cloudstack.storage.datastore.provider.SolidfirePrimaryDataStoreProvider" />
   <bean id="ApplicationLoadBalancerService" class="org.apache.cloudstack.network.lb.ApplicationLoadBalancerManagerImpl" />
   <bean id="InternalLoadBalancerVMManager" class="org.apache.cloudstack.network.lb.InternalLoadBalancerVMManagerImpl" />
   <bean id="StorageCacheReplacementAlgorithm" class="org.apache.cloudstack.storage.cache.manager.StorageCacheReplacementAlgorithmLRU" />

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/core/src/com/cloud/agent/api/AttachVolumeAnswer.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/AttachVolumeAnswer.java b/core/src/com/cloud/agent/api/AttachVolumeAnswer.java
index b377b7c..6b965b0 100644
--- a/core/src/com/cloud/agent/api/AttachVolumeAnswer.java
+++ b/core/src/com/cloud/agent/api/AttachVolumeAnswer.java
@@ -19,35 +19,33 @@ package com.cloud.agent.api;
 
 public class AttachVolumeAnswer extends Answer {
     private Long deviceId;
+    private String vdiUuid;
     private String chainInfo;
 
-    protected AttachVolumeAnswer() {
-
-    }
-
     public AttachVolumeAnswer(AttachVolumeCommand cmd, String result) {
         super(cmd, false, result);
         this.deviceId = null;
     }
 
-    public AttachVolumeAnswer(AttachVolumeCommand cmd, Long deviceId) {
+    public AttachVolumeAnswer(AttachVolumeCommand cmd, Long deviceId, String vdiUuid) {
         super(cmd);
         this.deviceId = deviceId;
+        this.vdiUuid = vdiUuid;
     }
 
-
     public AttachVolumeAnswer(AttachVolumeCommand cmd) {
         super(cmd);
         this.deviceId = null;
     }
 
-    /**
-     * @return the deviceId
-     */
     public Long getDeviceId() {
         return deviceId;
     }
 
+    public String getVdiUuid() {
+    	return vdiUuid;
+    }
+    
     public void setChainInfo(String chainInfo) {
     	this.chainInfo = chainInfo;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/core/src/com/cloud/agent/api/AttachVolumeCommand.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/AttachVolumeCommand.java b/core/src/com/cloud/agent/api/AttachVolumeCommand.java
index 2658262..2eb503a 100644
--- a/core/src/com/cloud/agent/api/AttachVolumeCommand.java
+++ b/core/src/com/cloud/agent/api/AttachVolumeCommand.java
@@ -19,29 +19,37 @@ package com.cloud.agent.api;
 import com.cloud.storage.Storage.StoragePoolType;
 
 public class AttachVolumeCommand extends Command {
-
-	boolean attach;
-	String vmName;
-	StoragePoolType pooltype;
-	String poolUuid;
-	String volumeFolder;
-	String volumePath;
-	String volumeName;
-	Long deviceId;
-	String chainInfo;
-    Long bytesReadRate;
-    Long bytesWriteRate;
-    Long iopsReadRate;
-    Long iopsWriteRate;
+	private boolean attach;
+	private boolean _managed;
+	private String vmName;
+	private StoragePoolType pooltype;
+	private String volumePath;
+	private String volumeName;
+	private Long deviceId;
+	private String chainInfo;
+	private String poolUuid;
+	private String _storageHost;
+	private int _storagePort;
+	private String _iScsiName;
+	private String _chapInitiatorUsername;
+	private String _chapInitiatorPassword;
+	private String _chapTargetUsername;
+	private String _chapTargetPassword;
+	private Long bytesReadRate;
+	private Long bytesWriteRate;
+	private Long iopsReadRate;
+	private Long iopsWriteRate;
 
 	protected AttachVolumeCommand() {
 	}
 
-	public AttachVolumeCommand(boolean attach, String vmName, StoragePoolType pooltype, String volumeFolder, String volumePath, String volumeName, Long deviceId, String chainInfo) {
+    public AttachVolumeCommand(boolean attach, boolean managed, String vmName,
+            StoragePoolType pooltype, String volumePath, String volumeName,
+            Long deviceId, String chainInfo) {
 		this.attach = attach;
+		this._managed = managed;
 		this.vmName = vmName;
 		this.pooltype = pooltype;
-		this.volumeFolder = volumeFolder;
 		this.volumePath = volumePath;
 		this.volumeName = volumeName;
 		this.deviceId = deviceId;
@@ -54,7 +62,7 @@ public class AttachVolumeCommand extends Command {
     }
 
 	public boolean getAttach() {
-		return attach;
+	    return attach;
 	}
 
 	public String getVmName() {
@@ -69,16 +77,12 @@ public class AttachVolumeCommand extends Command {
         this.pooltype = pooltype;
     }
 
-    public String getVolumeFolder() {
-		return volumeFolder;
-	}
-
 	public String getVolumePath() {
 		return volumePath;
 	}
 
 	public String getVolumeName() {
-		return volumeName;
+	    return volumeName;
 	}
 
     public Long getDeviceId() {
@@ -90,17 +94,77 @@ public class AttachVolumeCommand extends Command {
     }
 
     public String getPoolUuid() {
-    	return poolUuid;
+        return poolUuid;
     }
 
     public void setPoolUuid(String poolUuid) {
-    	this.poolUuid = poolUuid;
+        this.poolUuid = poolUuid;
     }
 
     public String getChainInfo() {
-    	return chainInfo;
+        return chainInfo;
+    }
+
+    public void setStorageHost(String storageHost) {
+        _storageHost = storageHost;
     }
 
+	public String getStorageHost() {
+	    return _storageHost;
+	}
+
+	public void setStoragePort(int storagePort) {
+	    _storagePort = storagePort;
+	}
+
+	public int getStoragePort() {
+	    return _storagePort;
+	}
+
+	public boolean isManaged() {
+        return _managed;
+    }
+
+	public void set_iScsiName(String iScsiName) {
+	    this._iScsiName = iScsiName;
+	}
+
+	public String get_iScsiName() {
+	    return _iScsiName;
+	}
+
+	public void setChapInitiatorUsername(String chapInitiatorUsername) {
+	    _chapInitiatorUsername = chapInitiatorUsername;
+	}
+
+	public String getChapInitiatorUsername() {
+	    return _chapInitiatorUsername;
+	}
+
+	public void setChapInitiatorPassword(String chapInitiatorPassword) {
+	    _chapInitiatorPassword = chapInitiatorPassword;
+	}
+
+	public String getChapInitiatorPassword() {
+	    return _chapInitiatorPassword;
+	}
+
+	public void setChapTargetUsername(String chapTargetUsername) {
+	    _chapTargetUsername = chapTargetUsername;
+	}
+
+	public String getChapTargetUsername() {
+	    return _chapTargetUsername;
+	}
+
+	public void setChapTargetPassword(String chapTargetPassword) {
+	    _chapTargetPassword = chapTargetPassword;
+	}
+
+	public String getChapTargetPassword() {
+	    return _chapTargetPassword;
+	}
+
     public void setBytesReadRate(Long bytesReadRate) {
         this.bytesReadRate = bytesReadRate;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeAnswerTest.java
----------------------------------------------------------------------
diff --git a/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeAnswerTest.java b/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeAnswerTest.java
index 251a6cb..9e43d9f 100644
--- a/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeAnswerTest.java
+++ b/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeAnswerTest.java
@@ -26,14 +26,14 @@ import com.cloud.agent.api.AttachVolumeCommand;
 import com.cloud.storage.Storage.StoragePoolType;
 
 public class AttachVolumeAnswerTest {
-    AttachVolumeCommand avc = new AttachVolumeCommand(true, "vmname",
-            StoragePoolType.Filesystem, "vFolder", "vPath", "vName",
+    AttachVolumeCommand avc = new AttachVolumeCommand(true, false, "vmname",
+            StoragePoolType.Filesystem, "vPath", "vName",
             123456789L, "chainInfo");
     AttachVolumeAnswer ava1 = new AttachVolumeAnswer(avc);
     String results = "";
     AttachVolumeAnswer ava2 = new AttachVolumeAnswer(avc, results);
     Long deviceId = 10L;
-    AttachVolumeAnswer ava3 = new AttachVolumeAnswer(avc, deviceId);
+    AttachVolumeAnswer ava3 = new AttachVolumeAnswer(avc, deviceId, "");
 
     @Test
     public void testGetDeviceId() {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeCommandTest.java
----------------------------------------------------------------------
diff --git a/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeCommandTest.java b/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeCommandTest.java
index 1ec416a..6f413c0 100644
--- a/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeCommandTest.java
+++ b/core/test/org/apache/cloudstack/api/agent/test/AttachVolumeCommandTest.java
@@ -25,8 +25,8 @@ import com.cloud.agent.api.AttachVolumeCommand;
 import com.cloud.storage.Storage.StoragePoolType;
 
 public class AttachVolumeCommandTest {
-    AttachVolumeCommand avc = new AttachVolumeCommand(true, "vmname",
-            StoragePoolType.Filesystem, "vFolder", "vPath", "vName",
+    AttachVolumeCommand avc = new AttachVolumeCommand(true, false, "vmname",
+            StoragePoolType.Filesystem, "vPath", "vName",
             123456789L, "chainInfo");
 
     @Test
@@ -66,12 +66,6 @@ public class AttachVolumeCommandTest {
     }
 
     @Test
-    public void testGetVolumeFolder() {
-        String vFolder = avc.getVolumeFolder();
-        assertTrue(vFolder.equals("vFolder"));
-    }
-
-    @Test
     public void testGetVolumePath() {
         String vPath = avc.getVolumePath();
         assertTrue(vPath.equals("vPath"));

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/core/test/org/apache/cloudstack/api/agent/test/BackupSnapshotCommandTest.java
----------------------------------------------------------------------
diff --git a/core/test/org/apache/cloudstack/api/agent/test/BackupSnapshotCommandTest.java b/core/test/org/apache/cloudstack/api/agent/test/BackupSnapshotCommandTest.java
index 9890593..0fee8c6 100644
--- a/core/test/org/apache/cloudstack/api/agent/test/BackupSnapshotCommandTest.java
+++ b/core/test/org/apache/cloudstack/api/agent/test/BackupSnapshotCommandTest.java
@@ -88,6 +88,11 @@ public class BackupSnapshotCommandTest {
         };
 
         @Override
+        public Long getCapacityIops() {
+            return 0L;
+        }
+
+        @Override
         public Long getClusterId() {
             return 0L;
         };

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/core/test/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java
----------------------------------------------------------------------
diff --git a/core/test/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java b/core/test/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java
index 4db6557..b834a26 100644
--- a/core/test/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java
+++ b/core/test/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java
@@ -126,6 +126,11 @@ public class CheckNetworkAnswerTest {
             };
 
             @Override
+            public Long getCapacityIops() {
+                return 0L;
+            };
+
+            @Override
             public Long getClusterId() {
                 return 0L;
             };

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/core/test/org/apache/cloudstack/api/agent/test/SnapshotCommandTest.java
----------------------------------------------------------------------
diff --git a/core/test/org/apache/cloudstack/api/agent/test/SnapshotCommandTest.java b/core/test/org/apache/cloudstack/api/agent/test/SnapshotCommandTest.java
index 3076d45..35bdfc8 100644
--- a/core/test/org/apache/cloudstack/api/agent/test/SnapshotCommandTest.java
+++ b/core/test/org/apache/cloudstack/api/agent/test/SnapshotCommandTest.java
@@ -78,6 +78,10 @@ public class SnapshotCommandTest {
             return 0L;
         };
 
+        public Long getCapacityIops() {
+            return 0L;
+        };
+
         public Long getClusterId() {
             return 0L;
         };

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/ChapInfo.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/ChapInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/ChapInfo.java
new file mode 100644
index 0000000..97c9ecb
--- /dev/null
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/ChapInfo.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.engine.subsystem.api.storage;
+
+public interface ChapInfo {
+    String getInitiatorUsername();
+    String getInitiatorSecret();
+    String getTargetUsername();
+    String getTargetSecret();
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java
index 1cb6e15..127b858 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java
@@ -24,17 +24,11 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.storage.command.CommandResult;
 
 public interface DataStoreDriver {
-    void createAsync(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback);
-
-    void deleteAsync(DataObject data, AsyncCompletionCallback<CommandResult> callback);
-
+    DataTO getTO(DataObject data);
+    DataStoreTO getStoreTO(DataStore store);
+    void createAsync(DataStore store, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback);
+    void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallback<CommandResult> callback);
     void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback);
-
     boolean canCopy(DataObject srcData, DataObject destData);
-
     void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback);
-
-    DataTO getTO(DataObject data);
-
-    DataStoreTO getStoreTO(DataStore store);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java
index 2528a53..b124d83 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java
@@ -22,7 +22,7 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.storage.command.CommandResult;
 
 public interface PrimaryDataStoreDriver extends DataStoreDriver {
-    void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback);
-
-    void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback);
+    public ChapInfo getChapInfo(VolumeInfo volumeInfo);
+    public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback);
+    public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreParameters.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreParameters.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreParameters.java
index 3b5362a..c05419f 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreParameters.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreParameters.java
@@ -20,6 +20,7 @@ package org.apache.cloudstack.engine.subsystem.api.storage;
 
 import java.util.Map;
 
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.Storage.StoragePoolType;
 
 public class PrimaryDataStoreParameters {
@@ -30,12 +31,17 @@ public class PrimaryDataStoreParameters {
     private Map<String, String> details;
     private String tags;
     private StoragePoolType type;
+    private HypervisorType hypervisorType;
     private String host;
     private String path;
     private int port;
     private String uuid;
     private String name;
     private String userInfo;
+    private long capacityBytes;
+    private long usedBytes;
+    private boolean managed;
+    private Long capacityIops;
 
     /**
      * @return the userInfo
@@ -187,6 +193,30 @@ public class PrimaryDataStoreParameters {
         this.providerName = providerName;
     }
 
+    public void setManaged(boolean managed) {
+    	this.managed = managed;
+    }
+
+    public boolean isManaged() {
+    	return managed;
+    }
+
+    public void setCapacityIops(Long capacityIops) {
+        this.capacityIops = capacityIops;
+    }
+
+    public Long getCapacityIops() {
+        return capacityIops;
+    }
+
+    public void setHypervisorType(HypervisorType hypervisorType) {
+        this.hypervisorType = hypervisorType;
+    }
+
+    public HypervisorType getHypervisorType() {
+        return hypervisorType;
+    }
+
     /**
      * @return the clusterId
      */
@@ -231,4 +261,24 @@ public class PrimaryDataStoreParameters {
     public void setZoneId(Long zoneId) {
         this.zoneId = zoneId;
     }
+
+    public long getCapacityBytes()
+    {
+    	return capacityBytes;
+    }
+
+    public void setCapacityBytes(long capacityBytes)
+    {
+    	this.capacityBytes = capacityBytes;
+    }
+
+    public long getUsedBytes()
+    {
+    	return usedBytes;
+    }
+
+    public void setUsedBytes(long usedBytes)
+    {
+    	this.usedBytes = usedBytes;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
index f96ea40..7515088 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
@@ -42,6 +42,8 @@ public interface VolumeService {
         }
     }
 
+    ChapInfo getChapInfo(VolumeInfo volumeInfo, DataStore dataStore);
+
     /**
      * Creates the volume based on the given criteria
      * 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java
index 6b4e9f7..44bce91 100644
--- a/engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java
+++ b/engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java
@@ -24,6 +24,14 @@ import com.cloud.agent.api.to.DiskTO;
 public final class AttachCommand extends Command implements StorageSubSystemCommand {
     private DiskTO disk;
     private String vmName;
+    private String _storageHost;
+    private int _storagePort;
+    private boolean _managed;
+    private String _iScsiName;
+    private String _chapInitiatorUsername;
+    private String _chapInitiatorPassword;
+    private String _chapTargetUsername;
+    private String _chapTargetPassword;
 
     public AttachCommand(DiskTO disk, String vmName) {
         super();
@@ -52,4 +60,67 @@ public final class AttachCommand extends Command implements StorageSubSystemComm
         this.vmName = vmName;
     }
 
+    public void setStorageHost(String storageHost) {
+        _storageHost = storageHost;
+    }
+
+    public String getStorageHost() {
+        return _storageHost;
+    }
+
+    public void setStoragePort(int storagePort) {
+        _storagePort = storagePort;
+    }
+
+    public int getStoragePort() {
+        return _storagePort;
+    }
+
+    public void setManaged(boolean managed) {
+        _managed = managed;
+    }
+
+    public boolean isManaged() {
+        return _managed;
+    }
+
+    public void set_iScsiName(String iScsiName) {
+        this._iScsiName = iScsiName;
+    }
+
+    public String get_iScsiName() {
+        return _iScsiName;
+    }
+
+    public void setChapInitiatorUsername(String chapInitiatorUsername) {
+        _chapInitiatorUsername = chapInitiatorUsername;
+    }
+
+    public String getChapInitiatorUsername() {
+        return _chapInitiatorUsername;
+    }
+
+    public void setChapInitiatorPassword(String chapInitiatorPassword) {
+        _chapInitiatorPassword = chapInitiatorPassword;
+    }
+
+    public String getChapInitiatorPassword() {
+        return _chapInitiatorPassword;
+    }
+
+    public void setChapTargetUsername(String chapTargetUsername) {
+        _chapTargetUsername = chapTargetUsername;
+    }
+
+    public String getChapTargetUsername() {
+        return _chapTargetUsername;
+    }
+
+    public void setChapTargetPassword(String chapTargetPassword) {
+        _chapTargetPassword = chapTargetPassword;
+    }
+
+    public String getChapTargetPassword() {
+        return _chapTargetPassword;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java
index a0ab4b2..bb7325c 100644
--- a/engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java
+++ b/engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java
@@ -24,6 +24,8 @@ import com.cloud.agent.api.to.DiskTO;
 public class DettachCommand extends Command implements StorageSubSystemCommand {
     private DiskTO disk;
     private String vmName;
+    private boolean _managed;
+    private String _iScsiName;
 
     public DettachCommand(DiskTO disk, String vmName) {
         super();
@@ -52,4 +54,19 @@ public class DettachCommand extends Command implements StorageSubSystemCommand {
         this.vmName = vmName;
     }
 
+    public void setManaged(boolean managed) {
+        _managed = managed;
+    }
+
+    public boolean isManaged() {
+        return _managed;
+    }
+
+    public void set_iScsiName(String iScsiName) {
+        _iScsiName = iScsiName;
+    }
+
+    public String get_iScsiName() {
+        return _iScsiName;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java b/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java
index 9b8de67..a8c1e7f 100644
--- a/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java
+++ b/engine/api/src/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java
@@ -103,6 +103,12 @@ public class StoragePoolVO implements StoragePool {
     @Enumerated(value = EnumType.STRING)
     private ScopeType scope;
 
+    @Column(name = "managed")
+    private boolean managed;
+
+    @Column(name = "capacity_iops", updatable = true, nullable = true)
+    private Long capacityIops;
+
     @Column(name = "hypervisor")
     @Enumerated(value = EnumType.STRING)
     private HypervisorType hypervisor;
@@ -201,8 +207,24 @@ public class StoragePoolVO implements StoragePool {
         usedBytes = available;
     }
 
-    public void setCapacityBytes(long capacity) {
-        capacityBytes = capacity;
+    public void setCapacityBytes(long capacityBytes) {
+        this.capacityBytes = capacityBytes;
+    }
+
+    public void setManaged(boolean managed) {
+    	this.managed = managed;
+    }
+
+    public boolean isManaged() {
+    	return managed;
+    }
+
+    public void setCapacityIops(Long capacityIops) {
+        this.capacityIops = capacityIops;
+    }
+
+    public Long getCapacityIops() {
+        return capacityIops;
     }
 
     public Long getClusterId() {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 b7363e7..d9656b4 100755
--- a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
+++ b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
@@ -94,6 +94,15 @@ public class DiskOfferingVO implements DiskOffering {
     @Column(name = "uuid")
     private String uuid;
 
+    @Column(name="customized_iops")
+    private Boolean customizedIops;
+
+    @Column(name="min_iops")
+    Long minIops;
+
+    @Column(name="max_iops")
+    Long maxIops;
+
     @Column(name = "sort_key")
     int sortKey;
 
@@ -116,8 +125,8 @@ public class DiskOfferingVO implements DiskOffering {
         this.uuid = UUID.randomUUID().toString();
     }
 
-    public DiskOfferingVO(Long domainId, String name, String displayText, long diskSize, String tags,
-            boolean isCustomized) {
+    public DiskOfferingVO(Long domainId, String name, String displayText, long diskSize, String tags, boolean isCustomized,
+    		Boolean isCustomizedIops, Long minIops, Long maxIops) {
         this.domainId = domainId;
         this.name = name;
         this.displayText = displayText;
@@ -128,6 +137,9 @@ public class DiskOfferingVO implements DiskOffering {
         this.useLocalStorage = false;
         this.customized = isCustomized;
         this.uuid = UUID.randomUUID().toString();
+        this.customizedIops = isCustomizedIops;
+        this.minIops = minIops;
+        this.maxIops = maxIops;
     }
 
     public DiskOfferingVO(String name, String displayText, boolean mirrored, String tags, boolean recreatable,
@@ -175,6 +187,36 @@ public class DiskOfferingVO implements DiskOffering {
     }
 
     @Override
+    public Boolean isCustomizedIops() {
+        return customizedIops;
+    }
+
+    @Override
+    public void setCustomizedIops(Boolean customizedIops) {
+        this.customizedIops = customizedIops;
+    }
+
+    @Override
+    public Long getMinIops() {
+        return minIops;
+    }
+
+	@Override
+    public void setMinIops(Long minIops) {
+        this.minIops = minIops;
+    }
+
+    @Override
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
+    @Override
+    public void setMaxIops(Long maxIops) {
+        this.maxIops = maxIops;
+    }
+
+	@Override
     public String getUniqueName() {
         return uniqueName;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 02c09a2..7b54f3d 100755
--- a/engine/schema/src/com/cloud/storage/VolumeVO.java
+++ b/engine/schema/src/com/cloud/storage/VolumeVO.java
@@ -70,6 +70,12 @@ public class VolumeVO implements Volume {
     @Column(name = "size")
     Long size;
 
+    @Column(name = "min_iops")
+    Long minIops;
+
+    @Column(name = "max_iops")
+    Long maxIops;
+
     @Column(name = "folder")
     String folder;
 
@@ -141,25 +147,32 @@ public class VolumeVO implements Volume {
     @Column(name = "display_volume", updatable = true, nullable = false)
     protected boolean displayVolume;
 
+    @Column(name = "iscsi_name")
+    private String _iScsiName;
+
     @Transient
     // @Column(name="reservation")
     String reservationId;
 
     // Real Constructor
-    public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, long size) {
+    public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, long size,
+    		Long minIops, Long maxIops, String iScsiName) {
         this.volumeType = type;
         this.name = name;
         this.dataCenterId = dcId;
         this.accountId = accountId;
         this.domainId = domainId;
         this.size = size;
+        this.minIops = minIops;
+        this.maxIops = maxIops;
+        this._iScsiName = iScsiName;
         this.diskOfferingId = diskOfferingId;
         this.state = State.Allocated;
         this.uuid = UUID.randomUUID().toString();
     }
 
-    public VolumeVO(String name, Long dcId, Long podId, long accountId, long domainId, Long instanceId, String folder,
-            String path, long size, Volume.Type vType) {
+    public VolumeVO(String name, long dcId, long podId, long accountId, long domainId, Long instanceId, String folder, String path,
+    		long size, Long minIops, Long maxIops, String iScsiName, Volume.Type vType) {
         this.name = name;
         this.accountId = accountId;
         this.domainId = domainId;
@@ -167,6 +180,9 @@ public class VolumeVO implements Volume {
         this.folder = folder;
         this.path = path;
         this.size = size;
+        this.minIops = minIops;
+        this.maxIops = maxIops;
+        this._iScsiName = iScsiName;
         this.podId = podId;
         this.dataCenterId = dcId;
         this.volumeType = vType;
@@ -177,11 +193,15 @@ public class VolumeVO implements Volume {
 
     // Copy Constructor
     public VolumeVO(Volume that) {
-        this(that.getName(), that.getDataCenterId(), that.getPodId(), that.getAccountId(), that.getDomainId(), that
-                .getInstanceId(), that.getFolder(), that.getPath(), that.getSize(), that.getVolumeType());
+        this(that.getName(), that.getDataCenterId(), that.getPodId(), that.getAccountId(), that.getDomainId(), that.getInstanceId(),
+        		that.getFolder(), that.getPath(), that.getSize(), that.getMinIops(), that.getMaxIops(),
+        		that.get_iScsiName(), that.getVolumeType());
         this.recreatable = that.isRecreatable();
         this.state = that.getState();
         this.size = that.getSize();
+        this.minIops = that.getMinIops();
+        this.maxIops = that.getMaxIops();
+        this._iScsiName = that.get_iScsiName();
         this.diskOfferingId = that.getDiskOfferingId();
         this.poolId = that.getPoolId();
         this.attached = that.getAttached();
@@ -275,6 +295,24 @@ public class VolumeVO implements Volume {
     }
 
     @Override
+    public Long getMinIops() {
+        return minIops;
+    }
+
+    public void setMinIops(Long minIops) {
+        this.minIops = minIops;
+    }
+
+    @Override
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
+    public void setMaxIops(Long maxIops) {
+        this.maxIops = maxIops;
+    }
+
+    @Override
     public Long getInstanceId() {
         return instanceId;
     }
@@ -464,6 +502,15 @@ public class VolumeVO implements Volume {
         this.uuid = uuid;
     }
 
+    @Override
+    public String get_iScsiName() {
+    	return this._iScsiName;
+    }
+
+    public void set_iScsiName(String iScsiName) {
+    	this._iScsiName = iScsiName;
+    }
+
     public boolean isDisplayVolume() {
         return displayVolume;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/schema/src/com/cloud/storage/dao/VolumeDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/dao/VolumeDao.java b/engine/schema/src/com/cloud/storage/dao/VolumeDao.java
index 79c0dc3..fb7dc70 100755
--- a/engine/schema/src/com/cloud/storage/dao/VolumeDao.java
+++ b/engine/schema/src/com/cloud/storage/dao/VolumeDao.java
@@ -58,6 +58,8 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S
 
     List<VolumeVO> findByPoolId(long poolId);
 
+    List<VolumeVO> findByPoolId(long poolId, Volume.Type volumeType);
+
     List<VolumeVO> findByInstanceAndDeviceId(long instanceId, long deviceId);
 
     List<VolumeVO> findUsableVolumesForInstance(long instanceId);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/schema/src/com/cloud/storage/dao/VolumeDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/com/cloud/storage/dao/VolumeDaoImpl.java
index f82b511..ba85466 100755
--- a/engine/schema/src/com/cloud/storage/dao/VolumeDaoImpl.java
+++ b/engine/schema/src/com/cloud/storage/dao/VolumeDaoImpl.java
@@ -109,6 +109,19 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
         sc.setParameters("poolId", poolId);
         sc.setParameters("notDestroyed", Volume.State.Destroy);
         sc.setParameters("vType", Volume.Type.ROOT.toString());
+	    return listBy(sc);
+	}
+
+    @Override
+    public List<VolumeVO> findByPoolId(long poolId, Volume.Type volumeType) {
+        SearchCriteria<VolumeVO> sc = AllFieldsSearch.create();
+        sc.setParameters("poolId", poolId);
+        sc.setParameters("notDestroyed", Volume.State.Destroy);
+
+        if (volumeType != null) {
+            sc.setParameters("vType", volumeType.toString());
+        }
+
         return listBy(sc);
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
index 96c35f3..da62712 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
@@ -166,7 +166,7 @@ public class TemplateServiceImpl implements TemplateService {
 
         AsyncCallbackDispatcher<TemplateServiceImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this);
         caller.setCallback(caller.getTarget().createTemplateCallback(null, null)).setContext(context);
-        store.getDriver().createAsync(templateOnStore, caller);
+        store.getDriver().createAsync(store, templateOnStore, caller);
     }
 
     @Override
@@ -511,7 +511,7 @@ public class TemplateServiceImpl implements TemplateService {
         TemplateOpContext<TemplateApiResult> context = new TemplateOpContext<TemplateApiResult>(null, to, future);
         AsyncCallbackDispatcher<TemplateServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
         caller.setCallback(caller.getTarget().deleteTemplateCallback(null, null)).setContext(context);
-        to.getDataStore().getDriver().deleteAsync(to, caller);
+        to.getDataStore().getDriver().deleteAsync(to.getDataStore(), to, caller);
         return future;
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
index 6d8e8e5..438ab69 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
@@ -145,7 +145,7 @@ public class ImageStoreImpl implements ImageStoreEntity {
     @Override
     public boolean delete(DataObject obj) {
         AsyncCallFuture<CommandResult> future = new AsyncCallFuture<CommandResult>();
-        this.driver.deleteAsync(obj, future);
+        this.driver.deleteAsync(obj.getDataStore(), obj, future);
         try {
             future.get();
         } catch (InterruptedException e) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/integration-test/test/org/apache/cloudstack/storage/allocator/StorageAllocatorTest.java
----------------------------------------------------------------------
diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/allocator/StorageAllocatorTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/allocator/StorageAllocatorTest.java
index 40d9d41..90696ca 100644
--- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/allocator/StorageAllocatorTest.java
+++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/allocator/StorageAllocatorTest.java
@@ -148,7 +148,8 @@ public class StorageAllocatorTest {
         diskOffering = diskOfferingDao.persist(diskOffering);
         diskOfferingId = diskOffering.getId();
 
-        volume = new VolumeVO(Volume.Type.ROOT, "volume", dcId, 1, 1, diskOffering.getId(), diskOffering.getDiskSize());
+        volume = new VolumeVO(Volume.Type.ROOT, "volume", dcId, 1, 1, diskOffering.getId(), diskOffering.getDiskSize(),
+                diskOffering.getMinIops(), diskOffering.getMaxIops(), "");
         volume = volumeDao.persist(volume);
         volumeId = volume.getId();
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java
----------------------------------------------------------------------
diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java
index 2579a38..f1eed3a 100644
--- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java
+++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java
@@ -347,7 +347,7 @@ public class SnapshotTest extends CloudStackTestNGBase {
 
     private VolumeVO createVolume(Long templateId, long dataStoreId) {
 
-        VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000);
+        VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000, 0L, 0L, "");
         volume.setDataCenterId(this.dcId);
         volume.setPoolId(dataStoreId);
         volume = volumeDao.persist(volume);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java
----------------------------------------------------------------------
diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java
index 70fdb1b..cbfafc9 100644
--- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java
+++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java
@@ -317,7 +317,7 @@ public class VolumeTest extends CloudStackTestNGBase {
     }
 
     private VolumeVO createVolume(Long templateId, long dataStoreId) {
-        VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000);
+        VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000, 0L, 0L, "");
         ;
         volume.setPoolId(dataStoreId);
         volume = volumeDao.persist(volume);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java
----------------------------------------------------------------------
diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java
index 4acc8dc..be9dd19 100644
--- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java
+++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java
@@ -317,7 +317,7 @@ public class VolumeTestVmware extends CloudStackTestNGBase {
     }
 
     private VolumeVO createVolume(Long templateId, long dataStoreId) {
-        VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000);
+        VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000, 0L, 0L, "");
         ;
         volume.setPoolId(dataStoreId);
         volume = volumeDao.persist(volume);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/volumeServiceTest.java
----------------------------------------------------------------------
diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/volumeServiceTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/volumeServiceTest.java
index 42b0463..08de7f3 100644
--- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/volumeServiceTest.java
+++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/volumeServiceTest.java
@@ -363,7 +363,7 @@ public class volumeServiceTest extends CloudStackTestNGBase {
     }
 
     private VolumeVO createVolume(Long templateId, long dataStoreId) {
-        VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000);
+        VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000, 0L, 0L, "");
         volume.setPoolId(dataStoreId);
         volume = volumeDao.persist(volume);
         return volume;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java
index 631d220..48ec512 100644
--- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java
+++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java
@@ -355,7 +355,7 @@ public class SnapshotServiceImpl implements SnapshotService {
         AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
         caller.setCallback(caller.getTarget().deleteSnapshotCallback(null, null)).setContext(context);
         DataStore store = snapInfo.getDataStore();
-        store.getDriver().deleteAsync(snapInfo, caller);
+        store.getDriver().deleteAsync(store, snapInfo, caller);
 
         SnapshotResult result = null;
         try {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java
index 29b3400..300d932 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java
@@ -49,26 +49,38 @@ public class ZoneWideStoragePoolAllocator extends AbstractStoragePoolAllocator {
         Volume volume = _volumeDao.findById(dskCh.getVolumeId());
         List<Volume> requestVolumes = new ArrayList<Volume>();
         requestVolumes.add(volume);
-        return storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool);
+
+        return storageMgr.storagePoolHasEnoughIops(requestVolumes, pool) &&
+               storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool);
     }
 
-    @Override
-    protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile<? extends VirtualMachine> vmProfile,
-            DeploymentPlan plan, ExcludeList avoid, int returnUpTo) {
-        s_logger.debug("ZoneWideStoragePoolAllocator to find storage pool");
-        List<StoragePool> suitablePools = new ArrayList<StoragePool>();
-        HypervisorType hypervisor = dskCh.getHypervisorType();
-        if (hypervisor != null) {
-            if (hypervisor != HypervisorType.KVM && hypervisor != HypervisorType.VMware) {
-                s_logger.debug("Only kvm, VMware hypervisors are enabled to support zone wide storage");
-                return suitablePools;
+	@Override
+	protected List<StoragePool> select(DiskProfile dskCh,
+			VirtualMachineProfile<? extends VirtualMachine> vmProfile,
+			DeploymentPlan plan, ExcludeList avoid, int returnUpTo) {
+	    s_logger.debug("ZoneWideStoragePoolAllocator to find storage pool");
+		List<StoragePool> suitablePools = new ArrayList<StoragePool>();
+
+        List<StoragePoolVO> storagePools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), dskCh.getTags());
+
+        if (storagePools == null) {
+            storagePools = new ArrayList<StoragePoolVO>();
+        }
+
+        List<StoragePoolVO> anyHypervisorStoragePools = new ArrayList<StoragePoolVO>();
+
+        for (StoragePoolVO storagePool : storagePools) {
+            if (HypervisorType.Any.equals(storagePool.getHypervisor())) {
+                anyHypervisorStoragePools.add(storagePool);
             }
         }
 
-        List<StoragePoolVO> storagePools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), dskCh.getTags());
         List<StoragePoolVO> storagePoolsByHypervisor = _storagePoolDao.findZoneWideStoragePoolsByHypervisor(plan.getDataCenterId(), dskCh.getHypervisorType());
+
         storagePools.retainAll(storagePoolsByHypervisor);
 
+        storagePools.addAll(anyHypervisorStoragePools);
+
         // add remaining pools in zone, that did not match tags, to avoid set
         List<StoragePoolVO> allPools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), null);
         allPools.removeAll(storagePools);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataObjectManagerImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/DataObjectManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataObjectManagerImpl.java
index fa9f993..7878d8d 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/datastore/DataObjectManagerImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataObjectManagerImpl.java
@@ -162,7 +162,7 @@ public class DataObjectManagerImpl implements DataObjectManager {
         AsyncCallbackDispatcher<DataObjectManagerImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this);
         caller.setCallback(caller.getTarget().createAsynCallback(null, null)).setContext(context);
 
-        store.getDriver().createAsync(objInStore, caller);
+        store.getDriver().createAsync(store, objInStore, caller);
         return;
     }
 
@@ -321,7 +321,7 @@ public class DataObjectManagerImpl implements DataObjectManager {
         AsyncCallbackDispatcher<DataObjectManagerImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
         caller.setCallback(caller.getTarget().deleteAsynCallback(null, null)).setContext(context);
 
-        data.getDataStore().getDriver().deleteAsync(data, caller);
+        data.getDataStore().getDriver().deleteAsync(data.getDataStore(), data, caller);
         return;
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreEntityImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreEntityImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreEntityImpl.java
index e861910..0aebee2 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreEntityImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreEntityImpl.java
@@ -176,6 +176,12 @@ public class PrimaryDataStoreEntityImpl implements StorageEntity {
     }
 
     @Override
+    public Long getCapacityIops() {
+        // TODO Auto-generated method stub
+        return 0L;
+    }
+
+    @Override
     public Long getClusterId() {
         // TODO Auto-generated method stub
         return null;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
index 93b0c2b..97c1671 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
@@ -81,7 +81,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
     }
 
     @Override
-    public void createAsync(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
+    public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
         CreateContext<CreateCmdResult> context = new CreateContext<CreateCmdResult>(callback, data);
         AsyncCallbackDispatcher<BaseImageStoreDriverImpl, DownloadAnswer> caller = AsyncCallbackDispatcher
                 .create(this);
@@ -184,7 +184,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
     }
 
     @Override
-    public void deleteAsync(DataObject data, AsyncCompletionCallback<CommandResult> callback) {
+    public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CommandResult> callback) {
         DeleteCommand cmd = new DeleteCommand(data.getTO());
 
         CommandResult result = new CommandResult();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/src/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java b/engine/storage/src/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java
index 6815dec..53ead0b 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java
@@ -79,6 +79,11 @@ public class PrimaryDataStoreHelper {
         dataStoreVO.setClusterId(params.getClusterId());
         dataStoreVO.setStatus(StoragePoolStatus.Initialized);
         dataStoreVO.setUserInfo(params.getUserInfo());
+        dataStoreVO.setManaged(params.isManaged());
+        dataStoreVO.setCapacityIops(params.getCapacityIops());
+        dataStoreVO.setCapacityBytes(params.getCapacityBytes());
+        dataStoreVO.setUsedBytes(params.getUsedBytes());
+        dataStoreVO.setHypervisor(params.getHypervisorType());
 
         Map<String, String> details = params.getDetails();
         String tags = params.getTags();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
index cfdb5c0..420fd29 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
@@ -288,6 +288,11 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore {
     }
 
     @Override
+    public Long getCapacityIops() {
+        return this.pdsv.getCapacityIops();
+    }
+
+    @Override
     public Long getClusterId() {
         return this.pdsv.getClusterId();
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/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 071c110..55fc3a6 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
@@ -107,6 +107,11 @@ public class VolumeObject implements VolumeInfo {
         volumeVO.setUuid(uuid);
     }
 
+    @Override
+    public String get_iScsiName() {
+    	return volumeVO.get_iScsiName();
+    }
+
     public void setSize(Long size) {
         volumeVO.setSize(size);
     }
@@ -126,6 +131,16 @@ public class VolumeObject implements VolumeInfo {
         return volumeVO.getSize();
     }
 
+    @Override
+    public Long getMinIops() {
+        return volumeVO.getMinIops();
+    }
+
+    @Override
+    public Long getMaxIops() {
+        return volumeVO.getMaxIops();
+    }
+
     public long getVolumeId() {
         return volumeVO.getId();
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
index 56b0b08..de1e423 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
@@ -35,8 +35,11 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
 import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
 import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
@@ -143,6 +146,16 @@ public class VolumeServiceImpl implements VolumeService {
 
     }
 
+    public ChapInfo getChapInfo(VolumeInfo volumeInfo, DataStore dataStore) {
+        DataStoreDriver dataStoreDriver = dataStore.getDriver();
+
+        if (dataStoreDriver instanceof PrimaryDataStoreDriver) {
+            return ((PrimaryDataStoreDriver)dataStoreDriver).getChapInfo(volumeInfo);
+        }
+
+        return null;
+    }
+
     @Override
     public AsyncCallFuture<VolumeApiResult> createVolumeAsync(VolumeInfo volume, DataStore dataStore) {
         AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
@@ -154,7 +167,7 @@ public class VolumeServiceImpl implements VolumeService {
         AsyncCallbackDispatcher<VolumeServiceImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this);
         caller.setCallback(caller.getTarget().createVolumeCallback(null, null)).setContext(context);
 
-        dataStore.getDriver().createAsync(volumeOnStore, caller);
+        dataStore.getDriver().createAsync(dataStore, volumeOnStore, caller);
         return future;
     }
 
@@ -238,7 +251,7 @@ public class VolumeServiceImpl implements VolumeService {
         AsyncCallbackDispatcher<VolumeServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
         caller.setCallback(caller.getTarget().deleteVolumeCallback(null, null)).setContext(context);
 
-        volume.getDataStore().getDriver().deleteAsync(volume, caller);
+        volume.getDataStore().getDriver().deleteAsync(volume.getDataStore(), volume, caller);
         return future;
     }
 
@@ -935,7 +948,7 @@ public class VolumeServiceImpl implements VolumeService {
         caller.setCallback(caller.getTarget().registerVolumeCallback(null, null));
         caller.setContext(context);
 
-        store.getDriver().createAsync(volumeOnStore, caller);
+        store.getDriver().createAsync(store, volumeOnStore, caller);
         return future;
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index e0c00fc..914017c 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -2573,7 +2573,7 @@ ServerResource {
             return new AttachVolumeAnswer(cmd, e.toString());
         }
 
-        return new AttachVolumeAnswer(cmd, cmd.getDeviceId());
+        return new AttachVolumeAnswer(cmd, cmd.getDeviceId(), cmd.getVolumePath());
     }
 
     private Answer execute(ReadyCommand cmd) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99227f7b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java
index 21b81e1..a59949f 100644
--- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java
+++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java
@@ -258,7 +258,7 @@ public class MockStorageManagerImpl extends ManagerBase implements MockStorageMa
             }
             txn.commit();
 
-            return new AttachVolumeAnswer(cmd, cmd.getDeviceId());
+            return new AttachVolumeAnswer(cmd, cmd.getDeviceId(), cmd.getVolumePath());
         } catch (Exception ex) {
             txn.rollback();
             throw new CloudRuntimeException("Error when attaching volume " + cmd.getVolumeName() + " to VM "