From 8dd6cef9a6d395a2b8d876e358077bd150cdc9f1 Mon Sep 17 00:00:00 2001 From: Sid Kattoju <45833770+skattoju4@users.noreply.github.com> Date: Tue, 2 Jun 2020 14:58:20 -0400 Subject: [PATCH] create Volume Access Groups per cluster instead of CloudStack-RandomUUID() (#3794) * create vags per cluster * vagname in solidfire utils vag object * fix string compare * refactor to make use of existing map * fix typos * rebuild vag to iqn map after creating cluster vag * refactor loop using java 8 stream api * update null entry in vag to iqn map * remove null vag to iqn mapping when creating cluster id vag * add initiator to sf vag when adding hosts * use cluster uuid instead of cluster id and refactor * update null entry in vagtoiqnmap * update sfvag list after creating new vag * pass clusterDao to handleVagForHost * check if initiator is not already added to the vag * factor logic into methods * fix typo and camel case * fix listing clusters by zone id Co-authored-by: Sid Kattoju --- .../SolidFirePrimaryDataStoreDriver.java | 4 +- ...idFireSharedPrimaryDataStoreLifeCycle.java | 4 +- .../storage/datastore/util/SolidFireUtil.java | 104 +++++++++++++----- 3 files changed, 83 insertions(+), 29 deletions(-) diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java index aa277cd4dab..22e4e952b3e 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java @@ -172,9 +172,11 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { try { List hosts = hostDao.findByClusterId(clusterId); + String clusterUuId = clusterDao.findById(clusterId).getUuid(); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolumeId, hosts); + SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolumeId, hosts, clusterUuId); return true; } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java index 2ebd69a2d93..9cc746d4ee8 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java @@ -285,7 +285,9 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor // place the newly created volume in the Volume Access Group List hosts = hostDao.findByClusterId(clusterId); - SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolume.getId(), hosts); + String clusterUuId = clusterDao.findById(clusterId).getUuid(); + + SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolume.getId(), hosts, clusterUuId); SolidFireUtil.SolidFireAccount sfAccount = sfCreateVolume.getAccount(); Account csAccount = CallContext.current().getCallingAccount(); diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index 9f7b2050a5b..acf1d5c7b15 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -393,7 +393,7 @@ public class SolidFireUtil { } } else { - List clustersInZone = clusterDao.listByZoneId(storagePoolVO.getDataCenterId()); + List clustersInZone = clusterDao.listClustersByDcId(storagePoolVO.getDataCenterId()); if (clustersInZone != null) { for (ClusterVO clusterInZone : clustersInZone) { @@ -497,7 +497,7 @@ public class SolidFireUtil { if (sfVag != null) { placeVolumeIdsInVag(sfConnection, sfVags, sfVag, hostVO, hostDao); } else { - handleVagForHost(sfConnection, sfVags, hostVO, hostDao); + handleVagForHost(sfConnection, sfVags, hostVO, hostDao, clusterDao); } } } @@ -513,12 +513,14 @@ public class SolidFireUtil { // creating a new VAG won't exceed 4 VAGs for the computer cluster). // If none of the hosts in the cluster are in a VAG, then leave this host out of a VAG. // Place applicable volume IDs in VAG, if need be (account of volume starts with SF_CS_ACCOUNT_PREFIX). - private static void handleVagForHost(SolidFireUtil.SolidFireConnection sfConnection, List sfVags, Host host, HostDao hostDao) { + private static void handleVagForHost(SolidFireUtil.SolidFireConnection sfConnection, List sfVags, Host host, HostDao hostDao, ClusterDao clusterDao) { List hostVOs = hostDao.findByClusterId(host.getClusterId()); if (hostVOs != null) { int numVags = 0; + addInitiatorsToExistingVag(clusterDao, host, sfVags, sfConnection); + Collections.shuffle(hostVOs, RANDOM); for (HostVO hostVO : hostVOs) { @@ -544,8 +546,9 @@ public class SolidFireUtil { throw new CloudRuntimeException(errMsg); } - addInitiatorsToSolidFireVag(sfConnection, sfVag.getId(), new String[] { host.getStorageUrl() }); - + if(!isInitiatorInSfVag(host.getStorageUrl(),sfVag)) { + addInitiatorsToSolidFireVag(sfConnection, sfVag.getId(), new String[]{host.getStorageUrl()}); + } return; } } @@ -571,6 +574,14 @@ public class SolidFireUtil { } } + private static void addInitiatorsToExistingVag(ClusterDao clusterDao, Host host, List sfVags, SolidFireUtil.SolidFireConnection sfConnection){ + String clusterUuId = clusterDao.findById(host.getClusterId()).getUuid(); + SolidFireVag sfVagMatchingClusterId = sfVags.stream().filter(vag -> vag.getName().equals("CloudStack-"+clusterUuId)).findFirst().orElse(null); + if (sfVagMatchingClusterId != null && sfVagMatchingClusterId.getInitiators().length < MAX_NUM_INITIATORS_PER_VAG) { + addInitiatorsToSolidFireVag(sfConnection, sfVagMatchingClusterId.getId(), new String[]{host.getStorageUrl()}); + } + } + /** * Make use of the volume access group (VAG) of a random host in the cluster. With this VAG, collect all of its volume IDs that are for * volumes that are in SolidFire accounts that are for CloudStack. @@ -683,7 +694,7 @@ public class SolidFireUtil { return null; } - public static void placeVolumeInVolumeAccessGroups(SolidFireConnection sfConnection, long sfVolumeId, List hosts) { + public static void placeVolumeInVolumeAccessGroups(SolidFireConnection sfConnection, long sfVolumeId, List hosts, String clusterUuId) { if (!SolidFireUtil.hostsSupport_iScsi(hosts)) { String errMsg = "Not all hosts in the compute cluster support iSCSI."; @@ -691,11 +702,50 @@ public class SolidFireUtil { throw new CloudRuntimeException(errMsg); } - List sfVags = SolidFireUtil.getAllVags(sfConnection); + Map> sfVagToIqnsMap = buildVagToIQNMap(hosts, sfVags); + if (sfVagToIqnsMap.size() > MAX_NUM_VAGS_PER_VOLUME) { + throw new CloudRuntimeException("A SolidFire volume can be in at most four volume access groups simultaneously."); + } + if (sfVagToIqnsMap.containsKey(null)) { + sfVagToIqnsMap = updateNullKeyInSfVagToIqnsMap(sfVagToIqnsMap, sfVags, sfConnection, clusterUuId, sfVolumeId); + } + addVolumestoVagIfNotPresent(sfVagToIqnsMap.keySet(), sfVolumeId, sfConnection); + } + + private static Map> updateNullKeyInSfVagToIqnsMap(Map> sfVagToIqnsMap, List sfVags, SolidFireConnection sfConnection, String clusterUuId, long sfVolumeId){ + SolidFireUtil.SolidFireVag sfVagMatchingClusterId = createClusterVagIfDoesntExist(sfVags, sfConnection, clusterUuId, sfVagToIqnsMap, sfVolumeId); + sfVagToIqnsMap.put(sfVagMatchingClusterId, sfVagToIqnsMap.get(null)); + sfVagToIqnsMap.remove(null); + return sfVagToIqnsMap; + } + + private static SolidFireVag createClusterVagIfDoesntExist(List sfVags, SolidFireConnection sfConnection, String clusterUuId, Map> sfVagToIqnsMap, long sfVolumeId) { + SolidFireVag sfVagMatchingClusterId = sfVags.stream().filter(vag -> vag.getName().equals("CloudStack-" + clusterUuId)).findFirst().orElse(null); + if (sfVagMatchingClusterId == null) { + LOGGER.info("Creating volume access group CloudStack-" + clusterUuId); + SolidFireUtil.createVag(sfConnection, "CloudStack-" + clusterUuId, sfVagToIqnsMap.get(null).toArray(new String[0]), new long[]{sfVolumeId}); + sfVags = SolidFireUtil.getAllVags(sfConnection); + return sfVags.stream().filter(vag -> vag.getName().equals("CloudStack-" + clusterUuId)).findFirst().orElse(null); + }else{ + return sfVagMatchingClusterId; + } + } + + private static void addVolumestoVagIfNotPresent(Set sfVagSet, long sfVolumeId, SolidFireConnection sfConnection){ + for (SolidFireUtil.SolidFireVag sfVag : sfVagSet) { + if (sfVag != null) { + if (!SolidFireUtil.isVolumeIdInSfVag(sfVolumeId, sfVag)) { + SolidFireUtil.addVolumeIdsToSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId }); + } + } + } + } + + + private static Map> buildVagToIQNMap(List hosts, List sfVags) { Map> sfVagToIqnsMap = new HashMap<>(); - for (HostVO hostVO : hosts) { String iqn = hostVO.getStorageUrl(); @@ -705,24 +755,8 @@ public class SolidFireUtil { iqnsInVag.add(iqn); } + return sfVagToIqnsMap; - if (sfVagToIqnsMap.size() > MAX_NUM_VAGS_PER_VOLUME) { - throw new CloudRuntimeException("A SolidFire volume can be in at most four volume access groups simultaneously."); - } - - for (SolidFireUtil.SolidFireVag sfVag : sfVagToIqnsMap.keySet()) { - if (sfVag != null) { - if (!SolidFireUtil.isVolumeIdInSfVag(sfVolumeId, sfVag)) { - SolidFireUtil.addVolumeIdsToSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId }); - } - } - else { - List iqnsNotInVag = sfVagToIqnsMap.get(null); - - SolidFireUtil.createVag(sfConnection, "CloudStack-" + UUID.randomUUID().toString(), - iqnsNotInVag.toArray(new String[0]), new long[] { sfVolumeId }); - } - } } public static SolidFireUtil.SolidFireVag getVolumeAccessGroup(String hostIqn, List sfVags) { @@ -777,6 +811,18 @@ public class SolidFireUtil { return false; } + private static boolean isInitiatorInSfVag(String initiatorName, SolidFireUtil.SolidFireVag sfVag) { + String[] initiatorsList = sfVag.getInitiators(); + + for (String initiator : initiatorsList) { + if (initiatorName.equals(initiator)) { + return true; + } + } + + return false; + } + private static boolean hostSupports_iScsi(Host host) { return host != null && host.getStorageUrl() != null && host.getStorageUrl().trim().length() > 0 && host.getStorageUrl().startsWith("iqn"); } @@ -1245,7 +1291,7 @@ public class SolidFireUtil { if (vags != null) { for (VolumeAccessGroup vag : vags) { - SolidFireVag sfVag = new SolidFireVag(vag.getVolumeAccessGroupID(), vag.getInitiators(), toPrimitive(vag.getVolumes())); + SolidFireVag sfVag = new SolidFireVag(vag.getVolumeAccessGroupID(), vag.getInitiators(), toPrimitive(vag.getVolumes()), vag.getName()); lstSolidFireVags.add(sfVag); } @@ -1258,11 +1304,13 @@ public class SolidFireUtil { private final long _id; private final String[] _initiators; private final long[] _volumeIds; + private final String _vagName; - SolidFireVag(long id, String[] initiators, long[] volumeIds) { + SolidFireVag(long id, String[] initiators, long[] volumeIds, String name) { _id = id; _initiators = initiators; _volumeIds = volumeIds; + _vagName = name; } public long getId() { @@ -1277,6 +1325,8 @@ public class SolidFireUtil { return _volumeIds; } + public String getName() { return _vagName; } + @Override public int hashCode() { return String.valueOf(_id).hashCode();