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

[06/50] [abbrv] SolidFire plug-in and related changes

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: ''