You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by mt...@apache.org on 2015/10/14 20:09:45 UTC

[5/7] git commit: updated refs/heads/sf-plugins-a to 49ca3b4

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/49ca3b40/plugins/api/solidfire/src/org/apache/cloudstack/solidfire/SolidFireManagerImpl.java
----------------------------------------------------------------------
diff --git a/plugins/api/solidfire/src/org/apache/cloudstack/solidfire/SolidFireManagerImpl.java b/plugins/api/solidfire/src/org/apache/cloudstack/solidfire/SolidFireManagerImpl.java
new file mode 100644
index 0000000..e02a9c5
--- /dev/null
+++ b/plugins/api/solidfire/src/org/apache/cloudstack/solidfire/SolidFireManagerImpl.java
@@ -0,0 +1,977 @@
+// 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.solidfire;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+import org.apache.cloudstack.dataaccess.dao.solidfire.SfClusterDao;
+import org.apache.cloudstack.dataaccess.dao.solidfire.SfVirtualNetworkDao;
+import org.apache.cloudstack.dataaccess.dao.solidfire.SfVolumeDao;
+import org.apache.cloudstack.dataaccess.vo.solidfire.SfClusterVO;
+import org.apache.cloudstack.dataaccess.vo.solidfire.SfVirtualNetworkVO;
+import org.apache.cloudstack.dataaccess.vo.solidfire.SfVolumeVO;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
+import org.apache.cloudstack.util.solidfire.SfUtil;
+import org.apache.cloudstack.util.solidfire.SolidFireConnection;
+import org.springframework.stereotype.Component;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.user.Account;
+import com.cloud.user.AccountDetailVO;
+import com.cloud.user.AccountDetailsDao;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.db.GlobalLock;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@Component
+public class SolidFireManagerImpl implements SolidFireManager {
+    private static final Logger s_logger = Logger.getLogger(SolidFireManagerImpl.class);
+    private static final int s_lockTimeInSeconds = 180;
+
+    private static final ConfigKey<Long> s_sfTotalAccountCapacity =
+            new ConfigKey<>(
+                    "Advanced",
+                    Long.class,
+                    "sf.total.capacity",
+                    "0",
+                    "Total capacity the account can draw from any and all SolidFire clusters (in GBs)",
+                    true, ConfigKey.Scope.Account);
+
+    private static final ConfigKey<Long> s_sfTotalAccountMinIops =
+            new ConfigKey<>(
+                    "Advanced",
+                    Long.class,
+                    "sf.total.min.iops",
+                    "0",
+                    "Total minimum IOPS the account can draw from any and all SolidFire clusters",
+                    true, ConfigKey.Scope.Account);
+
+    private static final ConfigKey<Long> s_sfTotalAccountMaxIops =
+            new ConfigKey<>(
+                    "Advanced",
+                    Long.class,
+                    "sf.total.max.iops",
+                    "0",
+                    "Total maximum IOPS the account can draw from any and all SolidFire clusters",
+                    true, ConfigKey.Scope.Account);
+
+    private static final ConfigKey<Long> s_sfTotalAccountBurstIops =
+            new ConfigKey<>(
+                    "Advanced",
+                    Long.class,
+                    "sf.total.burst.iops",
+                    "0",
+                    "Total burst IOPS the account can draw from any and all SolidFire clusters",
+                    true, ConfigKey.Scope.Account);
+
+    @Inject private AccountDao _accountDao;
+    @Inject private AccountDetailsDao _accountDetailsDao;
+    @Inject private DataCenterDao _zoneDao;
+    @Inject private SfClusterDao _sfClusterDao;
+    @Inject private SfVirtualNetworkDao _sfVirtualNetworkDao;
+    @Inject private SfVolumeDao _sfVolumeDao;
+    @Inject private SfUtil _sfUtil;
+
+    private SolidFireManagerImpl() {
+    }
+
+    @Override
+    public SfCluster listSolidFireCluster(String clusterName) {
+        s_logger.info("listSolidFireCluster invoked");
+
+        verifyRootAdmin();
+
+        return getSfCluster(clusterName);
+    }
+
+    @Override
+    public List<SfCluster> listSolidFireClusters() {
+        s_logger.info("listSolidFireClusters invoked");
+
+        verifyRootAdmin();
+
+        List<SfCluster> sfClusters = new ArrayList<>();
+
+        List<SfClusterVO> sfClusterVOs = _sfClusterDao.listAll();
+
+        if (sfClusterVOs != null) {
+            sfClusters.addAll(sfClusterVOs);
+        }
+
+        return sfClusters;
+    }
+
+    @Override
+    public SfCluster createReferenceToSolidFireCluster(String mvip, String username, String password, long totalCapacity,
+            long totalMinIops, long totalMaxIops, long totalBurstIops, long zoneId) {
+        s_logger.info("createReferenceToSolidFireCluster invoked");
+
+        verifyRootAdmin();
+
+        verifyClusterQuotas(totalCapacity, totalMinIops, totalMaxIops, totalBurstIops);
+
+        verifyZone(zoneId);
+
+        SolidFireConnection sfConnection = new SolidFireConnection(mvip, username, password);
+
+        String clusterName = sfConnection.getClusterName();
+
+        List<SfClusterVO> sfClusterVOs = _sfClusterDao.listAll();
+
+        for (SfCluster sfCluster : sfClusterVOs) {
+            if (sfCluster.getName().equals(clusterName)) {
+                throw new CloudRuntimeException("Unable to add a reference to cluster '" + clusterName + "' as a reference to a cluster by this name already exists");
+            }
+        }
+
+        SfClusterVO sfClusterVO = new SfClusterVO(clusterName, mvip, username, password, totalCapacity, totalMinIops, totalMaxIops, totalBurstIops, zoneId);
+
+        return _sfClusterDao.persist(sfClusterVO);
+    }
+
+    @Override
+    public SfCluster updateReferenceToSolidFireCluster(String clusterName, long newTotalCapacity,
+            long newTotalMinIops, long newTotalMaxIops, long newTotalBurstIops) {
+        s_logger.info("updateReferenceToSolidFireCluster invoked");
+
+        verifyRootAdmin();
+
+        verifyClusterQuotas(newTotalCapacity, newTotalMinIops, newTotalMaxIops, newTotalBurstIops);
+
+        SfClusterVO sfClusterVO = getSfCluster(clusterName);
+
+        GlobalLock sfClusterLock = GlobalLock.getInternLock(sfClusterVO.getUuid());
+
+        if (!sfClusterLock.lock(s_lockTimeInSeconds)) {
+            String errMsg = "Couldn't lock the DB on the following string (Storage cluster UUID): " + sfClusterVO.getUuid();
+
+            s_logger.debug(errMsg);
+
+            throw new CloudRuntimeException(errMsg);
+        }
+
+        try {
+            TotalRemaining totalRemainingInCluster = getTotalRemainingInCluster(sfClusterVO);
+
+            long totalUsedCapacityInCluster = sfClusterVO.getTotalCapacity() - totalRemainingInCluster.getTotalRemainingCapacity();
+            long totalUsedMinIopsInCluster = sfClusterVO.getTotalMinIops() - totalRemainingInCluster.getTotalRemainingMinIops();
+            long totalUsedMaxIopsInCluster = sfClusterVO.getTotalMaxIops() - totalRemainingInCluster.getTotalRemainingMaxIops();
+            long totalUsedBurstIopsInCluster = sfClusterVO.getTotalBurstIops() - totalRemainingInCluster.getTotalRemainingBurstIops();
+
+            if (totalUsedCapacityInCluster <= newTotalCapacity && totalUsedMinIopsInCluster <= newTotalMinIops &&
+                    totalUsedMaxIopsInCluster <= newTotalMaxIops && totalUsedBurstIopsInCluster <= newTotalBurstIops) {
+                sfClusterVO.setTotalCapacity(newTotalCapacity);
+                sfClusterVO.setTotalMinIops(newTotalMinIops);
+                sfClusterVO.setTotalMaxIops(newTotalMaxIops);
+                sfClusterVO.setTotalBurstIops(newTotalBurstIops);
+
+                if (_sfClusterDao.update(sfClusterVO.getId(), sfClusterVO)) {
+                    return sfClusterVO;
+                }
+
+                throw new CloudRuntimeException("Unable to update the cluster table");
+            }
+            else {
+                throw new CloudRuntimeException("Unable to update the cluster table as more capacity and/or performance is in use " +
+                        "in the storage cluster than one or more of the values passed in");
+            }
+        }
+        finally {
+            sfClusterLock.unlock();
+            sfClusterLock.releaseRef();
+        }
+    }
+
+    @Override
+    public SfCluster deleteReferenceToSolidFireCluster(String clusterName) {
+        s_logger.info("deleteReferenceToSolidFireCluster invoked");
+
+        verifyRootAdmin();
+
+        SfCluster sfCluster = getSfCluster(clusterName);
+
+        List<SfVirtualNetworkVO> sfVirtualNetworks = _sfVirtualNetworkDao.findByClusterId(sfCluster.getId());
+
+        if (sfVirtualNetworks != null && sfVirtualNetworks.size() > 0) {
+            throw new CloudRuntimeException("Unable to delete a reference to a cluster that has one or more virtual networks");
+        }
+
+        if (!_sfClusterDao.remove(sfCluster.getId())) {
+            throw new CloudRuntimeException("Unable to remove the following cluster: " + clusterName);
+        }
+
+        return sfCluster;
+    }
+
+    @Override
+    public SfVirtualNetwork listSolidFireVirtualNetworkById(long id) {
+        s_logger.info("listSolidFireVirtualNetworkById invoked");
+
+        SfVirtualNetwork sfVirtualNetwork = getSfVirtualNetwork(id);
+
+        verifyPermissionsForAccount(sfVirtualNetwork.getAccountId());
+
+        return getSfVirtualNetwork(id);
+    }
+
+    @Override
+    public List<SfVirtualNetwork> listSolidFireVirtualNetworkByClusterName(String clusterName) {
+        s_logger.info("listSolidFireVirtualNetworkByClusterName invoked");
+
+        verifyRootAdmin();
+
+        SfCluster sfCluster = getSfCluster(clusterName);
+
+        return filterVirtualNetworksByCluster(_sfVirtualNetworkDao.listAll(), sfCluster.getId());
+    }
+
+    @Override
+    public List<SfVirtualNetwork> listSolidFireVirtualNetworks(Long zoneId, Long accountId) {
+        s_logger.info("listSolidFireVirtualNetworks invoked");
+
+        final List<SfVirtualNetworkVO> sfVirtualNetworkVOs;
+
+        if (_sfUtil.isRootAdmin()) {
+            if (zoneId != null) {
+                if (accountId != null) {
+                    sfVirtualNetworkVOs = filterVirtualNetworksByZone(_sfVirtualNetworkDao.findByAccountId(accountId), zoneId);
+                }
+                else {
+                    sfVirtualNetworkVOs = filterVirtualNetworksByZone(_sfVirtualNetworkDao.listAll(), zoneId);
+                }
+            }
+            else {
+                if (accountId != null) {
+                    sfVirtualNetworkVOs = _sfVirtualNetworkDao.findByAccountId(accountId);
+                }
+                else {
+                    sfVirtualNetworkVOs = _sfVirtualNetworkDao.listAll();
+                }
+            }
+        }
+        else {
+            if (accountId != null && accountId != _sfUtil.getCallingAccount().getId()) {
+                throw new CloudRuntimeException("Only a root admin can specify an account other than his own.");
+            }
+
+            if (zoneId != null) {
+                sfVirtualNetworkVOs = filterVirtualNetworksByZone(_sfVirtualNetworkDao.findByAccountId(_sfUtil.getCallingAccount().getId()), zoneId);
+            }
+            else {
+                sfVirtualNetworkVOs = _sfVirtualNetworkDao.findByAccountId(_sfUtil.getCallingAccount().getId());
+            }
+        }
+
+        List<SfVirtualNetwork> sfVirtualNetworks = new ArrayList<>();
+
+        if (sfVirtualNetworkVOs != null) {
+            sfVirtualNetworks.addAll(sfVirtualNetworkVOs);
+        }
+
+        return sfVirtualNetworks;
+    }
+
+    @Override
+    public SfVirtualNetwork createSolidFireVirtualNetwork(String clusterName, String name, String tag, String startIp, int size,
+            String netmask, String svip, long accountId) {
+        s_logger.info("createSolidFireVirtualNetwork invoked");
+
+        verifyRootAdmin();
+
+        verifyAccount(accountId);
+
+        SfCluster sfCluster = getSfCluster(clusterName);
+
+        SolidFireConnection sfConnection = new SolidFireConnection(sfCluster.getMvip(), sfCluster.getUsername(), sfCluster.getPassword());
+
+        long sfVirtualNetworkId = sfConnection.createVirtualNetwork(name, tag, startIp, size, netmask, svip);
+
+        SfVirtualNetworkVO sfVirtualNetworkVO = new SfVirtualNetworkVO(sfVirtualNetworkId, name, tag, startIp, size, netmask, svip, accountId, sfCluster.getId());
+
+        return _sfVirtualNetworkDao.persist(sfVirtualNetworkVO);
+    }
+
+    @Override
+    public SfVirtualNetwork updateSolidFireVirtualNetwork(long id, String name, String startIp, int size, String netmask) {
+        s_logger.info("updateSolidFireVirtualNetwork invoked");
+
+        verifyRootAdmin();
+
+        SfVirtualNetworkVO sfVirtualNetworkVO = getSfVirtualNetwork(id);
+
+        long sfClusterId = sfVirtualNetworkVO.getSfClusterId();
+
+        SfClusterVO sfClusterVO = getSfCluster(sfClusterId);
+
+        SolidFireConnection sfConnection = new SolidFireConnection(sfClusterVO.getMvip(), sfClusterVO.getUsername(), sfClusterVO.getPassword());
+
+        sfConnection.modifyVirtualNetwork(sfVirtualNetworkVO.getSfId(), name, startIp, size, netmask);
+
+        sfVirtualNetworkVO.setName(name);
+        sfVirtualNetworkVO.setStartIp(startIp);
+        sfVirtualNetworkVO.setSize(size);
+        sfVirtualNetworkVO.setNetmask(netmask);
+
+        if (_sfVirtualNetworkDao.update(sfVirtualNetworkVO.getId(), sfVirtualNetworkVO)) {
+            return sfVirtualNetworkVO;
+        }
+
+        throw new CloudRuntimeException("Unable to update the virtual network table");
+    }
+
+    @Override
+    public SfVirtualNetwork deleteSolidFireVirtualNetwork(long id) {
+        s_logger.info("deleteSolidFireVirtualNetwork invoked");
+
+        verifyRootAdmin();
+
+        SfVirtualNetwork sfVirtualNetwork = getSfVirtualNetwork(id);
+
+        List<SfVolumeVO> sfVolumes = _sfVolumeDao.findBySfVirtualNetworkId(sfVirtualNetwork.getId());
+
+        if (sfVolumes != null && sfVolumes.size() > 0) {
+            throw new CloudRuntimeException("Unable to delete a virtual network that has one or more volumes");
+        }
+
+        if (!_sfVirtualNetworkDao.remove(id)) {
+            throw new CloudRuntimeException("Unable to remove the following virtual network: " + id);
+        }
+
+        SfCluster sfCluster = getSfCluster(sfVirtualNetwork.getSfClusterId());
+
+        SolidFireConnection sfConnection = new SolidFireConnection(sfCluster.getMvip(), sfCluster.getUsername(), sfCluster.getPassword());
+
+        sfConnection.deleteVirtualNetwork(sfVirtualNetwork.getSfId());
+
+        return sfVirtualNetwork;
+    }
+
+    @Override
+    public SfVolume listSolidFireVolume(long id) {
+        s_logger.info("listSolidFireVolume invoked");
+
+        SfVolume sfVolume = getSfVolume(id);
+
+        SfVirtualNetwork sfVirtualNetwork = getSfVirtualNetwork(sfVolume.getSfVirtualNetworkId());
+
+        verifyPermissionsForAccount(sfVirtualNetwork.getAccountId());
+
+        return sfVolume;
+    }
+
+    @Override
+    public List<SfVolume> listSolidFireVolumes() {
+        s_logger.info("listSolidFireVolumes invoked");
+
+        final List<SfVolumeVO> sfVolumeVOs;
+
+        if (_sfUtil.isRootAdmin()) {
+            sfVolumeVOs = _sfVolumeDao.listAll();
+        }
+        else {
+            sfVolumeVOs = new ArrayList<>();
+
+            List<SfVirtualNetworkVO> sfVirtualNetworkVOs = _sfVirtualNetworkDao.findByAccountId(_sfUtil.getCallingAccount().getId());
+
+            if (sfVirtualNetworkVOs != null) {
+                for (SfVirtualNetwork sfVirtualNetwork : sfVirtualNetworkVOs) {
+                    List<SfVolumeVO> sfVolumeVOsForVirtualNetwork = _sfVolumeDao.findBySfVirtualNetworkId(sfVirtualNetwork.getId());
+
+                    sfVolumeVOs.addAll(sfVolumeVOsForVirtualNetwork);
+                }
+            }
+        }
+
+        List<SfVolume> sfVolumes = new ArrayList<>();
+
+        if (sfVolumeVOs != null) {
+            sfVolumes.addAll(sfVolumeVOs);
+        }
+
+        return sfVolumes;
+    }
+
+    @Override
+    public SfVolume createSolidFireVolume(String name, long size, long minIops, long maxIops, long burstIops, long accountId, long sfVirtualNetworkId) {
+        s_logger.info("createSolidFireVolume invoked");
+
+        verifyPermissionsForAccount(accountId);
+
+        verifySfVirtualNetwork(sfVirtualNetworkId);
+
+        SfVolume sfVolume = createVolume(name, size, minIops, maxIops, burstIops, accountId, sfVirtualNetworkId);
+
+        if (sfVolume != null) {
+            return sfVolume;
+        }
+
+        throw new CloudRuntimeException("Unable to create the volume");
+    }
+
+    @Override
+    public SfVolume updateSolidFireVolume(long id, long size, long minIops, long maxIops, long burstIops) {
+        s_logger.info("updateSolidFireVolume invoked");
+
+        SfVolumeVO sfVolumeVO = getSfVolume(id);
+
+        SfVirtualNetwork sfVirtualNetwork = getSfVirtualNetwork(sfVolumeVO.getSfVirtualNetworkId());
+
+        verifyPermissionsForAccount(sfVirtualNetwork.getAccountId());
+
+        if ((sfVolumeVO = updateVolume(sfVolumeVO, size, minIops, maxIops, burstIops)) != null) {
+            return sfVolumeVO;
+        }
+
+        throw new CloudRuntimeException("Unable to update the volume with the following id: " + id);
+    }
+
+    @Override
+    public SfVolume deleteSolidFireVolume(long id) {
+        s_logger.info("deleteSolidFireVolume invoked");
+
+        SfVolume sfVolume = getSfVolume(id);
+
+        SfVirtualNetwork sfVirtualNetwork = getSfVirtualNetwork(sfVolume.getSfVirtualNetworkId());
+
+        verifyPermissionsForAccount(sfVirtualNetwork.getAccountId());
+
+        if (!_sfVolumeDao.remove(id)) {
+            throw new CloudRuntimeException("Unable to remove the following volume: " + id);
+        }
+
+        SfCluster sfCluster = getSfCluster(sfVirtualNetwork.getSfClusterId());
+
+        SolidFireConnection sfConnection = new SolidFireConnection(sfCluster.getMvip(), sfCluster.getUsername(), sfCluster.getPassword());
+
+        sfConnection.deleteVolume(sfVolume.getSfId());
+
+        return sfVolume;
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return SolidFireManagerImpl.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] { s_sfTotalAccountCapacity, s_sfTotalAccountMinIops, s_sfTotalAccountMaxIops, s_sfTotalAccountBurstIops };
+    }
+
+    private SfClusterVO getSfCluster(String clusterName) {
+        SfClusterVO sfClusterVO = _sfClusterDao.findByName(clusterName);
+
+        if (sfClusterVO == null) {
+            throw new CloudRuntimeException("The following SolidFire cluster name cannot be located in the database: '" + clusterName + "'.");
+        }
+
+        return sfClusterVO;
+    }
+
+    private SfClusterVO getSfCluster(long sfClusterId) {
+        SfClusterVO sfClusterVO = _sfClusterDao.findById(sfClusterId);
+
+        if (sfClusterVO == null) {
+            throw new CloudRuntimeException("The SolidFire cluster with the following ID cannot be located in the database: '" + sfClusterId + "'.");
+        }
+
+        return sfClusterVO;
+    }
+
+    private SfVirtualNetworkVO getSfVirtualNetwork(long id) {
+        SfVirtualNetworkVO sfVirtualNetworkVO = _sfVirtualNetworkDao.findById(id);
+
+        if (sfVirtualNetworkVO == null) {
+            throw new CloudRuntimeException("The SolidFire VLAN with the following ID cannot be located in the database: '" + id + "'.");
+        }
+
+        return sfVirtualNetworkVO;
+    }
+
+    private SfVolumeVO getSfVolume(long id) {
+        SfVolumeVO sfVolumeVO = _sfVolumeDao.findById(id);
+
+        if (sfVolumeVO == null) {
+            throw new CloudRuntimeException("The SolidFire volume with the following ID cannot be located in the database: '" + id + "'.");
+        }
+
+        return sfVolumeVO;
+    }
+
+    private void verifyRootAdmin() {
+        if (!_sfUtil.isRootAdmin()) {
+            throw new PermissionDeniedException("Only a root admin can perform this operation.");
+        }
+    }
+
+    private void verifyPermissionsForAccount(long accountId) {
+        Account account = _sfUtil.getCallingAccount();
+
+        if (_sfUtil.isRootAdmin(account.getId())) {
+            return; // permissions OK
+        }
+
+        if (account.getId() == accountId) {
+            return; // permissions OK
+        }
+
+        throw new PermissionDeniedException("Only a root admin or a user of the owning account can perform this operation.");
+    }
+
+    private void verifyClusterQuotas(long totalCapacity, long totalMinIops, long totalMaxIops, long totalBurstIops) {
+        if (totalCapacity < 0) {
+            throw new CloudRuntimeException("The total capacity of the cluster must be a positive whole number.");
+        }
+
+        if (totalMinIops < 0) {
+            throw new CloudRuntimeException("The total minimum IOPS of the cluster must be a positive whole number.");
+        }
+
+        if (totalMaxIops < 0) {
+            throw new CloudRuntimeException("The total maximum IOPS of the cluster must be a positive whole number.");
+        }
+
+        if (totalBurstIops < 0) {
+            throw new CloudRuntimeException("The total burst IOPS of the cluster must be a positive whole number.");
+        }
+
+        if (totalMinIops > totalMaxIops) {
+            throw new CloudRuntimeException("The total minimum IOPS of the cluster must be less than or equal to the total maximum IOPS of the cluster.");
+        }
+
+        if (totalMaxIops > totalBurstIops) {
+            throw new CloudRuntimeException("The total maximum IOPS of the cluster must be less than or equal to the total burst IOPS of the cluster.");
+        }
+    }
+
+    private void verifyAccount(long accountId) {
+        Account account = _accountDao.findById(accountId);
+
+        if (account == null) {
+            throw new CloudRuntimeException("Unable to locate the following account: " + accountId);
+        }
+     }
+
+    private void verifySfVirtualNetwork(long sfVirtualNetworkId) {
+        SfVirtualNetwork sfVirtualNetwork = _sfVirtualNetworkDao.findById(sfVirtualNetworkId);
+
+        if (sfVirtualNetwork == null) {
+            throw new CloudRuntimeException("Unable to locate the following virtual network: " + sfVirtualNetworkId);
+        }
+     }
+
+    private void verifyZone(long zoneId) {
+       DataCenterVO dataCenterVO = _zoneDao.findById(zoneId);
+
+       if (dataCenterVO == null) {
+           throw new CloudRuntimeException("Unable to locate the following zone: " + zoneId);
+       }
+    }
+
+    private SfVolume createVolume(String name, long size, long minIops, long maxIops, long burstIops, long accountId, long sfVirtualNetworkId) {
+        verifyIops(minIops, maxIops, burstIops);
+
+        Account account = _accountDao.findById(accountId);
+
+        SfVirtualNetwork sfVirtualNetwork = getSfVirtualNetwork(sfVirtualNetworkId);
+        SfCluster sfCluster = getSfCluster(sfVirtualNetwork.getSfClusterId());
+
+        GlobalLock accountLock = GlobalLock.getInternLock(account.getUuid());
+
+        if (!accountLock.lock(s_lockTimeInSeconds)) {
+            String errMsg = "Couldn't lock the DB on the following string (Account UUID): " + sfCluster.getUuid();
+
+            s_logger.debug(errMsg);
+
+            throw new CloudRuntimeException(errMsg);
+        }
+
+        GlobalLock sfClusterLock = GlobalLock.getInternLock(sfCluster.getUuid());
+
+        boolean clusterLockSuccess = false;
+
+        try {
+            clusterLockSuccess = sfClusterLock.lock(s_lockTimeInSeconds);
+        }
+        catch (Throwable t) {
+            // clusterLockSuccess should still be false
+        }
+
+        if (!clusterLockSuccess) {
+            accountLock.unlock();
+            accountLock.releaseRef();
+
+            String errMsg = "Couldn't lock the DB on the following string (Storage cluster UUID): " + sfCluster.getUuid();
+
+            s_logger.debug(errMsg);
+
+            throw new CloudRuntimeException(errMsg);
+        }
+
+        try {
+            TotalRemaining totalRemainingInAccount = getTotalRemainingInAccount(accountId);
+
+            long totalRemainingCapacityInAccount = totalRemainingInAccount.getTotalRemainingCapacity() - size;
+            long totalRemainingMinIopsInAccount = totalRemainingInAccount.getTotalRemainingMinIops() - minIops;
+            long totalRemainingMaxIopsInAccount = totalRemainingInAccount.getTotalRemainingMaxIops() - maxIops;
+            long totalRemainingBurstIopsInAccount = totalRemainingInAccount.getTotalRemainingBurstIops() - burstIops;
+
+            if (totalRemainingCapacityInAccount >= 0 && totalRemainingMinIopsInAccount >= 0 && totalRemainingMaxIopsInAccount >= 0 && totalRemainingBurstIopsInAccount >= 0) {
+                TotalRemaining totalRemainingInCluster = getTotalRemainingInCluster(sfCluster);
+
+                long totalRemainingCapacityInCluster = totalRemainingInCluster.getTotalRemainingCapacity() - size;
+                long totalRemainingMinIopsInCluster = totalRemainingInCluster.getTotalRemainingMinIops() - minIops;
+                long totalRemainingMaxIopsInCluster = totalRemainingInCluster.getTotalRemainingMaxIops() - maxIops;
+                long totalRemainingBurstIopsInCluster = totalRemainingInCluster.getTotalRemainingBurstIops() - burstIops;
+
+                if (totalRemainingCapacityInCluster >= 0 && totalRemainingMinIopsInCluster >= 0 && totalRemainingMaxIopsInCluster >= 0 && totalRemainingBurstIopsInCluster >= 0) {
+                    SolidFireConnection sfConnection = new SolidFireConnection(sfCluster.getMvip(), sfCluster.getUsername(), sfCluster.getPassword());
+
+                    long sfClusterId = sfCluster.getId();
+
+                    AccountDetailVO accountDetail = getAccountDetail(accountId, sfClusterId);
+
+                    if (accountDetail == null || accountDetail.getValue() == null) {
+                        String sfAccountName = getSolidFireAccountName(accountId, account.getUuid());
+                        SolidFireConnection.SolidFireAccount sfAccount = sfConnection.getSolidFireAccount(sfAccountName);
+
+                        if (sfAccount == null) {
+                            sfAccount = createSolidFireAccount(sfConnection, sfAccountName);
+                        }
+
+                        updateCsDbWithSolidFireAccountInfo(account.getId(), sfAccount, sfClusterId);
+
+                        accountDetail = getAccountDetail(accountId, sfClusterId);
+                    }
+
+                    long sfAccountId = Long.parseLong(accountDetail.getValue());
+
+                    long sfVolumeId = sfConnection.createVolume(name, sfAccountId, size, minIops, maxIops, burstIops);
+
+                    SolidFireConnection.SolidFireVolume sfVolume = sfConnection.getVolume(sfVolumeId);
+
+                    SfVolumeVO sfVolumeVO = new SfVolumeVO(sfVolumeId, name, sfVolume.getIqn(), size, minIops, maxIops, burstIops, sfVirtualNetworkId);
+
+                    return _sfVolumeDao.persist(sfVolumeVO);
+                }
+                else {
+                    throw new CloudRuntimeException("Unable to create the volume due to insufficient capacity or performance remaining in the storage cluster");
+                }
+            }
+            else {
+                throw new CloudRuntimeException("Unable to create the volume due to insufficient capacity or performance remaining in the account");
+            }
+        }
+        finally {
+            sfClusterLock.unlock();
+            sfClusterLock.releaseRef();
+
+            accountLock.unlock();
+            accountLock.releaseRef();
+        }
+    }
+
+    private SfVolumeVO updateVolume(SfVolumeVO sfVolumeVO, long size, long minIops, long maxIops, long burstIops) {
+        verifyIops(minIops, maxIops, burstIops);
+
+        SfVirtualNetwork sfVirtualNetwork = getSfVirtualNetwork(sfVolumeVO.getSfVirtualNetworkId());
+
+        Account account = _accountDao.findById(sfVirtualNetwork.getAccountId());
+        SfCluster sfCluster = getSfCluster(sfVirtualNetwork.getSfClusterId());
+
+        GlobalLock accountLock = GlobalLock.getInternLock(account.getUuid());
+
+        if (!accountLock.lock(s_lockTimeInSeconds)) {
+            String errMsg = "Couldn't lock the DB on the following string (Account UUID): " + sfCluster.getUuid();
+
+            s_logger.debug(errMsg);
+
+            throw new CloudRuntimeException(errMsg);
+        }
+
+        GlobalLock sfClusterLock = GlobalLock.getInternLock(sfCluster.getUuid());
+
+        boolean clusterLockSuccess = false;
+
+        try {
+            clusterLockSuccess = sfClusterLock.lock(s_lockTimeInSeconds);
+        }
+        catch (Throwable t) {
+            // clusterLockSuccess should still be false
+        }
+
+        if (!clusterLockSuccess) {
+            accountLock.unlock();
+            accountLock.releaseRef();
+
+            String errMsg = "Couldn't lock the DB on the following string (Storage cluster UUID): " + sfCluster.getUuid();
+
+            s_logger.debug(errMsg);
+
+            throw new CloudRuntimeException(errMsg);
+        }
+
+        try {
+            TotalRemaining totalRemainingInAccount = getTotalRemainingInAccount(account.getId());
+
+            long totalRemainingCapacityInAccount = totalRemainingInAccount.getTotalRemainingCapacity() - size;
+            long totalRemainingMinIopsInAccount = totalRemainingInAccount.getTotalRemainingMinIops() - minIops;
+            long totalRemainingMaxIopsInAccount = totalRemainingInAccount.getTotalRemainingMaxIops() - maxIops;
+            long totalRemainingBurstIopsInAccount = totalRemainingInAccount.getTotalRemainingBurstIops() - burstIops;
+
+            if (totalRemainingCapacityInAccount >= 0 && totalRemainingMinIopsInAccount >= 0 && totalRemainingMaxIopsInAccount >= 0 && totalRemainingBurstIopsInAccount >= 0) {
+                TotalRemaining totalRemainingInCluster = getTotalRemainingInCluster(sfCluster, sfVolumeVO);
+
+                long totalRemainingCapacityInCluster = totalRemainingInCluster.getTotalRemainingCapacity() - size;
+                long totalRemainingMinIopsInCluster = totalRemainingInCluster.getTotalRemainingMinIops() - minIops;
+                long totalRemainingMaxIopsInCluster = totalRemainingInCluster.getTotalRemainingMaxIops() - maxIops;
+                long totalRemainingBurstIopsInCluster = totalRemainingInCluster.getTotalRemainingBurstIops() - burstIops;
+
+                if (totalRemainingCapacityInCluster >= 0 && totalRemainingMinIopsInCluster >= 0 && totalRemainingMaxIopsInCluster >= 0 && totalRemainingBurstIopsInCluster >= 0) {
+                    SolidFireConnection sfConnection = new SolidFireConnection(sfCluster.getMvip(), sfCluster.getUsername(), sfCluster.getPassword());
+
+                    sfConnection.modifyVolume(sfVolumeVO.getSfId(), size, minIops, maxIops, burstIops);
+
+                    sfVolumeVO.setSize(size);
+                    sfVolumeVO.setMinIops(minIops);
+                    sfVolumeVO.setMaxIops(maxIops);
+                    sfVolumeVO.setBurstIops(burstIops);
+
+                    if (!_sfVolumeDao.update(sfVolumeVO.getId(), sfVolumeVO)) {
+                        throw new CloudRuntimeException("Unable to update the following volume:" + sfVolumeVO.getId());
+                    }
+
+                    return sfVolumeVO;
+                }
+                else {
+                    throw new CloudRuntimeException("Unable to update the volume due to insufficient capacity or performance remaining in the storage cluster");
+                }
+            }
+            else {
+                throw new CloudRuntimeException("Unable to update the volume due to insufficient capacity or performance remaining in the account");
+            }
+        }
+        finally {
+            sfClusterLock.unlock();
+            sfClusterLock.releaseRef();
+
+            accountLock.unlock();
+            accountLock.releaseRef();
+        }
+    }
+
+    private TotalRemaining getTotalRemainingInAccount(long accountId) {
+        return getTotalRemainingInAccount(accountId, null);
+    }
+
+    private TotalRemaining getTotalRemainingInAccount(long accountId, SfVolume volumeToExclude) {
+        Long totalRemainingCapacity = s_sfTotalAccountCapacity.valueIn(accountId);
+        Long totalRemainingMinIops = s_sfTotalAccountMinIops.valueIn(accountId);
+        Long totalRemainingMaxIops = s_sfTotalAccountMaxIops.valueIn(accountId);
+        Long totalRemainingBurstIops = s_sfTotalAccountBurstIops.valueIn(accountId);
+
+        List<SfVolume> sfVolumesInAccount = new ArrayList<>();
+
+        List<SfVirtualNetworkVO> sfVirtualNetworkVOs = _sfVirtualNetworkDao.findByAccountId(accountId);
+
+        if (sfVirtualNetworkVOs != null) {
+            for (SfVirtualNetwork sfVirtualNetwork : sfVirtualNetworkVOs) {
+                List<SfVolumeVO> sfVolumeVOs = _sfVolumeDao.findBySfVirtualNetworkId(sfVirtualNetwork.getId());
+
+                sfVolumesInAccount.addAll(sfVolumeVOs);
+            }
+        }
+
+        for (SfVolume sfVolumeInAccount : sfVolumesInAccount) {
+            if (volumeToExclude == null || sfVolumeInAccount.getId() != volumeToExclude.getId()) {
+                totalRemainingCapacity -= sfVolumeInAccount.getSize();
+                totalRemainingMinIops -= sfVolumeInAccount.getMinIops();
+                totalRemainingMaxIops -= sfVolumeInAccount.getMaxIops();
+                totalRemainingBurstIops -= sfVolumeInAccount.getBurstIops();
+            }
+        }
+
+        return new TotalRemaining(totalRemainingCapacity, totalRemainingMinIops, totalRemainingMaxIops, totalRemainingBurstIops);
+    }
+
+    private TotalRemaining getTotalRemainingInCluster(SfCluster sfCluster) {
+        return getTotalRemainingInCluster(sfCluster, null);
+    }
+
+    private TotalRemaining getTotalRemainingInCluster(SfCluster sfCluster, SfVolume volumeToExclude) {
+        long totalRemainingCapacity = sfCluster.getTotalCapacity();
+        long totalRemainingMinIops = sfCluster.getTotalMinIops();
+        long totalRemainingMaxIops = sfCluster.getTotalMaxIops();
+        long totalRemainingBurstIops = sfCluster.getTotalBurstIops();
+
+        List<SfVolume> sfVolumesInCluster = new ArrayList<>();
+
+        List<SfVirtualNetworkVO> sfVirtualNetworkVOs = _sfVirtualNetworkDao.findByClusterId(sfCluster.getId());
+
+        if (sfVirtualNetworkVOs != null) {
+            for (SfVirtualNetwork sfVirtualNetwork : sfVirtualNetworkVOs) {
+                List<SfVolumeVO> sfVolumeVOs = _sfVolumeDao.findBySfVirtualNetworkId(sfVirtualNetwork.getId());
+
+                sfVolumesInCluster.addAll(sfVolumeVOs);
+            }
+        }
+
+        for (SfVolume sfVolumeInCluster : sfVolumesInCluster) {
+            if (volumeToExclude == null || sfVolumeInCluster.getId() != volumeToExclude.getId()) {
+                totalRemainingCapacity -= sfVolumeInCluster.getSize();
+                totalRemainingMinIops -= sfVolumeInCluster.getMinIops();
+                totalRemainingMaxIops -= sfVolumeInCluster.getMaxIops();
+                totalRemainingBurstIops -= sfVolumeInCluster.getBurstIops();
+            }
+        }
+
+        return new TotalRemaining(totalRemainingCapacity, totalRemainingMinIops, totalRemainingMaxIops, totalRemainingBurstIops);
+    }
+
+    private static class TotalRemaining {
+        private final long _totalRemainingCapacity;
+        private final long _totalRemainingMinIops;
+        private final long _totalRemainingMaxIops;
+        private final long _totalRemainingBurstIops;
+
+        public TotalRemaining(long totalRemainingCapacity, long totalRemainingMinIops, long totalRemainingMaxIops, long totalRemainingBurstIops) {
+            _totalRemainingCapacity = totalRemainingCapacity;
+            _totalRemainingMinIops = totalRemainingMinIops;
+            _totalRemainingMaxIops = totalRemainingMaxIops;
+            _totalRemainingBurstIops = totalRemainingBurstIops;
+        }
+
+        public long getTotalRemainingCapacity() {
+            return _totalRemainingCapacity;
+        }
+
+        public long getTotalRemainingMinIops() {
+            return _totalRemainingMinIops;
+        }
+
+        public long getTotalRemainingMaxIops() {
+            return _totalRemainingMaxIops;
+        }
+
+        public long getTotalRemainingBurstIops() {
+            return _totalRemainingBurstIops;
+        }
+    }
+
+    private static String getSolidFireAccountName(long accountId, String accountUuid) {
+        return "CloudStack_" + accountId + "_" + accountUuid;
+    }
+
+    private static String getAccountKey(long sfClusterId) {
+        return "sfAccountIdForClusterId_" + sfClusterId;
+    }
+
+    private AccountDetailVO getAccountDetail(long accountId, long sfClusterId) {
+        return _accountDetailsDao.findDetail(accountId, getAccountKey(sfClusterId));
+    }
+
+    private SolidFireConnection.SolidFireAccount createSolidFireAccount(SolidFireConnection sfConnection, String sfAccountName) {
+        long accountNumber = sfConnection.createSolidFireAccount(sfAccountName);
+
+        return sfConnection.getSolidFireAccountById(accountNumber);
+    }
+
+    private void updateCsDbWithSolidFireAccountInfo(long accountId, SolidFireConnection.SolidFireAccount sfAccount, long sfClusterId) {
+        AccountDetailVO accountDetail = new AccountDetailVO(accountId,
+                getAccountKey(sfClusterId),
+                String.valueOf(sfAccount.getId()));
+
+        _accountDetailsDao.persist(accountDetail);
+
+        accountDetail = new AccountDetailVO(accountId,
+                SolidFireUtil.CHAP_INITIATOR_USERNAME,
+                sfAccount.getName());
+
+        _accountDetailsDao.persist(accountDetail);
+
+        accountDetail = new AccountDetailVO(accountId,
+                SolidFireUtil.CHAP_INITIATOR_SECRET,
+                sfAccount.getInitiatorSecret());
+
+        _accountDetailsDao.persist(accountDetail);
+
+        accountDetail = new AccountDetailVO(accountId,
+                SolidFireUtil.CHAP_TARGET_USERNAME,
+                sfAccount.getName());
+
+        _accountDetailsDao.persist(accountDetail);
+
+        accountDetail = new AccountDetailVO(accountId,
+                SolidFireUtil.CHAP_TARGET_SECRET,
+                sfAccount.getTargetSecret());
+
+        _accountDetailsDao.persist(accountDetail);
+    }
+
+    private List<SfVirtualNetwork> filterVirtualNetworksByCluster(List<SfVirtualNetworkVO> sfVirtualNetworkVOs, long clusterId) {
+        List<SfVirtualNetwork> sfVirtualNetworkVOsToReturn = new ArrayList<>();
+
+        if (sfVirtualNetworkVOs != null) {
+            for (SfVirtualNetworkVO sfVirtualNetworkVO : sfVirtualNetworkVOs) {
+                long sfClusterId = sfVirtualNetworkVO.getSfClusterId();
+
+                if (sfClusterId == clusterId) {
+                    sfVirtualNetworkVOsToReturn.add(sfVirtualNetworkVO);
+                }
+            }
+        }
+
+        return sfVirtualNetworkVOsToReturn;
+    }
+
+    private List<SfVirtualNetworkVO> filterVirtualNetworksByZone(List<SfVirtualNetworkVO> sfVirtualNetworkVOs, long zoneId) {
+        List<SfVirtualNetworkVO> sfVirtualNetworkVOsToReturn = new ArrayList<>();
+
+        if (sfVirtualNetworkVOs != null) {
+            for (SfVirtualNetworkVO sfVirtualNetworkVO : sfVirtualNetworkVOs) {
+                SfCluster sfCluster = getSfCluster(sfVirtualNetworkVO.getSfClusterId());
+
+                if (sfCluster.getZoneId() == zoneId) {
+                    sfVirtualNetworkVOsToReturn.add(sfVirtualNetworkVO);
+                }
+            }
+        }
+
+        return sfVirtualNetworkVOsToReturn;
+    }
+
+    private static void verifyIops(long minIops, long maxIops, long burstIops) {
+        if (minIops <= 0 || maxIops <= 0 || burstIops <= 0) {
+            throw new IllegalArgumentException("The 'Min IOPS', 'Max IOPS', and 'Burst IOPS' values must be greater than 0.");
+        }
+
+        if (minIops > maxIops) {
+            throw new IllegalArgumentException("The 'Min IOPS' value cannot exceed the 'Max IOPS' value.");
+        }
+
+        if (maxIops > burstIops) {
+            throw new IllegalArgumentException("The 'Max IOPS' value cannot exceed the 'Burst IOPS' value.");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/49ca3b40/plugins/api/solidfire/src/org/apache/cloudstack/util/solidfire/SfUtil.java
----------------------------------------------------------------------
diff --git a/plugins/api/solidfire/src/org/apache/cloudstack/util/solidfire/SfUtil.java b/plugins/api/solidfire/src/org/apache/cloudstack/util/solidfire/SfUtil.java
new file mode 100644
index 0000000..ea9df30
--- /dev/null
+++ b/plugins/api/solidfire/src/org/apache/cloudstack/util/solidfire/SfUtil.java
@@ -0,0 +1,221 @@
+package org.apache.cloudstack.util.solidfire;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.api.ResponseObject.ResponseView;
+import org.apache.cloudstack.api.response.solidfire.ApiSolidFireClusterResponse;
+import org.apache.cloudstack.api.response.solidfire.ApiSolidFireVirtualNetworkResponse;
+import org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.dataaccess.dao.solidfire.SfClusterDao;
+import org.apache.cloudstack.dataaccess.dao.solidfire.SfVirtualNetworkDao;
+import org.apache.cloudstack.solidfire.SfCluster;
+import org.apache.cloudstack.solidfire.SfVirtualNetwork;
+import org.apache.cloudstack.solidfire.SfVolume;
+import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountDetailVO;
+import com.cloud.user.AccountDetailsDao;
+import com.cloud.user.AccountManager;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+public class SfUtil {
+    @Inject private AccountDao _accountDao;
+    @Inject private AccountDetailsDao _accountDetailsDao;
+    @Inject private AccountManager _accountMgr;
+    @Inject private SfClusterDao _sfClusterDao;
+    @Inject private DataCenterDao _zoneDao;
+    @Inject private SfVirtualNetworkDao _sfVirtualNetworkDao;
+
+    private SfUtil() {
+    }
+
+    public ApiSolidFireClusterResponse getApiSolidFireClusterResponse(SfCluster sfCluster) {
+        ApiSolidFireClusterResponse sfResponse = new ApiSolidFireClusterResponse();
+
+        sfResponse.setId(sfCluster.getId());
+        sfResponse.setUuid(sfCluster.getUuid());
+        sfResponse.setName(sfCluster.getName());
+        sfResponse.setMvip(sfCluster.getMvip());
+        sfResponse.setUsername(sfCluster.getUsername());
+        sfResponse.setTotalCapacity(sfCluster.getTotalCapacity());
+        sfResponse.setTotalMinIops(sfCluster.getTotalMinIops());
+        sfResponse.setTotalMaxIops(sfCluster.getTotalMaxIops());
+        sfResponse.setTotalBurstIops(sfCluster.getTotalBurstIops());
+        sfResponse.setZoneId(sfCluster.getZoneId());
+
+        DataCenterVO dataCenterVO = _zoneDao.findById(sfCluster.getZoneId());
+
+        sfResponse.setZoneName(dataCenterVO.getName());
+
+        sfResponse.setObjectName("sfcluster");
+
+        return sfResponse;
+    }
+
+    public List<ApiSolidFireClusterResponse> getApiSolidFireClusterResponse(List<SfCluster> sfClusters) {
+        List<ApiSolidFireClusterResponse> sfResponse = new ArrayList<>();
+
+        if (sfClusters != null) {
+            for (SfCluster sfCluster : sfClusters) {
+                ApiSolidFireClusterResponse response = getApiSolidFireClusterResponse(sfCluster);
+
+                sfResponse.add(response);
+            }
+        }
+
+        return sfResponse;
+    }
+
+    public ApiSolidFireVirtualNetworkResponse getApiSolidFireVirtualNetworkResponse(SfVirtualNetwork sfVirtualNetwork, ResponseView responseView) {
+        ApiSolidFireVirtualNetworkResponse sfResponse = new ApiSolidFireVirtualNetworkResponse();
+
+        sfResponse.setId(sfVirtualNetwork.getId());
+        sfResponse.setUuid(sfVirtualNetwork.getUuid());
+        sfResponse.setName(sfVirtualNetwork.getName());
+        sfResponse.setSvip(sfVirtualNetwork.getSvip());
+        sfResponse.setAccountId(sfVirtualNetwork.getAccountId());
+
+        Account account = _accountDao.findById(sfVirtualNetwork.getAccountId());
+
+        sfResponse.setAccountUuid(account.getUuid());
+        sfResponse.setAccountName(account.getAccountName());
+
+        SfCluster sfCluster = _sfClusterDao.findById(sfVirtualNetwork.getSfClusterId());
+
+        sfResponse.setZoneId(sfCluster.getZoneId());
+
+        DataCenterVO dataCenterVO = _zoneDao.findById(sfCluster.getZoneId());
+
+        sfResponse.setZoneUuid(dataCenterVO.getUuid());
+        sfResponse.setZoneName(dataCenterVO.getName());
+
+        if (ResponseView.Full.equals(responseView)) {
+            sfResponse.setTag(sfVirtualNetwork.getTag());
+            sfResponse.setStartIp(sfVirtualNetwork.getStartIp());
+            sfResponse.setSize(sfVirtualNetwork.getSize());
+            sfResponse.setNetmask(sfVirtualNetwork.getNetmask());
+            sfResponse.setClusterName(sfCluster.getName());
+        }
+
+        sfResponse.setObjectName("sfvirtualnetwork");
+
+        return sfResponse;
+    }
+
+    public List<ApiSolidFireVirtualNetworkResponse> getApiSolidFireVirtualNetworkResponse(List<SfVirtualNetwork> sfVirtualNetworks, ResponseView responseView) {
+        List<ApiSolidFireVirtualNetworkResponse> sfResponse = new ArrayList<>();
+
+        if (sfVirtualNetworks != null) {
+            for (SfVirtualNetwork sfVirtualNetwork : sfVirtualNetworks) {
+                ApiSolidFireVirtualNetworkResponse response = getApiSolidFireVirtualNetworkResponse(sfVirtualNetwork, responseView);
+
+                sfResponse.add(response);
+            }
+        }
+
+        return sfResponse;
+    }
+
+    public ApiSolidFireVolumeResponse getApiSolidFireVolumeResponse(SfVolume sfVolume, ResponseView responseView) {
+        ApiSolidFireVolumeResponse sfResponse = new ApiSolidFireVolumeResponse();
+
+        sfResponse.setId(sfVolume.getId());
+        sfResponse.setUuid(sfVolume.getUuid());
+        sfResponse.setName(sfVolume.getName());
+        sfResponse.setIqn(sfVolume.getIqn());
+        sfResponse.setSize(sfVolume.getSize());
+        sfResponse.setMinIops(sfVolume.getMinIops());
+        sfResponse.setMaxIops(sfVolume.getMaxIops());
+        sfResponse.setBurstIops(sfVolume.getBurstIops());
+        sfResponse.setCreated(sfVolume.getCreated());
+
+        SfVirtualNetwork sfVirtualNetwork = _sfVirtualNetworkDao.findById(sfVolume.getSfVirtualNetworkId());
+
+        sfResponse.setAccountId(sfVirtualNetwork.getAccountId());
+
+        Account account = _accountDao.findById(sfVirtualNetwork.getAccountId());
+
+        sfResponse.setAccountUuid(account.getUuid());
+        sfResponse.setAccountName(account.getAccountName());
+
+        SfCluster sfCluster = _sfClusterDao.findById(sfVirtualNetwork.getSfClusterId());
+
+        sfResponse.setZoneId(sfCluster.getZoneId());
+
+        DataCenterVO dataCenterVO = _zoneDao.findById(sfCluster.getZoneId());
+
+        sfResponse.setZoneUuid(dataCenterVO.getUuid());
+        sfResponse.setZoneName(dataCenterVO.getName());
+
+        if (ResponseView.Full.equals(responseView)) {
+            sfResponse.setClusterName(sfCluster.getName());
+        }
+
+        sfResponse.setTargetPortal(sfVirtualNetwork.getSvip());
+        sfResponse.setVlanId(sfVirtualNetwork.getId());
+        sfResponse.setVlanUuid(sfVirtualNetwork.getUuid());
+        sfResponse.setVlanName(sfVirtualNetwork.getName());
+
+        AccountDetailVO accountDetail = _accountDetailsDao.findDetail(sfVirtualNetwork.getAccountId(), SolidFireUtil.CHAP_INITIATOR_USERNAME);
+
+        sfResponse.setChapInitiatorUsername(accountDetail.getValue());
+
+        accountDetail = _accountDetailsDao.findDetail(sfVirtualNetwork.getAccountId(), SolidFireUtil.CHAP_INITIATOR_SECRET);
+
+        sfResponse.setChapInitiatorSecret(accountDetail.getValue());
+
+        accountDetail = _accountDetailsDao.findDetail(sfVirtualNetwork.getAccountId(), SolidFireUtil.CHAP_TARGET_USERNAME);
+
+        sfResponse.setChapTargetUsername(accountDetail.getValue());
+
+        accountDetail = _accountDetailsDao.findDetail(sfVirtualNetwork.getAccountId(), SolidFireUtil.CHAP_TARGET_SECRET);
+
+        sfResponse.setChapTargetSecret(accountDetail.getValue());
+
+        sfResponse.setObjectName("sfvolume");
+
+        return sfResponse;
+    }
+
+    public List<ApiSolidFireVolumeResponse> getApiSolidFireVolumeResponse(List<SfVolume> sfVolumes, ResponseView responseView) {
+        List<ApiSolidFireVolumeResponse> sfResponse = new ArrayList<>();
+
+        if (sfVolumes != null) {
+            for (SfVolume sfVolume : sfVolumes) {
+                ApiSolidFireVolumeResponse response = getApiSolidFireVolumeResponse(sfVolume, responseView);
+
+                sfResponse.add(response);
+            }
+        }
+
+        return sfResponse;
+    }
+
+    public boolean isRootAdmin() {
+        Account account = getCallingAccount();
+
+        return isRootAdmin(account.getId());
+    }
+
+    public boolean isRootAdmin(long accountId) {
+        return _accountMgr.isRootAdmin(accountId);
+    }
+
+    public Account getCallingAccount() {
+        Account account = CallContext.current().getCallingAccount();
+
+        if (account == null) {
+            throw new CloudRuntimeException("The user's account cannot be determined.");
+        }
+
+        return account;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/49ca3b40/plugins/api/solidfire/src/org/apache/cloudstack/util/solidfire/SolidFireConnection.java
----------------------------------------------------------------------
diff --git a/plugins/api/solidfire/src/org/apache/cloudstack/util/solidfire/SolidFireConnection.java b/plugins/api/solidfire/src/org/apache/cloudstack/util/solidfire/SolidFireConnection.java
new file mode 100644
index 0000000..e9e011c
--- /dev/null
+++ b/plugins/api/solidfire/src/org/apache/cloudstack/util/solidfire/SolidFireConnection.java
@@ -0,0 +1,950 @@
+// 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.util.solidfire;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+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 javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.cloudstack.utils.security.SSLUtils;
+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 org.apache.log4j.Logger;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@SuppressWarnings("deprecation")
+public class SolidFireConnection {
+    private static final Logger s_logger = Logger.getLogger(SolidFireConnection.class.getName());
+
+    private final String _managementVip;
+    private final int _managementPort = 443;
+    private final String _clusterAdminUsername;
+    private final String _clusterAdminPassword;
+
+    public SolidFireConnection(String managementVip, String clusterAdminUsername, String clusterAdminPassword) {
+        _managementVip = managementVip;
+        _clusterAdminUsername = clusterAdminUsername;
+        _clusterAdminPassword = clusterAdminPassword;
+    }
+
+    public String getClusterName() {
+        final Gson gson = new GsonBuilder().create();
+
+        ClusterInfoToGet clusterInfoToGet = new ClusterInfoToGet();
+
+        String strClusterInfoToGetJson = gson.toJson(clusterInfoToGet);
+
+        String strClusterInfoToGetResultJson = executeJsonRpc(strClusterInfoToGetJson);
+
+        ClusterInfoGetResult clusterInfoGetResult = gson.fromJson(strClusterInfoToGetResultJson, ClusterInfoGetResult.class);
+
+        verifyResult(clusterInfoGetResult.result, strClusterInfoToGetResultJson, gson);
+
+        return clusterInfoGetResult.result.clusterInfo.name;
+    }
+
+    public long createVirtualNetwork(String name, String tag, String startIp, int size, String netmask, String svip) {
+        final Gson gson = new GsonBuilder().create();
+
+        VirtualNetworkToAdd virtualNetworkToAdd = new VirtualNetworkToAdd(name, tag, startIp, size, netmask, svip);
+
+        String strVirtualNetworkToAddJson = gson.toJson(virtualNetworkToAdd);
+
+        String strVirtualNetworkToAddResultJson = executeJsonRpc(strVirtualNetworkToAddJson);
+
+        VirtualNetworkAddResult virtualNetworkAddResult = gson.fromJson(strVirtualNetworkToAddResultJson, VirtualNetworkAddResult.class);
+
+        verifyResult(virtualNetworkAddResult.result, strVirtualNetworkToAddResultJson, gson);
+
+        return virtualNetworkAddResult.result.virtualNetworkID;
+    }
+
+    public void modifyVirtualNetwork(long id, String name, String startIp, int size, String netmask) {
+        final Gson gson = new GsonBuilder().create();
+
+        VirtualNetworkToModify virtualNetworkToModify = new VirtualNetworkToModify(id, name, startIp, size, netmask);
+
+        String strVirtualNetworkToModifyJson = gson.toJson(virtualNetworkToModify);
+
+        String strVirtualNetworkToModifyResultJson = executeJsonRpc(strVirtualNetworkToModifyJson);
+
+        JsonError jsonError = gson.fromJson(strVirtualNetworkToModifyResultJson, JsonError.class);
+
+        if (jsonError.error != null) {
+            throw new IllegalStateException(jsonError.error.message);
+        }
+    }
+
+    public void deleteVirtualNetwork(long id) {
+        final Gson gson = new GsonBuilder().create();
+
+        VirtualNetworkToDelete virtualNetworkToDelete = new VirtualNetworkToDelete(id);
+
+        String strVolumeToDeleteJson = gson.toJson(virtualNetworkToDelete);
+
+        String strVirtualNetworkToDeleteResultJson = executeJsonRpc(strVolumeToDeleteJson);
+
+        JsonError jsonError = gson.fromJson(strVirtualNetworkToDeleteResultJson, JsonError.class);
+
+        if (jsonError.error != null) {
+            throw new IllegalStateException(jsonError.error.message);
+        }
+    }
+
+    public SolidFireVolume getVolume(long lVolumeId) {
+        final Gson gson = new GsonBuilder().create();
+
+        VolumeToGet volumeToGet = new VolumeToGet(lVolumeId);
+
+        String strVolumeToGetJson = gson.toJson(volumeToGet);
+
+        String strVolumeGetResultJson = executeJsonRpc(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);
+        long lTotalSize = getVolumeTotalSize(volumeGetResult, lVolumeId);
+
+        return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus, lTotalSize);
+    }
+
+    public long createVolume(String name, long accountId, long totalSizeInGBs, long minIops, long maxIops, long burstIops) {
+        final Gson gson = new GsonBuilder().create();
+
+        long totalSizeInBytes = convertGBsToBytes(totalSizeInGBs);
+
+        VolumeToCreate volumeToCreate = new VolumeToCreate(name, accountId, totalSizeInBytes, true, minIops, maxIops, burstIops);
+
+        String strVolumeToCreateJson = gson.toJson(volumeToCreate);
+
+        String strVolumeCreateResultJson = executeJsonRpc(strVolumeToCreateJson);
+
+        VolumeCreateResult volumeCreateResult = gson.fromJson(strVolumeCreateResultJson, VolumeCreateResult.class);
+
+        verifyResult(volumeCreateResult.result, strVolumeCreateResultJson, gson);
+
+        return volumeCreateResult.result.volumeID;
+    }
+
+    public void modifyVolume(long volumeId, long totalSizeInGBs, long minIops, long maxIops, long burstIops)
+    {
+        final Gson gson = new GsonBuilder().create();
+
+        long totalSizeInBytes = convertGBsToBytes(totalSizeInGBs);
+
+        VolumeToModify volumeToModify = new VolumeToModify(volumeId, totalSizeInBytes, minIops, maxIops, burstIops);
+
+        String strVolumeToModifyJson = gson.toJson(volumeToModify);
+
+        String strVolumeModifyResultJson = executeJsonRpc(strVolumeToModifyJson);
+
+        JsonError jsonError = gson.fromJson(strVolumeModifyResultJson, JsonError.class);
+
+        if (jsonError.error != null) {
+            throw new IllegalStateException(jsonError.error.message);
+        }
+    }
+
+    public void deleteVolume(long id)
+    {
+        final Gson gson = new GsonBuilder().create();
+
+        VolumeToDelete volumeToDelete = new VolumeToDelete(id);
+
+        String strVolumeToDeleteJson = gson.toJson(volumeToDelete);
+
+        String strVolumeToDeleteResultJson = executeJsonRpc(strVolumeToDeleteJson);
+
+        JsonError jsonError = gson.fromJson(strVolumeToDeleteResultJson, JsonError.class);
+
+        if (jsonError.error != null) {
+            throw new IllegalStateException(jsonError.error.message);
+        }
+    }
+
+    public long createSolidFireAccount(String strAccountName)
+    {
+        final Gson gson = new GsonBuilder().create();
+
+        AccountToAdd accountToAdd = new AccountToAdd(strAccountName);
+
+        String strAccountAddJson = gson.toJson(accountToAdd);
+
+        String strAccountAddResultJson = executeJsonRpc(strAccountAddJson);
+
+        AccountAddResult accountAddResult = gson.fromJson(strAccountAddResultJson, AccountAddResult.class);
+
+        verifyResult(accountAddResult.result, strAccountAddResultJson, gson);
+
+        return accountAddResult.result.accountID;
+    }
+
+    public SolidFireAccount getSolidFireAccount(String sfAccountName) {
+        try {
+            return getSolidFireAccountByName(sfAccountName);
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    public SolidFireAccount getSolidFireAccountById(long lSfAccountId)
+    {
+        final Gson gson = new GsonBuilder().create();
+
+        AccountToGetById accountToGetById = new AccountToGetById(lSfAccountId);
+
+        String strAccountToGetByIdJson = gson.toJson(accountToGetById);
+
+        String strAccountGetByIdResultJson = executeJsonRpc(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 SolidFireAccount getSolidFireAccountByName(String strSfAccountName)
+    {
+        final Gson gson = new GsonBuilder().create();
+
+        AccountToGetByName accountToGetByName = new AccountToGetByName(strSfAccountName);
+
+        String strAccountToGetByNameJson = gson.toJson(accountToGetByName);
+
+        String strAccountGetByNameResultJson = executeJsonRpc(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);
+    }
+
+    private String executeJsonRpc(String strJsonToExecute) {
+        DefaultHttpClient httpClient = null;
+        StringBuilder sb = new StringBuilder();
+
+        try {
+            StringEntity input = new StringEntity(strJsonToExecute);
+
+            input.setContentType("application/json");
+
+            httpClient = getHttpClient(_managementPort);
+
+            URI uri = new URI("https://" + _managementVip + ":" + _managementPort + "/json-rpc/7.0");
+            AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_SCHEME);
+            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(_clusterAdminUsername, _clusterAdminPassword);
+
+            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 (Throwable t) {
+            s_logger.error(t.getMessage());
+
+            throw new CloudRuntimeException(t.getMessage());
+        } finally {
+            if (httpClient != null) {
+                try {
+                    httpClient.getConnectionManager().shutdown();
+                } catch (Exception t) {
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private static DefaultHttpClient getHttpClient(int iPort) {
+        DefaultHttpClient client = null;
+
+        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);
+            client = new DefaultHttpClient();
+
+            return new DefaultHttpClient(mgr, client.getParams());
+        } catch (NoSuchAlgorithmException ex) {
+            s_logger.error(ex.getMessage());
+
+            throw new CloudRuntimeException(ex.getMessage());
+        } catch (KeyManagementException ex) {
+            s_logger.error(ex.getMessage());
+
+            throw new CloudRuntimeException(ex.getMessage());
+        }
+        finally {
+            if (client != null) {
+                try {
+                    client.close();
+                }
+                catch (Throwable t) {
+                    s_logger.error(t.getMessage());
+
+                    throw t;
+                }
+            }
+        }
+    }
+
+    private static boolean isSuccess(int iCode) {
+        return iCode >= 200 && iCode < 300;
+    }
+
+    @SuppressWarnings("unused")
+    private static final class ClusterInfoToGet {
+        private final String method = "GetClusterInfo";
+    }
+
+    @SuppressWarnings("unused")
+    private static final class VirtualNetworkToAdd {
+        private final String method = "AddVirtualNetwork";
+        private final VirtualNetworkToAddParams params;
+
+        public VirtualNetworkToAdd(String name, String tag, String startIp, int size, String netmask, String svip) {
+            params = new VirtualNetworkToAddParams(name, tag, startIp, size, netmask, svip);
+        }
+
+        private static final class VirtualNetworkToAddParams {
+            private final String name;
+            private final String virtualNetworkTag;
+            private final AddressBlock[] addressBlocks = new AddressBlock[1];
+            private final String netmask;
+            private final String svip;
+
+            public VirtualNetworkToAddParams(String name, String tag, String startIp, int size, String netmask, String svip) {
+                this.name = name;
+                this.virtualNetworkTag = tag;
+
+                this.addressBlocks[0] = new AddressBlock(startIp, size);
+
+                this.netmask = netmask;
+                this.svip = svip;
+            }
+
+            private static final class AddressBlock {
+                private final String start;
+                private final int size;
+
+                public AddressBlock(String start, int size) {
+                    this.start = start;
+                    this.size = size;
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private static final class VirtualNetworkToModify {
+        private final String method = "ModifyVirtualNetwork";
+        private final VirtualNetworkToModifyParams params;
+
+        public VirtualNetworkToModify(long id, String name, String startIp, int size, String netmask) {
+            params = new VirtualNetworkToModifyParams(id, name, startIp, size, netmask);
+        }
+
+        private static final class VirtualNetworkToModifyParams {
+            private final long virtualNetworkID;
+            private final String name;
+            private final AddressBlock[] addressBlocks = new AddressBlock[1];
+            private final String netmask;
+
+            public VirtualNetworkToModifyParams(long id, String name, String startIp, int size, String netmask) {
+                this.virtualNetworkID = id;
+                this.name = name;
+
+                this.addressBlocks[0] = new AddressBlock(startIp, size);
+
+                this.netmask = netmask;
+            }
+
+            private static final class AddressBlock {
+                private final String start;
+                private final int size;
+
+                public AddressBlock(String start, int size) {
+                    this.start = start;
+                    this.size = size;
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private static final class VirtualNetworkToDelete
+    {
+        private final String method = "RemoveVirtualNetwork";
+        private final VirtualNetworkToDeleteParams params;
+
+        private VirtualNetworkToDelete(long id) {
+            params = new VirtualNetworkToDeleteParams(id);
+        }
+
+        private static final class VirtualNetworkToDeleteParams {
+            private final long virtualNetworkID;
+
+            private VirtualNetworkToDeleteParams(long id) {
+                virtualNetworkID = id;
+            }
+        }
+    }
+
+    @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 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 VolumeToModify
+    {
+        private final String method = "ModifyVolume";
+        private final VolumeToModifyParams params;
+
+        private VolumeToModify(long id, long totalSize, long minIOPS, long maxIOPS, long burstIOPS)
+        {
+            params = new VolumeToModifyParams(id, totalSize, minIOPS, maxIOPS, burstIOPS);
+        }
+
+        private static final class VolumeToModifyParams
+        {
+            private final long volumeID;
+            private final long totalSize;
+            private final VolumeToModifyParamsQoS qos;
+
+            private VolumeToModifyParams(long id, long totalSize, long minIOPS, long maxIOPS, long burstIOPS)
+            {
+                this.volumeID = id;
+                this.totalSize = totalSize;
+
+                this.qos = new VolumeToModifyParamsQoS(minIOPS, maxIOPS, burstIOPS);
+            }
+        }
+
+        private static final class VolumeToModifyParamsQoS {
+            private final long minIOPS;
+            private final long maxIOPS;
+            private final long burstIOPS;
+
+            private VolumeToModifyParamsQoS(long minIOPS, long maxIOPS, long burstIOPS) {
+                this.minIOPS = minIOPS;
+                this.maxIOPS = maxIOPS;
+                this.burstIOPS = burstIOPS;
+            }
+        }
+    }
+
+    @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 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;
+            }
+        }
+    }
+
+    private static final class ClusterInfoGetResult
+    {
+        private Result result;
+
+        private static final class Result
+        {
+            private ClusterInfo clusterInfo;
+
+            private static final class ClusterInfo
+            {
+                private String name;
+            }
+        }
+    }
+
+    private static final class VirtualNetworkAddResult
+    {
+        private Result result;
+
+        private static final class Result
+        {
+            private long virtualNetworkID;
+        }
+    }
+
+    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 long totalSize;
+            }
+        }
+    }
+
+    private static final class VolumeCreateResult {
+        private Result result;
+
+        private static final class Result {
+            private long volumeID;
+        }
+    }
+
+    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 JsonError
+    {
+        private Error error;
+
+        private static final class Error {
+            private String message;
+        }
+    }
+
+    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 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 long _totalSize;
+
+        public SolidFireVolume(long id, String name, String iqn,
+                long accountId, String status, long totalSize)
+        {
+            _id = id;
+            _name = name;
+            _iqn = iqn;
+            _accountId = accountId;
+            _status = status;
+            _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 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 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;
+        }
+    }
+
+    private static long convertGBsToBytes(long gbs) {
+        return gbs * 1024 * 1024 * 1024;
+    }
+
+    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 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 + ".");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/49ca3b40/plugins/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 1706fe8..cf74dc4 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -38,6 +38,7 @@
   </build>
   <modules>
     <module>api/rate-limit</module>
+    <module>api/solidfire</module>
     <module>api/solidfire-intg-test</module>
     <module>api/discovery</module>
     <module>acl/static-role-based</module>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/49ca3b40/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java
----------------------------------------------------------------------
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 dcbabe6..6c6c41d 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
@@ -259,8 +259,10 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         Integer hypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve();
 
         if (hypervisorSnapshotReserve != null) {
-            if (hypervisorSnapshotReserve < 50) {
-                hypervisorSnapshotReserve = 50;
+            int minimumHypervisorSnapshotReserve = 10;
+
+            if (hypervisorSnapshotReserve < minimumHypervisorSnapshotReserve) {
+                hypervisorSnapshotReserve = minimumHypervisorSnapshotReserve;
             }
 
             volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f);