Java tutorial
// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package org.apache.cloudstack.storage.datastore.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; 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.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; 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 org.apache.log4j.Logger; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.security.SSLUtils; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.user.AccountDetailVO; import com.cloud.user.AccountDetailsDao; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; public class SolidFireUtil { private static final Logger s_logger = Logger.getLogger(SolidFireUtil.class); public static final String PROVIDER_NAME = "SolidFire"; public static final String SHARED_PROVIDER_NAME = "SolidFireShared"; public static final int s_lockTimeInSeconds = 300; public static final String LOG_PREFIX = "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"; // these three variables should only be used for the SolidFire plug-in with the name SolidFireUtil.PROVIDER_NAME public static final String CLUSTER_DEFAULT_MIN_IOPS = "clusterDefaultMinIops"; public static final String CLUSTER_DEFAULT_MAX_IOPS = "clusterDefaultMaxIops"; public static final String CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS = "clusterDefaultBurstIopsPercentOfMaxIops"; // these three variables should only be used for the SolidFire plug-in with the name SolidFireUtil.SHARED_PROVIDER_NAME public static final String MIN_IOPS = "minIops"; public static final String MAX_IOPS = "maxIops"; public static final String BURST_IOPS = "burstIops"; public static final String ACCOUNT_ID = "accountId"; public static final String VOLUME_ID = "volumeId"; public static final String TEMP_VOLUME_ID = "tempVolumeId"; public static final String SNAPSHOT_ID = "snapshotId"; public static final String CloudStackVolumeId = "CloudStackVolumeId"; public static final String CloudStackVolumeSize = "CloudStackVolumeSize"; public static final String CloudStackSnapshotId = "CloudStackSnapshotId"; public static final String CloudStackSnapshotSize = "CloudStackSnapshotSize"; public static final String CloudStackTemplateId = "CloudStackTemplateId"; public static final String CloudStackTemplateSize = "CloudStackTemplateSize"; public static final String VOLUME_SIZE = "sfVolumeSize"; public static final String STORAGE_POOL_ID = "sfStoragePoolId"; 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 final String DATACENTER = "datacenter"; public static final String DATASTORE_NAME = "datastoreName"; public static final String IQN = "iqn"; public static final long MAX_IOPS_PER_VOLUME = 100000; private static final int DEFAULT_MANAGEMENT_PORT = 443; private static final int DEFAULT_STORAGE_PORT = 3260; public static class SolidFireConnection { private final String _managementVip; private final int _managementPort; private final String _clusterAdminUsername; private final String _clusterAdminPassword; public SolidFireConnection(String managementVip, int managementPort, String clusterAdminUsername, String clusterAdminPassword) { if (managementVip == null) { throw new CloudRuntimeException("The management VIP cannot be 'null'."); } if (managementPort <= 0) { throw new CloudRuntimeException("The management port must be a positive integer."); } if (clusterAdminUsername == null) { throw new CloudRuntimeException("The cluster admin username cannot be 'null'."); } if (clusterAdminPassword == null) { throw new CloudRuntimeException("The cluster admin password cannot be 'null'."); } _managementVip = managementVip; _managementPort = managementPort; _clusterAdminUsername = clusterAdminUsername; _clusterAdminPassword = clusterAdminPassword; } public String getManagementVip() { return _managementVip; } public int getManagementPort() { return _managementPort; } public String getClusterAdminUsername() { return _clusterAdminUsername; } public String getClusterAdminPassword() { return _clusterAdminPassword; } @Override public int hashCode() { return _managementVip.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof SolidFireConnection)) { return false; } SolidFireConnection sfConnection = (SolidFireConnection) obj; return _managementVip.equals(sfConnection.getManagementVip()); } } public static SolidFireConnection getSolidFireConnection(long storagePoolId, StoragePoolDetailsDao storagePoolDetailsDao) { StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_VIP); String mVip = storagePoolDetail.getValue(); storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_PORT); int mPort = Integer.parseInt(storagePoolDetail.getValue()); storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_USERNAME); String clusterAdminUsername = storagePoolDetail.getValue(); storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_PASSWORD); String clusterAdminPassword = storagePoolDetail.getValue(); return new SolidFireConnection(mVip, mPort, clusterAdminUsername, clusterAdminPassword); } public static String getSolidFireAccountName(String csAccountUuid, long csAccountId) { return "CloudStack_" + csAccountUuid + "_" + csAccountId; } public static void updateCsDbWithSolidFireIopsInfo(long storagePoolId, PrimaryDataStoreDao primaryDataStoreDao, StoragePoolDetailsDao storagePoolDetailsDao, long minIops, long maxIops, long burstIops) { Map<String, String> existingDetails = storagePoolDetailsDao.listDetailsKeyPairs(storagePoolId); Set<String> existingKeys = existingDetails.keySet(); Map<String, String> existingDetailsToKeep = new HashMap<String, String>(); for (String existingKey : existingKeys) { String existingValue = existingDetails.get(existingKey); if (!SolidFireUtil.MIN_IOPS.equalsIgnoreCase(existingValue) && !SolidFireUtil.MAX_IOPS.equalsIgnoreCase(existingValue) && !SolidFireUtil.BURST_IOPS.equalsIgnoreCase(existingValue)) { existingDetailsToKeep.put(existingKey, existingValue); } } existingDetailsToKeep.put(SolidFireUtil.MIN_IOPS, String.valueOf(minIops)); existingDetailsToKeep.put(SolidFireUtil.MAX_IOPS, String.valueOf(maxIops)); existingDetailsToKeep.put(SolidFireUtil.BURST_IOPS, String.valueOf(burstIops)); primaryDataStoreDao.updateDetails(storagePoolId, existingDetailsToKeep); } public static void updateCsDbWithSolidFireAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount, long storagePoolId, AccountDetailsDao accountDetailsDao) { AccountDetailVO accountDetail = new AccountDetailVO(csAccountId, SolidFireUtil.getAccountKey(storagePoolId), String.valueOf(sfAccount.getId())); accountDetailsDao.persist(accountDetail); /* accountDetail = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_INITIATOR_USERNAME, String.valueOf(sfAccount.getName())); accountDetailsDao.persist(accountDetail); accountDetail = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_INITIATOR_SECRET, String.valueOf(sfAccount.getInitiatorSecret())); accountDetailsDao.persist(accountDetail); accountDetail = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_TARGET_USERNAME, sfAccount.getName()); accountDetailsDao.persist(accountDetail); accountDetail = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_TARGET_SECRET, sfAccount.getTargetSecret()); accountDetailsDao.persist(accountDetail); */ } public static SolidFireAccount getSolidFireAccount(SolidFireConnection sfConnection, String sfAccountName) { try { return getSolidFireAccountByName(sfConnection, sfAccountName); } catch (Exception ex) { return null; } } public static void hostAddedToOrRemovedFromCluster(long hostId, long clusterId, boolean added, String storageProvider, ClusterDao clusterDao, ClusterDetailsDao clusterDetailsDao, PrimaryDataStoreDao storagePoolDao, StoragePoolDetailsDao storagePoolDetailsDao, HostDao hostDao) { ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); if (!lock.lock(s_lockTimeInSeconds)) { String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid(); s_logger.debug(errMsg); throw new CloudRuntimeException(errMsg); } try { List<StoragePoolVO> storagePools = storagePoolDao.findPoolsByProvider(storageProvider); if (storagePools != null && storagePools.size() > 0) { List<SolidFireUtil.SolidFireConnection> sfConnections = new ArrayList<SolidFireUtil.SolidFireConnection>(); for (StoragePoolVO storagePool : storagePools) { ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePool.getId())); String vagId = clusterDetail != null ? clusterDetail.getValue() : null; if (vagId != null) { SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil .getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); if (!sfConnections.contains(sfConnection)) { sfConnections.add(sfConnection); SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection, Long.parseLong(vagId)); List<HostVO> hostsToAddOrRemove = new ArrayList<>(); HostVO hostToAddOrRemove = hostDao.findByIdIncludingRemoved(hostId); hostsToAddOrRemove.add(hostToAddOrRemove); String[] hostIqns = SolidFireUtil.getNewHostIqns(sfVag.getInitiators(), SolidFireUtil.getIqnsFromHosts(hostsToAddOrRemove), added); SolidFireUtil.modifySolidFireVag(sfConnection, sfVag.getId(), hostIqns, sfVag.getVolumeIds()); } } } } } finally { lock.unlock(); lock.releaseRef(); } } public static long placeVolumeInVolumeAccessGroup(SolidFireConnection sfConnection, long sfVolumeId, long storagePoolId, String vagUuid, List<HostVO> hosts, ClusterDetailsDao clusterDetailsDao) { if (hosts == null || hosts.isEmpty()) { throw new CloudRuntimeException("There must be at least one host in the cluster."); } long lVagId; try { lVagId = SolidFireUtil.createSolidFireVag(sfConnection, "CloudStack-" + vagUuid, SolidFireUtil.getIqnsFromHosts(hosts), new long[] { sfVolumeId }); } catch (Exception ex) { String iqnInVagAlready = "Exceeded maximum number of Volume Access Groups per initiator"; if (!ex.getMessage().contains(iqnInVagAlready)) { throw new CloudRuntimeException(ex.getMessage()); } // getCompatibleVag throws an exception if an existing VAG can't be located SolidFireUtil.SolidFireVag sfVag = getCompatibleVag(sfConnection, hosts); lVagId = sfVag.getId(); long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); SolidFireUtil.modifySolidFireVag(sfConnection, lVagId, sfVag.getInitiators(), volumeIds); } ClusterDetailsVO clusterDetail = new ClusterDetailsVO(hosts.get(0).getClusterId(), getVagKey(storagePoolId), String.valueOf(lVagId)); clusterDetailsDao.persist(clusterDetail); return lVagId; } public static boolean hostsSupport_iScsi(List<HostVO> hosts) { if (hosts == null || hosts.size() == 0) { return false; } for (Host host : hosts) { if (host == null || host.getStorageUrl() == null || host.getStorageUrl().trim().length() == 0 || !host.getStorageUrl().startsWith("iqn")) { return false; } } return true; } public static String[] getNewHostIqns(String[] iqns, String[] iqnsToAddOrRemove, boolean add) { if (add) { return getNewHostIqnsAdd(iqns, iqnsToAddOrRemove); } return getNewHostIqnsRemove(iqns, iqnsToAddOrRemove); } private static String[] getNewHostIqnsAdd(String[] iqns, String[] iqnsToAdd) { List<String> lstIqns = iqns != null ? new ArrayList<>(Arrays.asList(iqns)) : new ArrayList<String>(); if (iqnsToAdd != null) { for (String iqnToAdd : iqnsToAdd) { if (!lstIqns.contains(iqnToAdd)) { lstIqns.add(iqnToAdd); } } } return lstIqns.toArray(new String[0]); } private static String[] getNewHostIqnsRemove(String[] iqns, String[] iqnsToRemove) { List<String> lstIqns = iqns != null ? new ArrayList<>(Arrays.asList(iqns)) : new ArrayList<String>(); if (iqnsToRemove != null) { for (String iqnToRemove : iqnsToRemove) { lstIqns.remove(iqnToRemove); } } return lstIqns.toArray(new String[0]); } public static long[] getNewVolumeIds(long[] volumeIds, long volumeIdToAddOrRemove, boolean add) { if (add) { return getNewVolumeIdsAdd(volumeIds, volumeIdToAddOrRemove); } return getNewVolumeIdsRemove(volumeIds, volumeIdToAddOrRemove); } private static long[] getNewVolumeIdsAdd(long[] volumeIds, long volumeIdToAdd) { List<Long> lstVolumeIds = new ArrayList<Long>(); if (volumeIds != null) { for (long volumeId : volumeIds) { lstVolumeIds.add(volumeId); } } if (lstVolumeIds.contains(volumeIdToAdd)) { return volumeIds; } lstVolumeIds.add(volumeIdToAdd); return convertArray(lstVolumeIds); } private static long[] getNewVolumeIdsRemove(long[] volumeIds, long volumeIdToRemove) { List<Long> lstVolumeIds = new ArrayList<Long>(); if (volumeIds != null) { for (long volumeId : volumeIds) { lstVolumeIds.add(volumeId); } } lstVolumeIds.remove(volumeIdToRemove); return convertArray(lstVolumeIds); } private static long[] convertArray(List<Long> items) { if (items == null) { return new long[0]; } long[] outArray = new long[items.size()]; for (int i = 0; i < items.size(); i++) { Long value = items.get(i); outArray[i] = value; } return outArray; } public static String getVagKey(long storagePoolId) { return "sfVolumeAccessGroup_" + storagePoolId; } public static String getAccountKey(long storagePoolId) { return SolidFireUtil.ACCOUNT_ID + "_" + storagePoolId; } public static AccountDetailVO getAccountDetail(long csAccountId, long storagePoolId, AccountDetailsDao accountDetailsDao) { AccountDetailVO accountDetail = accountDetailsDao.findDetail(csAccountId, SolidFireUtil.getAccountKey(storagePoolId)); if (accountDetail == null || accountDetail.getValue() == null) { accountDetail = accountDetailsDao.findDetail(csAccountId, SolidFireUtil.ACCOUNT_ID); } return accountDetail; } public static String[] getIqnsFromHosts(List<? extends Host> hosts) { if (hosts == null || hosts.size() == 0) { throw new CloudRuntimeException("There do not appear to be any hosts in this cluster."); } List<String> lstIqns = new ArrayList<String>(); for (Host host : hosts) { lstIqns.add(host.getStorageUrl()); } return lstIqns.toArray(new String[0]); } // this method takes in a collection of hosts and tries to find an existing VAG that has all of them in it // if successful, the VAG is returned; else, a CloudRuntimeException is thrown and this issue should be corrected by an admin private static SolidFireUtil.SolidFireVag getCompatibleVag(SolidFireConnection sfConnection, List<HostVO> hosts) { List<SolidFireUtil.SolidFireVag> sfVags = SolidFireUtil.getAllSolidFireVags(sfConnection); if (sfVags != null) { List<String> hostIqns = new ArrayList<String>(); // where the method we're in is called, hosts should not be null for (HostVO host : hosts) { // where the method we're in is called, host.getStorageUrl() should not be null (it actually should start with "iqn") hostIqns.add(host.getStorageUrl().toLowerCase()); } for (SolidFireUtil.SolidFireVag sfVag : sfVags) { List<String> lstInitiators = getStringArrayAsLowerCaseStringList(sfVag.getInitiators()); // lstInitiators should not be returned from getStringArrayAsLowerCaseStringList as null if (lstInitiators.containsAll(hostIqns)) { return sfVag; } } } throw new CloudRuntimeException("Unable to locate the appropriate SolidFire Volume Access Group"); } private static List<String> getStringArrayAsLowerCaseStringList(String[] aString) { List<String> lstLowerCaseString = new ArrayList<String>(); if (aString != null) { for (String str : aString) { if (str != null) { lstLowerCaseString.add(str.toLowerCase()); } } } return lstLowerCaseString; } public static String getSolidFireVolumeName(String strCloudStackVolumeName) { final String specialChar = "-"; StringBuilder strSolidFireVolumeName = new StringBuilder(); for (int i = 0; i < strCloudStackVolumeName.length(); i++) { String strChar = strCloudStackVolumeName.substring(i, i + 1); if (StringUtils.isAlphanumeric(strChar)) { strSolidFireVolumeName.append(strChar); } else { strSolidFireVolumeName.append(specialChar); } } return strSolidFireVolumeName.toString(); } public static long createSolidFireVolume(SolidFireConnection sfConnection, String strSfVolumeName, long lSfAccountId, long lTotalSize, boolean bEnable512e, Map<String, String> mapAttributes, long minIops, long maxIops, long burstIops) { JsonObject volumeToCreate = new JsonObject(); volumeToCreate.addProperty("method", "CreateVolume"); JsonObject params = new JsonObject(); volumeToCreate.add("params", params); params.addProperty("name", strSfVolumeName); params.addProperty("accountID", lSfAccountId); params.addProperty("totalSize", lTotalSize); params.addProperty("enable512e", bEnable512e); JsonObject qos = new JsonObject(); params.add("qos", qos); qos.addProperty("minIOPS", minIops); qos.addProperty("maxIOPS", maxIops); qos.addProperty("burstIOPS", burstIops); if (mapAttributes != null && mapAttributes.size() > 0) { JsonObject attributes = new JsonObject(); params.add("attributes", attributes); Iterator<Map.Entry<String, String>> itr = mapAttributes.entrySet().iterator(); while (itr.hasNext()) { Map.Entry<String, String> pair = itr.next(); attributes.addProperty(pair.getKey(), pair.getValue()); } } final Gson gson = new GsonBuilder().create(); String strVolumeToCreateJson = gson.toJson(volumeToCreate); String strVolumeCreateResultJson = executeJsonRpc(sfConnection, strVolumeToCreateJson); VolumeCreateResult volumeCreateResult = gson.fromJson(strVolumeCreateResultJson, VolumeCreateResult.class); verifyResult(volumeCreateResult.result, strVolumeCreateResultJson, gson); return volumeCreateResult.result.volumeID; } public static void modifySolidFireVolume(SolidFireConnection sfConnection, long volumeId, Long totalSize, Map<String, String> mapAttributes, long minIops, long maxIops, long burstIops) { JsonObject volumeToModify = new JsonObject(); volumeToModify.addProperty("method", "ModifyVolume"); JsonObject params = new JsonObject(); volumeToModify.add("params", params); params.addProperty("volumeID", volumeId); if (totalSize != null) { params.addProperty("totalSize", totalSize); } JsonObject qos = new JsonObject(); params.add("qos", qos); qos.addProperty("minIOPS", minIops); qos.addProperty("maxIOPS", maxIops); qos.addProperty("burstIOPS", burstIops); if (mapAttributes != null && mapAttributes.size() > 0) { JsonObject attributes = new JsonObject(); params.add("attributes", attributes); Iterator<Map.Entry<String, String>> itr = mapAttributes.entrySet().iterator(); while (itr.hasNext()) { Map.Entry<String, String> pair = itr.next(); attributes.addProperty(pair.getKey(), pair.getValue()); } } final Gson gson = new GsonBuilder().create(); String strVolumeToModifyJson = gson.toJson(volumeToModify); String strVolumeModifyResultJson = executeJsonRpc(sfConnection, strVolumeToModifyJson); JsonError jsonError = gson.fromJson(strVolumeModifyResultJson, JsonError.class); if (jsonError.error != null) { throw new IllegalStateException(jsonError.error.message); } } public static SolidFireVolume getSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) { final Gson gson = new GsonBuilder().create(); VolumeToGet volumeToGet = new VolumeToGet(lVolumeId); String strVolumeToGetJson = gson.toJson(volumeToGet); String strVolumeGetResultJson = executeJsonRpc(sfConnection, strVolumeToGetJson); 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); boolean enable512e = getVolumeEnable512e(volumeGetResult, lVolumeId); long lMinIops = getVolumeMinIops(volumeGetResult, lVolumeId); long lMaxIops = getVolumeMaxIops(volumeGetResult, lVolumeId); long lBurstIops = getVolumeBurstIops(volumeGetResult, lVolumeId); long lTotalSize = getVolumeTotalSize(volumeGetResult, lVolumeId); return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus, enable512e, lMinIops, lMaxIops, lBurstIops, lTotalSize); } public static List<SolidFireVolume> getSolidFireVolumesForAccountId(SolidFireConnection sfConnection, long lAccountId) { final Gson gson = new GsonBuilder().create(); VolumesToGetForAccount volumesToGetForAccount = new VolumesToGetForAccount(lAccountId); String strVolumesToGetForAccountJson = gson.toJson(volumesToGetForAccount); String strVolumesGetForAccountResultJson = executeJsonRpc(sfConnection, strVolumesToGetForAccountJson); 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, volume.enable512e, volume.qos.minIOPS, volume.qos.maxIOPS, volume.qos.burstIOPS, volume.totalSize)); } return sfVolumes; } public static List<SolidFireVolume> getDeletedVolumes(SolidFireConnection sfConnection) { final Gson gson = new GsonBuilder().create(); ListDeletedVolumes listDeletedVolumes = new ListDeletedVolumes(); String strListDeletedVolumesJson = gson.toJson(listDeletedVolumes); String strListDeletedVolumesResultJson = executeJsonRpc(sfConnection, strListDeletedVolumesJson); 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, volume.enable512e, volume.qos.minIOPS, volume.qos.maxIOPS, volume.qos.burstIOPS, volume.totalSize)); } return deletedVolumes; } public static void deleteSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) { final Gson gson = new GsonBuilder().create(); VolumeToDelete volumeToDelete = new VolumeToDelete(lVolumeId); String strVolumeToDeleteJson = gson.toJson(volumeToDelete); executeJsonRpc(sfConnection, strVolumeToDeleteJson); } public static void purgeSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) { final Gson gson = new GsonBuilder().create(); VolumeToPurge volumeToPurge = new VolumeToPurge(lVolumeId); String strVolumeToPurgeJson = gson.toJson(volumeToPurge); executeJsonRpc(sfConnection, strVolumeToPurgeJson); } 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; private final boolean _enable512e; private final long _minIops; private final long _maxIops; private final long _burstIops; private final long _totalSize; public SolidFireVolume(long id, String name, String iqn, long accountId, String status, boolean enable512e, long minIops, long maxIops, long burstIops, long totalSize) { _id = id; _name = name; _iqn = "/" + iqn + "/0"; _accountId = accountId; _status = status; _enable512e = enable512e; _minIops = minIops; _maxIops = maxIops; _burstIops = burstIops; _totalSize = totalSize; } 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); } public boolean isEnable512e() { return _enable512e; } public long getMinIops() { return _minIops; } public long getMaxIops() { return _maxIops; } public long getBurstIops() { return _burstIops; } public long getTotalSize() { return _totalSize; } @Override public int hashCode() { return _iqn.hashCode(); } @Override public String toString() { return _name; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!obj.getClass().equals(SolidFireVolume.class)) { return false; } SolidFireVolume sfv = (SolidFireVolume) obj; if (_id == sfv._id && _name.equals(sfv._name) && _iqn.equals(sfv._iqn) && _accountId == sfv._accountId && isActive() == sfv.isActive() && getTotalSize() == sfv.getTotalSize()) { return true; } return false; } } public static class SolidFireSnapshot { private final long _id; private final String _name; public SolidFireSnapshot(long id, String name) { _id = id; _name = name; } public long getId() { return _id; } public String getName() { return _name; } } public static long createSolidFireSnapshot(SolidFireConnection sfConnection, long lVolumeId, String snapshotName, Map<String, String> mapAttributes) { JsonObject snapshotToCreate = new JsonObject(); snapshotToCreate.addProperty("method", "CreateSnapshot"); JsonObject params = new JsonObject(); snapshotToCreate.add("params", params); params.addProperty("volumeID", lVolumeId); params.addProperty("name", snapshotName); if (mapAttributes != null && mapAttributes.size() > 0) { JsonObject attributes = new JsonObject(); params.add("attributes", attributes); Iterator<Map.Entry<String, String>> itr = mapAttributes.entrySet().iterator(); while (itr.hasNext()) { Map.Entry<String, String> pair = itr.next(); attributes.addProperty(pair.getKey(), pair.getValue()); } } final Gson gson = new GsonBuilder().create(); String strSnapshotToCreateJson = gson.toJson(snapshotToCreate); String strSnapshotCreateResultJson = executeJsonRpc(sfConnection, strSnapshotToCreateJson); SnapshotCreateResult snapshotCreateResult = gson.fromJson(strSnapshotCreateResultJson, SnapshotCreateResult.class); verifyResult(snapshotCreateResult.result, strSnapshotCreateResultJson, gson); return snapshotCreateResult.result.snapshotID; } public static SolidFireSnapshot getSolidFireSnapshot(SolidFireConnection sfConnection, long lVolumeId, long lSnapshotId) { final Gson gson = new GsonBuilder().create(); SnapshotsToGet snapshotsToGet = new SnapshotsToGet(lVolumeId); String strSnapshotsToGetJson = gson.toJson(snapshotsToGet); String strSnapshotsGetResultJson = executeJsonRpc(sfConnection, strSnapshotsToGetJson); SnapshotsGetResult snapshotsGetResult = gson.fromJson(strSnapshotsGetResultJson, SnapshotsGetResult.class); verifyResult(snapshotsGetResult.result, strSnapshotsGetResultJson, gson); String snapshotName = null; if (snapshotsGetResult.result.snapshots != null) { for (SnapshotsGetResult.Result.Snapshot snapshot : snapshotsGetResult.result.snapshots) { if (snapshot.snapshotID == lSnapshotId) { snapshotName = snapshot.name; break; } } } if (snapshotName == null) { throw new CloudRuntimeException("Could not find SolidFire snapshot ID: " + lSnapshotId + " for the following SolidFire volume ID: " + lVolumeId); } return new SolidFireSnapshot(lSnapshotId, snapshotName); } public static void deleteSolidFireSnapshot(SolidFireConnection sfConnection, long lSnapshotId) { final Gson gson = new GsonBuilder().create(); SnapshotToDelete snapshotToDelete = new SnapshotToDelete(lSnapshotId); String strSnapshotToDeleteJson = gson.toJson(snapshotToDelete); executeJsonRpc(sfConnection, strSnapshotToDeleteJson); } public static void rollBackVolumeToSnapshot(SolidFireConnection sfConnection, long volumeId, long snapshotId) { final Gson gson = new GsonBuilder().create(); RollbackToInitiate rollbackToInitiate = new RollbackToInitiate(volumeId, snapshotId); String strRollbackToInitiateJson = gson.toJson(rollbackToInitiate); String strRollbackInitiatedResultJson = executeJsonRpc(sfConnection, strRollbackToInitiateJson); RollbackInitiatedResult rollbackInitiatedResult = gson.fromJson(strRollbackInitiatedResultJson, RollbackInitiatedResult.class); verifyResult(rollbackInitiatedResult.result, strRollbackInitiatedResultJson, gson); } public static long createSolidFireClone(SolidFireConnection sfConnection, long lVolumeId, long lSnapshotId, long sfAccountId, String cloneName, Map<String, String> mapAttributes) { JsonObject cloneToCreate = new JsonObject(); cloneToCreate.addProperty("method", "CloneVolume"); JsonObject params = new JsonObject(); cloneToCreate.add("params", params); params.addProperty("volumeID", lVolumeId); if (lSnapshotId > 0) { params.addProperty("snapshotID", lSnapshotId); } params.addProperty("newAccountID", sfAccountId); params.addProperty("name", cloneName); if (mapAttributes != null && mapAttributes.size() > 0) { JsonObject attributes = new JsonObject(); params.add("attributes", attributes); Iterator<Map.Entry<String, String>> itr = mapAttributes.entrySet().iterator(); while (itr.hasNext()) { Map.Entry<String, String> pair = itr.next(); attributes.addProperty(pair.getKey(), pair.getValue()); } } final Gson gson = new GsonBuilder().create(); String strCloneToCreateJson = gson.toJson(cloneToCreate); String strCloneCreateResultJson = executeJsonRpc(sfConnection, strCloneToCreateJson); CloneCreateResult cloneCreateResult = gson.fromJson(strCloneCreateResultJson, CloneCreateResult.class); verifyResult(cloneCreateResult.result, strCloneCreateResultJson, gson); // Clone is an async operation. Poll until we get data. AsyncJobToPoll asyncJobToPoll = new AsyncJobToPoll(cloneCreateResult.result.asyncHandle); String strAsyncJobToPollJson = gson.toJson(asyncJobToPoll); do { String strAsyncJobResultJson = executeJsonRpc(sfConnection, strAsyncJobToPollJson); AsyncJobResult asyncJobResult = gson.fromJson(strAsyncJobResultJson, AsyncJobResult.class); verifyResult(asyncJobResult.result, strAsyncJobResultJson, gson); if (asyncJobResult.result.status.equals("complete")) { break; } try { Thread.sleep(500); // sleep for 1/2 of a second } catch (Exception ex) { // ignore } } while (true); return cloneCreateResult.result.volumeID; } public static long createSolidFireAccount(SolidFireConnection sfConnection, String strAccountName) { final Gson gson = new GsonBuilder().create(); AccountToAdd accountToAdd = new AccountToAdd(strAccountName); String strAccountAddJson = gson.toJson(accountToAdd); String strAccountAddResultJson = executeJsonRpc(sfConnection, strAccountAddJson); AccountAddResult accountAddResult = gson.fromJson(strAccountAddResultJson, AccountAddResult.class); verifyResult(accountAddResult.result, strAccountAddResultJson, gson); return accountAddResult.result.accountID; } public static SolidFireAccount getSolidFireAccountById(SolidFireConnection sfConnection, long lSfAccountId) { final Gson gson = new GsonBuilder().create(); AccountToGetById accountToGetById = new AccountToGetById(lSfAccountId); String strAccountToGetByIdJson = gson.toJson(accountToGetById); String strAccountGetByIdResultJson = executeJsonRpc(sfConnection, strAccountToGetByIdJson); 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(SolidFireConnection sfConnection, String strSfAccountName) { final Gson gson = new GsonBuilder().create(); AccountToGetByName accountToGetByName = new AccountToGetByName(strSfAccountName); String strAccountToGetByNameJson = gson.toJson(accountToGetByName); String strAccountGetByNameResultJson = executeJsonRpc(sfConnection, strAccountToGetByNameJson); 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 void deleteSolidFireAccount(SolidFireConnection sfConnection, long lAccountId) { final Gson gson = new GsonBuilder().create(); AccountToRemove accountToRemove = new AccountToRemove(lAccountId); String strAccountToRemoveJson = gson.toJson(accountToRemove); executeJsonRpc(sfConnection, strAccountToRemoveJson); } 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 (_id + _name).hashCode(); } @Override public String toString() { return _name; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!obj.getClass().equals(SolidFireAccount.class)) { 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 long createSolidFireVag(SolidFireConnection sfConnection, String strVagName, String[] iqns, long[] volumeIds) { final Gson gson = new GsonBuilder().create(); VagToCreate vagToCreate = new VagToCreate(strVagName, iqns, volumeIds); String strVagCreateJson = gson.toJson(vagToCreate); String strVagCreateResultJson = executeJsonRpc(sfConnection, strVagCreateJson); VagCreateResult vagCreateResult = gson.fromJson(strVagCreateResultJson, VagCreateResult.class); verifyResult(vagCreateResult.result, strVagCreateResultJson, gson); return vagCreateResult.result.volumeAccessGroupID; } public static void modifySolidFireVag(SolidFireConnection sfConnection, long lVagId, String[] iqns, long[] volumeIds) { final Gson gson = new GsonBuilder().create(); VagToModify vagToModify = new VagToModify(lVagId, iqns, volumeIds); String strVagModifyJson = gson.toJson(vagToModify); executeJsonRpc(sfConnection, strVagModifyJson); } public static SolidFireVag getSolidFireVag(SolidFireConnection sfConnection, long lVagId) { final Gson gson = new GsonBuilder().create(); VagToGet vagToGet = new VagToGet(lVagId); String strVagToGetJson = gson.toJson(vagToGet); String strVagGetResultJson = executeJsonRpc(sfConnection, strVagToGetJson); VagGetResult vagGetResult = gson.fromJson(strVagGetResultJson, VagGetResult.class); verifyResult(vagGetResult.result, strVagGetResultJson, gson); String[] vagIqns = getVagIqns(vagGetResult, lVagId); long[] vagVolumeIds = getVagVolumeIds(vagGetResult, lVagId); return new SolidFireVag(lVagId, vagIqns, vagVolumeIds); } public static List<SolidFireVag> getAllSolidFireVags(SolidFireConnection sfConnection) { final Gson gson = new GsonBuilder().create(); AllVags allVags = new AllVags(); String strAllVagsJson = gson.toJson(allVags); String strAllVagsGetResultJson = executeJsonRpc(sfConnection, strAllVagsJson); VagGetResult allVagsGetResult = gson.fromJson(strAllVagsGetResultJson, VagGetResult.class); verifyResult(allVagsGetResult.result, strAllVagsGetResultJson, gson); List<SolidFireVag> lstSolidFireVags = new ArrayList<SolidFireVag>(); if (allVagsGetResult.result.volumeAccessGroups != null) { for (VagGetResult.Result.Vag vag : allVagsGetResult.result.volumeAccessGroups) { SolidFireVag sfVag = new SolidFireVag(vag.volumeAccessGroupID, vag.initiators, vag.volumes); lstSolidFireVags.add(sfVag); } } return lstSolidFireVags; } public static void deleteSolidFireVag(SolidFireConnection sfConnection, long lVagId) { final Gson gson = new GsonBuilder().create(); VagToDelete vagToDelete = new VagToDelete(lVagId); String strVagToDeleteJson = gson.toJson(vagToDelete); executeJsonRpc(sfConnection, strVagToDeleteJson); } public static class SolidFireVag { private final long _id; private final String[] _initiators; private final long[] _volumeIds; public SolidFireVag(long id, String[] initiators, long[] volumeIds) { _id = id; _initiators = initiators; _volumeIds = volumeIds; } public long getId() { return _id; } public String[] getInitiators() { return _initiators; } public long[] getVolumeIds() { return _volumeIds; } @Override public int hashCode() { return String.valueOf(_id).hashCode(); } @Override public String toString() { return String.valueOf(_id); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!obj.getClass().equals(SolidFireVag.class)) { return false; } SolidFireVag sfvag = (SolidFireVag) obj; if (_id == sfvag._id) { return true; } return false; } } @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 ListDeletedVolumes { private final String method = "ListDeletedVolumes"; } @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 final long volumeID; private VolumeToDeleteParams(final long lVolumeId) { volumeID = lVolumeId; } } } @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 final long volumeID; private VolumeToPurgeParams(final long lVolumeId) { volumeID = lVolumeId; } } } @SuppressWarnings("unused") private static final class SnapshotsToGet { private final String method = "ListSnapshots"; private final SnapshotsToGetParams params; private SnapshotsToGet(final long lVolumeId) { params = new SnapshotsToGetParams(lVolumeId); } private static final class SnapshotsToGetParams { private final long volumeID; private SnapshotsToGetParams(final long lVolumeId) { volumeID = lVolumeId; } } } @SuppressWarnings("unused") private static final class SnapshotToDelete { private final String method = "DeleteSnapshot"; private final SnapshotToDeleteParams params; private SnapshotToDelete(final long lSnapshotId) { params = new SnapshotToDeleteParams(lSnapshotId); } private static final class SnapshotToDeleteParams { private final long snapshotID; private SnapshotToDeleteParams(final long lSnapshotId) { snapshotID = lSnapshotId; } } } @SuppressWarnings("unused") private static final class RollbackToInitiate { private final String method = "RollbackToSnapshot"; private final RollbackToInitiateParams params; private RollbackToInitiate(final long lVolumeId, final long lSnapshotId) { params = new RollbackToInitiateParams(lVolumeId, lSnapshotId); } private static final class RollbackToInitiateParams { private final long volumeID; private final long snapshotID; private RollbackToInitiateParams(final long lVolumeId, final long lSnapshotId) { volumeID = lVolumeId; snapshotID = lSnapshotId; } } } @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 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 = "GetAccountByName"; 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 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 final long accountID; private AccountToRemoveParams(final long lAccountId) { accountID = lAccountId; } } } @SuppressWarnings("unused") private static final class VagToCreate { private final String method = "CreateVolumeAccessGroup"; private final VagToCreateParams params; private VagToCreate(final String strVagName, final String[] iqns, final long[] volumeIds) { params = new VagToCreateParams(strVagName, iqns, volumeIds); } private static final class VagToCreateParams { private final String name; private final String[] initiators; private final long[] volumes; private VagToCreateParams(final String strVagName, final String[] iqns, final long[] volumeIds) { name = strVagName; initiators = iqns; volumes = volumeIds; } } } @SuppressWarnings("unused") private static final class VagToModify { private final String method = "ModifyVolumeAccessGroup"; private final VagToModifyParams params; private VagToModify(final long lVagName, final String[] iqns, final long[] volumeIds) { params = new VagToModifyParams(lVagName, iqns, volumeIds); } private static final class VagToModifyParams { private final long volumeAccessGroupID; private final String[] initiators; private final long[] volumes; private VagToModifyParams(final long lVagName, final String[] iqns, final long[] volumeIds) { volumeAccessGroupID = lVagName; initiators = iqns; volumes = volumeIds; } } } @SuppressWarnings("unused") private static final class VagToGet { private final String method = "ListVolumeAccessGroups"; private final VagToGetParams params; private VagToGet(final long lVagId) { params = new VagToGetParams(lVagId); } private static final class VagToGetParams { private final long startVolumeAccessGroupID; private final long limit = 1; private VagToGetParams(final long lVagId) { startVolumeAccessGroupID = lVagId; } } } @SuppressWarnings("unused") private static final class AllVags { private final String method = "ListVolumeAccessGroups"; private final VagToGetParams params; private AllVags() { params = new VagToGetParams(); } private static final class VagToGetParams { } } @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 final long volumeAccessGroupID; private VagToDeleteParams(final long lVagId) { volumeAccessGroupID = lVagId; } } } @SuppressWarnings("unused") private static final class AsyncJobToPoll { private final String method = "GetAsyncResult"; private final AsyncJobToPollParams params; private AsyncJobToPoll(final long asyncHandle) { params = new AsyncJobToPollParams(asyncHandle); } private static final class AsyncJobToPollParams { private final long asyncHandle; private AsyncJobToPollParams(final long asyncHandle) { this.asyncHandle = asyncHandle; } } } 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 boolean enable512e; private Qos qos; private long totalSize; private static final class Qos { private long minIOPS; private long maxIOPS; private long burstIOPS; } } } } private static final class SnapshotCreateResult { private Result result; private static final class Result { private long snapshotID; } } private static final class SnapshotsGetResult { private Result result; private static final class Result { private Snapshot[] snapshots; private static final class Snapshot { private long snapshotID; private String name; } } } @SuppressWarnings("unused") private static final class RollbackInitiatedResult { private Result result; private static final class Result { private long snapshotID; } } private static final class CloneCreateResult { private Result result; private static final class Result { private long volumeID; private long asyncHandle; } } 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 VagGetResult { private Result result; private static final class Result { private Vag[] volumeAccessGroups; private static final class Vag { private long volumeAccessGroupID; private String[] initiators; private long[] volumes; } } } private static final class AsyncJobResult { private AsyncResult result; private static final class AsyncResult { private String status; } } private static final class JsonError { private Error error; private static final class Error { private String message; } } private static DefaultHttpClient getHttpClient(int iPort) { try { SSLContext sslContext = SSLUtils.getSSLContext(); X509TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } @Override 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()); } catch (NoSuchAlgorithmException ex) { throw new CloudRuntimeException(ex.getMessage()); } catch (KeyManagementException ex) { throw new CloudRuntimeException(ex.getMessage()); } } private static String executeJsonRpc(SolidFireConnection sfConnection, String strJsonToExecute) { DefaultHttpClient httpClient = null; StringBuilder sb = new StringBuilder(); try { StringEntity input = new StringEntity(strJsonToExecute); input.setContentType("application/json"); httpClient = getHttpClient(sfConnection.getManagementPort()); URI uri = new URI("https://" + sfConnection.getManagementVip() + ":" + sfConnection.getManagementPort() + "/json-rpc/6.0"); AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_SCHEME); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials( sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword()); 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 CloudRuntimeException("Failed on JSON-RPC API call. HTTP error code = " + response.getStatusLine().getStatusCode()); } try (BufferedReader br = new BufferedReader( new InputStreamReader(response.getEntity().getContent()));) { String strOutput; while ((strOutput = br.readLine()) != null) { sb.append(strOutput); } } catch (IOException ex) { throw new CloudRuntimeException(ex.getMessage()); } } catch (UnsupportedEncodingException ex) { throw new CloudRuntimeException(ex.getMessage()); } catch (ClientProtocolException ex) { throw new CloudRuntimeException(ex.getMessage()); } catch (IOException ex) { throw new CloudRuntimeException(ex.getMessage()); } catch (URISyntaxException ex) { throw new CloudRuntimeException(ex.getMessage()); } finally { if (httpClient != null) { try { httpClient.getConnectionManager().shutdown(); } catch (Exception t) { s_logger.info("[ignored]" + "error shutting down http client: " + t.getLocalizedMessage()); } } } return sb.toString(); } private static boolean isSuccess(int iCode) { return iCode >= 200 && iCode < 300; } private static void verifyResult(Object result, String strJson, Gson gson) throws IllegalStateException { if (result != 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) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) { return volumeGetResult.result.volumes[0].name; } throw new CloudRuntimeException( "Could not determine the name of the volume for volume ID of " + lVolumeId + "."); } private static String getVolumeIqn(VolumeGetResult volumeGetResult, long lVolumeId) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) { return volumeGetResult.result.volumes[0].iqn; } throw new CloudRuntimeException( "Could not determine the IQN of the volume for volume ID of " + lVolumeId + "."); } private static long getVolumeAccountId(VolumeGetResult volumeGetResult, long lVolumeId) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) { return volumeGetResult.result.volumes[0].accountID; } throw new CloudRuntimeException( "Could not determine the account ID of the volume for volume ID of " + lVolumeId + "."); } private static String getVolumeStatus(VolumeGetResult volumeGetResult, long lVolumeId) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) { return volumeGetResult.result.volumes[0].status; } throw new CloudRuntimeException( "Could not determine the status of the volume for volume ID of " + lVolumeId + "."); } private static boolean getVolumeEnable512e(VolumeGetResult volumeGetResult, long lVolumeId) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) { return volumeGetResult.result.volumes[0].enable512e; } throw new CloudRuntimeException( "Could not determine the enable 512 emulation of the volume for volume ID of " + lVolumeId + "."); } private static long getVolumeMinIops(VolumeGetResult volumeGetResult, long lVolumeId) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId && volumeGetResult.result.volumes[0].qos != null) { return volumeGetResult.result.volumes[0].qos.minIOPS; } throw new CloudRuntimeException( "Could not determine the min IOPS of the volume for volume ID of " + lVolumeId + "."); } private static long getVolumeMaxIops(VolumeGetResult volumeGetResult, long lVolumeId) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId && volumeGetResult.result.volumes[0].qos != null) { return volumeGetResult.result.volumes[0].qos.maxIOPS; } throw new CloudRuntimeException( "Could not determine the max IOPS of the volume for volume ID of " + lVolumeId + "."); } private static long getVolumeBurstIops(VolumeGetResult volumeGetResult, long lVolumeId) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId && volumeGetResult.result.volumes[0].qos != null) { return volumeGetResult.result.volumes[0].qos.burstIOPS; } throw new CloudRuntimeException( "Could not determine the burst IOPS of the volume for volume ID of " + lVolumeId + "."); } private static long getVolumeTotalSize(VolumeGetResult volumeGetResult, long lVolumeId) { if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && volumeGetResult.result.volumes[0].volumeID == lVolumeId) { return volumeGetResult.result.volumes[0].totalSize; } throw new CloudRuntimeException( "Could not determine the total size of the volume for volume ID of " + lVolumeId + "."); } private static String[] getVagIqns(VagGetResult vagGetResult, long lVagId) { if (vagGetResult.result.volumeAccessGroups != null && vagGetResult.result.volumeAccessGroups.length == 1 && vagGetResult.result.volumeAccessGroups[0].volumeAccessGroupID == lVagId) { return vagGetResult.result.volumeAccessGroups[0].initiators; } throw new CloudRuntimeException( "Could not determine the IQNs of the volume access group for volume access group ID of " + lVagId + "."); } private static long[] getVagVolumeIds(VagGetResult vagGetResult, long lVagId) { if (vagGetResult.result.volumeAccessGroups != null && vagGetResult.result.volumeAccessGroups.length == 1 && vagGetResult.result.volumeAccessGroups[0].volumeAccessGroupID == lVagId) { return vagGetResult.result.volumeAccessGroups[0].volumes; } throw new CloudRuntimeException( "Could not determine the volume IDs of the volume access group for volume access group ID of " + lVagId + "."); } // used to parse the "url" parameter when creating primary storage that's based on the SolidFire plug-in with the // name SolidFireUtil.PROVIDER_NAME (as opposed to the SolidFire plug-in with the name SolidFireUtil.SHARED_PROVIDER_NAME) // return a String instance that contains at most the MVIP and SVIP info public static String getModifiedUrl(String originalUrl) { StringBuilder sb = new StringBuilder(); String delimiter = ";"; StringTokenizer st = new StringTokenizer(originalUrl, delimiter); while (st.hasMoreElements()) { String token = st.nextElement().toString().toUpperCase(); if (token.startsWith(SolidFireUtil.MANAGEMENT_VIP.toUpperCase()) || token.startsWith(SolidFireUtil.STORAGE_VIP.toUpperCase())) { sb.append(token).append(delimiter); } } String modifiedUrl = sb.toString(); int lastIndexOf = modifiedUrl.lastIndexOf(delimiter); if (lastIndexOf == (modifiedUrl.length() - delimiter.length())) { return modifiedUrl.substring(0, lastIndexOf); } return modifiedUrl; } public static String getManagementVip(String url) { return getVip(SolidFireUtil.MANAGEMENT_VIP, url); } public static String getStorageVip(String url) { return getVip(SolidFireUtil.STORAGE_VIP, url); } public static int getManagementPort(String url) { return getPort(SolidFireUtil.MANAGEMENT_VIP, url, DEFAULT_MANAGEMENT_PORT); } public static int getStoragePort(String url) { return getPort(SolidFireUtil.STORAGE_VIP, url, DEFAULT_STORAGE_PORT); } private static String getVip(String keyToMatch, String url) { String delimiter = ":"; String storageVip = getValue(keyToMatch, url); int index = storageVip.indexOf(delimiter); if (index != -1) { return storageVip.substring(0, index); } return storageVip; } private static int getPort(String keyToMatch, String url, int defaultPortNumber) { String delimiter = ":"; String storageVip = getValue(keyToMatch, url); int index = storageVip.indexOf(delimiter); int portNumber = defaultPortNumber; if (index != -1) { String port = storageVip.substring(index + delimiter.length()); try { portNumber = Integer.parseInt(port); } catch (NumberFormatException ex) { throw new IllegalArgumentException("Invalid URL format (port is not an integer)"); } } return portNumber; } public static String getValue(String keyToMatch, String url) { return getValue(keyToMatch, url, true); } public static String getValue(String keyToMatch, String url, boolean throwExceptionIfNotFound) { String delimiter1 = ";"; String delimiter2 = "="; StringTokenizer st = new StringTokenizer(url, delimiter1); while (st.hasMoreElements()) { String token = st.nextElement().toString(); int index = token.indexOf(delimiter2); if (index == -1) { throw new RuntimeException("Invalid URL format"); } String key = token.substring(0, index); if (key.equalsIgnoreCase(keyToMatch)) { String valueToReturn = token.substring(index + delimiter2.length()); return valueToReturn; } } if (throwExceptionIfNotFound) { throw new RuntimeException("Key not found in URL"); } return null; } }