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