http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5a33835d/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/5a33835d/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/5a33835d/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/5a33835d/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/5a33835d/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);