From 2efe61ddbc57316348bc0fa328a09fe4dadb08d2 Mon Sep 17 00:00:00 2001 From: Mike Tutkowski Date: Tue, 7 Jan 2014 22:52:11 -0700 Subject: [PATCH] CLOUDSTACK-4810: Enable hypervisor snapshots for CloudStack-managed storage (for XenServer and VMware) --- .../SolidfirePrimaryDataStoreDriver.java | 108 +++++++++++++++--- .../storage/datastore/util/SolidFireUtil.java | 54 +++++++-- 2 files changed, 141 insertions(+), 21 deletions(-) diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java index ff3e33b9425..0bf9c5082ff 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java @@ -133,18 +133,15 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return new SolidFireConnection(mVip, mPort, clusterAdminUsername, clusterAdminPassword); } - private SolidFireUtil.SolidFireAccount createSolidFireAccount(String sfAccountName, - SolidFireConnection sfConnection) { + private SolidFireUtil.SolidFireAccount createSolidFireAccount(String sfAccountName, SolidFireConnection sfConnection) { String mVip = sfConnection.getManagementVip(); int mPort = sfConnection.getManagementPort(); String clusterAdminUsername = sfConnection.getClusterAdminUsername(); String clusterAdminPassword = sfConnection.getClusterAdminPassword(); - long accountNumber = SolidFireUtil.createSolidFireAccount(mVip, mPort, - clusterAdminUsername, clusterAdminPassword, sfAccountName); + long accountNumber = SolidFireUtil.createSolidFireAccount(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountName); - return SolidFireUtil.getSolidFireAccountById(mVip, mPort, - clusterAdminUsername, clusterAdminPassword, accountNumber); + return SolidFireUtil.getSolidFireAccountById(mVip, mPort, clusterAdminUsername, clusterAdminPassword, accountNumber); } private void updateCsDbWithAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount) { @@ -185,8 +182,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { private final String _targetUsername; private final String _targetSecret; - public ChapInfoImpl(String initiatorUsername, String initiatorSecret, - String targetUsername, String targetSecret) { + public ChapInfoImpl(String initiatorUsername, String initiatorSecret, String targetUsername, String targetSecret) { _initiatorUsername = initiatorUsername; _initiatorSecret = initiatorSecret; _targetUsername = targetUsername; @@ -269,16 +265,39 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), Long.parseLong(vagId)); + String[] hostIqns = getNewHostIqns(sfVag.getInitiators(), getIqnsFromHosts(hosts)); long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(), - getIqnsFromHosts(hosts), volumeIds); + hostIqns, volumeIds); } else { - long lVagId = SolidFireUtil.createSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), - sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), "CloudStack-" + UUID.randomUUID().toString(), - getIqnsFromHosts(hosts), new long[] { sfVolumeId }); + long lVagId; + + try { + lVagId = SolidFireUtil.createSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), "CloudStack-" + UUID.randomUUID().toString(), + 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(hosts, sfConnection); + + long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); + + SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(), + sfVag.getInitiators(), volumeIds); + + lVagId = sfVag.getId(); + } clusterDetail = new ClusterDetailsVO(clusterId, getVagKey(storagePoolId), String.valueOf(lVagId)); @@ -288,6 +307,48 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return true; } + // this method takes in a collection of hosts and tries to find an existing VAG that has all three 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 SolidFireUtil.SolidFireVag getCompatibleVag(List hosts, SolidFireConnection sfConnection) { + List sfVags = SolidFireUtil.getAllSolidFireVags(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword()); + + if (sfVags != null) { + List hostIqns = new ArrayList(); + + // 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 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 List getStringArrayAsLowerCaseStringList(String[] aString) { + List lstLowerCaseString = new ArrayList(); + + if (aString != null) { + for (String str : aString) { + if (str != null) { + lstLowerCaseString.add(str.toLowerCase()); + } + } + } + + return lstLowerCaseString; + } + // get the VAG associated with volumeInfo's cluster, if any (ListVolumeAccessGroups) // might not exist if using CHAP // if the VAG exists // remove the ID of volumeInfo from the VAG (ModifyVolumeAccessGroup) @@ -314,11 +375,12 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), Long.parseLong(vagId)); + String[] hostIqns = getNewHostIqns(sfVag.getInitiators(), getIqnsFromHosts(hosts)); long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, false); SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(), - getIqnsFromHosts(hosts), volumeIds); + hostIqns, volumeIds); } } @@ -336,6 +398,26 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return true; } + private String[] getNewHostIqns(String[] currentIqns, String[] newIqns) { + List lstIqns = new ArrayList(); + + if (currentIqns != null) { + for (String currentIqn : currentIqns) { + lstIqns.add(currentIqn); + } + } + + if (newIqns != null) { + for (String newIqn : newIqns) { + if (!lstIqns.contains(newIqn)) { + lstIqns.add(newIqn); + } + } + } + + return lstIqns.toArray(new String[0]); + } + private long[] getNewVolumeIds(long[] volumeIds, long volumeIdToAddOrRemove, boolean add) { if (add) { return getNewVolumeIdsAdd(volumeIds, volumeIdToAddOrRemove); 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 index a3724977b8a..2d528ef683f 100644 --- 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 @@ -276,8 +276,7 @@ public class SolidFireUtil } } - public static long createSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, - String strAccountName) + public static long createSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strAccountName) { final Gson gson = new GsonBuilder().create(); @@ -294,8 +293,7 @@ public class SolidFireUtil return accountAddResult.result.accountID; } - public static SolidFireAccount getSolidFireAccountById(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, - long lSfAccountId) + public static SolidFireAccount getSolidFireAccountById(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lSfAccountId) { final Gson gson = new GsonBuilder().create(); @@ -316,8 +314,7 @@ public class SolidFireUtil return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret); } - public static SolidFireAccount getSolidFireAccountByName(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, - String strSfAccountName) + public static SolidFireAccount getSolidFireAccountByName(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strSfAccountName) { final Gson gson = new GsonBuilder().create(); @@ -338,8 +335,7 @@ public class SolidFireUtil return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret); } - public static void deleteSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, - long lAccountId) + public static void deleteSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lAccountId) { final Gson gson = new GsonBuilder().create(); @@ -467,6 +463,33 @@ public class SolidFireUtil return new SolidFireVag(lVagId, vagIqns, vagVolumeIds); } + public static List getAllSolidFireVags(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword) + { + final Gson gson = new GsonBuilder().create(); + + AllVags allVags = new AllVags(); + + String strAllVagsJson = gson.toJson(allVags); + + String strAllVagsGetResultJson = executeJsonRpc(strAllVagsJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); + + VagGetResult allVagsGetResult = gson.fromJson(strAllVagsGetResultJson, VagGetResult.class); + + verifyResult(allVagsGetResult.result, strAllVagsGetResultJson, gson); + + List lstSolidFireVags = new ArrayList(); + + 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(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId) { final Gson gson = new GsonBuilder().create(); @@ -854,6 +877,21 @@ public class SolidFireUtil } } + @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 {