content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.cloudstack.storage.datastore.lifecycle;
  
  import java.text.NumberFormat;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.inject.Inject;
  
  import com.cloud.host.Host;
  import com.cloud.storage.dao.StoragePoolAndAccessGroupMapDao;
  import org.apache.cloudstack.context.CallContext;
  import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
  import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
  import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
  import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
  import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
  import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
  import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
  import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
  import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
  import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
  import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
  import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
  import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
  
  import com.cloud.agent.AgentManager;
  import com.cloud.agent.api.Answer;
  import com.cloud.agent.api.CreateStoragePoolCommand;
  import com.cloud.agent.api.DeleteStoragePoolCommand;
  import com.cloud.agent.api.ModifyTargetsCommand;
  import com.cloud.agent.api.StoragePoolInfo;
  import com.cloud.dc.ClusterVO;
  import com.cloud.dc.dao.ClusterDao;
  import com.cloud.dc.dao.DataCenterDao;
  import com.cloud.host.HostVO;
  import com.cloud.host.dao.HostDao;
  import com.cloud.hypervisor.Hypervisor.HypervisorType;
  import com.cloud.resource.ResourceManager;
  import com.cloud.storage.Storage.StoragePoolType;
  import com.cloud.storage.StorageManager;
  import com.cloud.storage.StoragePool;
  import com.cloud.storage.StoragePoolAutomation;
  import com.cloud.storage.StoragePoolHostVO;
  import com.cloud.storage.VMTemplateStoragePoolVO;
  import com.cloud.storage.dao.StoragePoolHostDao;
  import com.cloud.template.TemplateManager;
  import com.cloud.user.Account;
  import com.cloud.user.AccountDetailsDao;
  import com.cloud.user.AccountVO;
  import com.cloud.user.dao.AccountDao;
  import com.cloud.utils.db.GlobalLock;
  import com.cloud.utils.exception.CloudRuntimeException;
  
  public class SolidFireSharedPrimaryDataStoreLifeCycle extends BasePrimaryDataStoreLifeCycleImpl implements PrimaryDataStoreLifeCycle {
      @Inject private AccountDao accountDao;
      @Inject private AccountDetailsDao accountDetailsDao;
      @Inject private AgentManager agentMgr;
      @Inject private ClusterDao clusterDao;
      @Inject private DataCenterDao zoneDao;
      @Inject private HostDao hostDao;
      @Inject private PrimaryDataStoreDao primaryDataStoreDao;
      @Inject private PrimaryDataStoreHelper primaryDataStoreHelper;
      @Inject private ResourceManager resourceMgr;
      @Inject private StorageManager storageMgr;
      @Inject private StoragePoolAutomation storagePoolAutomation;
      @Inject private StoragePoolDetailsDao storagePoolDetailsDao;
      @Inject private StoragePoolHostDao storagePoolHostDao;
      @Inject private TemplateManager tmpltMgr;
      @Inject
      private StoragePoolAndAccessGroupMapDao storagePoolAndAccessGroupMapDao;
  
      // invoked to add primary storage that is based on the SolidFire plug-in
      @Override
      public DataStore initialize(Map<String, Object> dsInfos) {
          final String CAPACITY_IOPS = "capacityIops";
  
          String url = (String)dsInfos.get("url");
          Long zoneId = (Long)dsInfos.get("zoneId");
          Long podId = (Long)dsInfos.get("podId");
          Long clusterId = (Long)dsInfos.get("clusterId");
          String storagePoolName = (String)dsInfos.get("name");
          String providerName = (String)dsInfos.get("providerName");
          Long capacityBytes = (Long)dsInfos.get("capacityBytes");
          Long capacityIops = (Long)dsInfos.get(CAPACITY_IOPS);
          String tags = (String)dsInfos.get("tags");
          Boolean isTagARule = (Boolean) dsInfos.get("isTagARule");
          @SuppressWarnings("unchecked")
          Map<String, String> details = (Map<String, String>)dsInfos.get("details");
  
          if (podId == null) {
              throw new CloudRuntimeException("The Pod ID must be specified.");
          }
  
          if (clusterId == null) {
              throw new CloudRuntimeException("The Cluster ID must be specified.");
          }
  
          String storageVip = SolidFireUtil.getStorageVip(url);
          int storagePort = SolidFireUtil.getStoragePort(url);
  
          if (capacityBytes == null || capacityBytes <= 0) {
              throw new IllegalArgumentException("'capacityBytes' must be present and greater than 0.");
          }
  
          if (capacityIops == null || capacityIops <= 0) {
              throw new IllegalArgumentException("'capacityIops' must be present and greater than 0.");
          }
  
          HypervisorType hypervisorType = getHypervisorTypeForCluster(clusterId);
  
          if (!isSupportedHypervisorType(hypervisorType)) {
              throw new CloudRuntimeException(hypervisorType + " is not a supported hypervisor type.");
          }
  
          String datacenter = SolidFireUtil.getValue(SolidFireUtil.DATACENTER, url, false);
  
          if (HypervisorType.VMware.equals(hypervisorType) && datacenter == null) {
              throw new CloudRuntimeException("'Datacenter' must be set for hypervisor type of " + HypervisorType.VMware);
          }
  
          PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
  
          parameters.setType(getStorageType(hypervisorType));
          parameters.setZoneId(zoneId);
          parameters.setPodId(podId);
          parameters.setClusterId(clusterId);
          parameters.setName(storagePoolName);
          parameters.setProviderName(providerName);
          parameters.setManaged(false);
          parameters.setCapacityBytes(capacityBytes);
          parameters.setUsedBytes(0);
          parameters.setCapacityIops(capacityIops);
          parameters.setHypervisorType(hypervisorType);
          parameters.setTags(tags);
          parameters.setIsTagARule(isTagARule);
          parameters.setDetails(details);
  
          String managementVip = SolidFireUtil.getManagementVip(url);
          int managementPort = SolidFireUtil.getManagementPort(url);
  
          details.put(SolidFireUtil.MANAGEMENT_VIP, managementVip);
          details.put(SolidFireUtil.MANAGEMENT_PORT, String.valueOf(managementPort));
  
          String clusterAdminUsername = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_ADMIN_USERNAME, url);
          String clusterAdminPassword = SolidFireUtil.getValue(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, url);
  
          details.put(SolidFireUtil.CLUSTER_ADMIN_USERNAME, clusterAdminUsername);
          details.put(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, clusterAdminPassword);
  
          if (capacityBytes < SolidFireUtil.MIN_VOLUME_SIZE) {
              capacityBytes = SolidFireUtil.MIN_VOLUME_SIZE;
          }
  
          long lMinIops = 100;
          long lMaxIops = 15000;
          long lBurstIops = 15000;
  
          try {
              String minIops = SolidFireUtil.getValue(SolidFireUtil.MIN_IOPS, url);
  
              if (minIops != null && minIops.trim().length() > 0) {
                  lMinIops = Long.parseLong(minIops);
              }
          } catch (Exception ex) {
              logger.info("[ignored] error getting Min IOPS: " + ex.getLocalizedMessage());
          }
  
          try {
              String maxIops = SolidFireUtil.getValue(SolidFireUtil.MAX_IOPS, url);
  
              if (maxIops != null && maxIops.trim().length() > 0) {
                  lMaxIops = Long.parseLong(maxIops);
              }
          } catch (Exception ex) {
              logger.info("[ignored] error getting Max IOPS: " + ex.getLocalizedMessage());
          }
  
          try {
              String burstIops = SolidFireUtil.getValue(SolidFireUtil.BURST_IOPS, url);
  
              if (burstIops != null && burstIops.trim().length() > 0) {
                  lBurstIops = Long.parseLong(burstIops);
              }
          } catch (Exception ex) {
              logger.info("[ignored] error getting Burst IOPS: " + ex.getLocalizedMessage());
          }
  
          if (lMinIops > lMaxIops) {
              throw new CloudRuntimeException("The parameter '" + SolidFireUtil.MIN_IOPS + "' must be less than or equal to the parameter '" + SolidFireUtil.MAX_IOPS + "'.");
          }
  
          if (lMaxIops > lBurstIops) {
              throw new CloudRuntimeException("The parameter '" + SolidFireUtil.MAX_IOPS + "' must be less than or equal to the parameter '" + SolidFireUtil.BURST_IOPS + "'.");
          }
  
          if (lMinIops != capacityIops) {
              throw new CloudRuntimeException("The parameter '" + CAPACITY_IOPS + "' must be equal to the parameter '" + SolidFireUtil.MIN_IOPS + "'.");
          }
  
          if (lMinIops > SolidFireUtil.MAX_MIN_IOPS_PER_VOLUME) {
              throw new CloudRuntimeException("This volume's Min IOPS cannot exceed " + NumberFormat.getInstance().format(SolidFireUtil.MAX_MIN_IOPS_PER_VOLUME) + " IOPS.");
          }
  
          if (lMaxIops > SolidFireUtil.MAX_IOPS_PER_VOLUME) {
              throw new CloudRuntimeException("This volume's Max IOPS cannot exceed " + NumberFormat.getInstance().format(SolidFireUtil.MAX_IOPS_PER_VOLUME) + " IOPS.");
          }
  
          if (lBurstIops > SolidFireUtil.MAX_IOPS_PER_VOLUME) {
              throw new CloudRuntimeException("This volume's Burst IOPS cannot exceed " + NumberFormat.getInstance().format(SolidFireUtil.MAX_IOPS_PER_VOLUME) + " IOPS.");
          }
  
          details.put(SolidFireUtil.MIN_IOPS, String.valueOf(lMinIops));
          details.put(SolidFireUtil.MAX_IOPS, String.valueOf(lMaxIops));
          details.put(SolidFireUtil.BURST_IOPS, String.valueOf(lBurstIops));
  
          SolidFireUtil.SolidFireConnection sfConnection = new SolidFireUtil.SolidFireConnection(managementVip, managementPort, clusterAdminUsername, clusterAdminPassword);
  
          SolidFireCreateVolume sfCreateVolume = createSolidFireVolume(sfConnection, storagePoolName, capacityBytes, lMinIops, lMaxIops, lBurstIops);
  
          SolidFireUtil.SolidFireVolume sfVolume = sfCreateVolume.getVolume();
  
          String iqn = sfVolume.getIqn();
  
          details.put(SolidFireUtil.VOLUME_ID, String.valueOf(sfVolume.getId()));
  
          parameters.setUuid(iqn);
  
          if (HypervisorType.VMware.equals(hypervisorType)) {
              String datastore = iqn.replace("/", "_");
              String path = "/" + datacenter + "/" + datastore;
  
              parameters.setHost("VMFS datastore: " + path);
              parameters.setPort(0);
              parameters.setPath(path);
  
              details.put(SolidFireUtil.DATASTORE_NAME, datastore);
              details.put(SolidFireUtil.IQN, iqn);
              details.put(SolidFireUtil.STORAGE_VIP, storageVip);
              details.put(SolidFireUtil.STORAGE_PORT, String.valueOf(storagePort));
          }
          else {
              parameters.setHost(storageVip);
              parameters.setPort(storagePort);
              parameters.setPath(iqn);
          }
  
          ClusterVO cluster = clusterDao.findById(clusterId);
  
          GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid());
  
          if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) {
              String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid();
  
              logger.debug(errMsg);
  
              throw new CloudRuntimeException(errMsg);
          }
  
          DataStore dataStore = null;
  
          try {
              // this adds a row in the cloud.storage_pool table for this SolidFire volume
              dataStore = primaryDataStoreHelper.createPrimaryDataStore(parameters);
  
              // now that we have a DataStore (we need the id from the DataStore instance), we can create a Volume Access Group, if need be, and
              // place the newly created volume in the Volume Access Group
              List<HostVO> hosts = hostDao.findByClusterId(clusterId);
  
              String clusterUuId = clusterDao.findById(clusterId).getUuid();
  
              SolidFireUtil.placeVolumeInVolumeAccessGroups(sfConnection, sfVolume.getId(), hosts, clusterUuId);
  
              SolidFireUtil.SolidFireAccount sfAccount = sfCreateVolume.getAccount();
              Account csAccount = CallContext.current().getCallingAccount();
  
              SolidFireUtil.updateCsDbWithSolidFireAccountInfo(csAccount.getId(), sfAccount, dataStore.getId(), accountDetailsDao);
          } catch (Exception ex) {
              if (dataStore != null) {
                  primaryDataStoreDao.expunge(dataStore.getId());
              }
  
              throw new CloudRuntimeException(ex.getMessage());
          }
          finally {
              lock.unlock();
              lock.releaseRef();
          }
  
          return dataStore;
      }
  
      private HypervisorType getHypervisorTypeForCluster(long clusterId) {
          ClusterVO cluster = clusterDao.findById(clusterId);
  
          if (cluster == null) {
              throw new CloudRuntimeException("Cluster ID '" + clusterId + "' was not found in the database.");
          }
  
          return cluster.getHypervisorType();
      }
  
      private StoragePoolType getStorageType(HypervisorType hypervisorType) {
          if (HypervisorType.XenServer.equals(hypervisorType)) {
              return StoragePoolType.IscsiLUN;
          }
  
          if (HypervisorType.VMware.equals(hypervisorType)) {
              return StoragePoolType.VMFS;
          }
  
          throw new CloudRuntimeException("The 'hypervisor' parameter must be '" + HypervisorType.XenServer + "' or '" + HypervisorType.VMware + "'.");
      }
  
      private class SolidFireCreateVolume {
          private final SolidFireUtil.SolidFireVolume _sfVolume;
          private final SolidFireUtil.SolidFireAccount _sfAccount;
  
          SolidFireCreateVolume(SolidFireUtil.SolidFireVolume sfVolume, SolidFireUtil.SolidFireAccount sfAccount) {
              _sfVolume = sfVolume;
              _sfAccount = sfAccount;
          }
  
          public SolidFireUtil.SolidFireVolume getVolume() {
              return _sfVolume;
          }
  
          public SolidFireUtil.SolidFireAccount getAccount() {
              return _sfAccount;
          }
      }
  
      private SolidFireCreateVolume createSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection,
              String volumeName, long volumeSize, long minIops, long maxIops, long burstIops) {
          try {
              Account csAccount = CallContext.current().getCallingAccount();
              long csAccountId = csAccount.getId();
              AccountVO accountVo = accountDao.findById(csAccountId);
  
              String sfAccountName = SolidFireUtil.getSolidFireAccountName(accountVo.getUuid(), csAccountId);
  
              SolidFireUtil.SolidFireAccount sfAccount = SolidFireUtil.getAccount(sfConnection, sfAccountName);
  
              if (sfAccount == null) {
                  long sfAccountNumber = SolidFireUtil.createAccount(sfConnection, sfAccountName);
  
                  sfAccount = SolidFireUtil.getAccountById(sfConnection, sfAccountNumber);
              }
  
              long sfVolumeId = SolidFireUtil.createVolume(sfConnection, SolidFireUtil.getSolidFireVolumeName(volumeName), sfAccount.getId(), volumeSize,
                      true, null, minIops, maxIops, burstIops);
              SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getVolume(sfConnection, sfVolumeId);
  
              return new SolidFireCreateVolume(sfVolume, sfAccount);
          } catch (Throwable e) {
              throw new CloudRuntimeException("Failed to create a SolidFire volume: " + e.toString());
          }
      }
  
      @Override
      public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {
          return true;
      }
  
      @Override
      public boolean attachCluster(DataStore store, ClusterScope scope) {
          PrimaryDataStoreInfo primaryDataStoreInfo = (PrimaryDataStoreInfo)store;
  
          List<HostVO> hostsToConnect = resourceMgr.getEligibleUpHostsInClusterForStorageConnection(primaryDataStoreInfo);
  
          boolean success = false;
          logger.debug(String.format("Attaching the pool to each of the hosts %s in the cluster: %s", hostsToConnect,  clusterDao.findById(primaryDataStoreInfo.getClusterId())));
  
          for (HostVO host : hostsToConnect) {
              success = createStoragePool(host, primaryDataStoreInfo);
  
              if (success) {
                  break;
              }
          }
  
          if (!success) {
              throw new CloudRuntimeException("Unable to create storage in cluster " + clusterDao.findById(primaryDataStoreInfo.getClusterId()));
          }
  
          List<HostVO> poolHosts = new ArrayList<>();
  
          for (HostVO host : hostsToConnect) {
              try {
                  storageMgr.connectHostToSharedPool(host, primaryDataStoreInfo.getId());
  
                  poolHosts.add(host);
              } catch (Exception e) {
                  logger.warn("Unable to establish a connection between " + host + " and " + primaryDataStoreInfo, e);
              }
          }
  
          if (poolHosts.isEmpty()) {
              logger.warn("No host can access storage pool '{}' on cluster '{}'.", primaryDataStoreInfo, clusterDao.findById(primaryDataStoreInfo.getClusterId()));
  
              primaryDataStoreDao.expunge(primaryDataStoreInfo.getId());
  
              throw new CloudRuntimeException("Failed to access storage pool");
          }
  
          primaryDataStoreHelper.attachCluster(store);
  
          return true;
      }
  
      private boolean createStoragePool(HostVO host, StoragePool storagePool) {
          long hostId = host.getId();
          HypervisorType hypervisorType = host.getHypervisorType();
          CreateStoragePoolCommand cmd = new CreateStoragePoolCommand(true, storagePool);
  
          if (HypervisorType.VMware.equals(hypervisorType)) {
              cmd.setCreateDatastore(true);
  
              Map<String, String> details = new HashMap<>();
  
              StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME);
  
              details.put(CreateStoragePoolCommand.DATASTORE_NAME, storagePoolDetail.getValue());
  
              storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN);
  
              details.put(CreateStoragePoolCommand.IQN, storagePoolDetail.getValue());
  
              storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP);
  
              details.put(CreateStoragePoolCommand.STORAGE_HOST, storagePoolDetail.getValue());
  
              storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT);
  
              details.put(CreateStoragePoolCommand.STORAGE_PORT, storagePoolDetail.getValue());
  
              cmd.setDetails(details);
          }
  
          Answer answer = agentMgr.easySend(hostId, cmd);
  
          if (answer != null && answer.getResult()) {
              return true;
          } else {
              primaryDataStoreDao.expunge(storagePool.getId());
  
              final String msg;
  
              if (answer != null) {
                  msg = String.format("Cannot create storage pool through host '%s' due to the following: %s", host, answer.getDetails());
              } else {
                  msg = String.format("Cannot create storage pool through host '%s' due to CreateStoragePoolCommand returns null", host);
              }
  
              logger.warn(msg);
  
              throw new CloudRuntimeException(msg);
          }
      }
  
      @Override
      public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) {
          return true;
      }
  
      @Override
      public boolean maintain(DataStore dataStore) {
          storagePoolAutomation.maintain(dataStore);
          primaryDataStoreHelper.maintain(dataStore);
  
          return true;
      }
  
      @Override
      public boolean cancelMaintain(DataStore store) {
          primaryDataStoreHelper.cancelMaintain(store);
          storagePoolAutomation.cancelMaintain(store);
  
          return true;
      }
  
      // invoked to delete primary storage that is based on the SolidFire plug-in
      @Override
      public boolean deleteDataStore(DataStore dataStore) {
          List<StoragePoolHostVO> hostPoolRecords = storagePoolHostDao.listByPoolId(dataStore.getId());
  
          HypervisorType hypervisorType = null;
  
          if (hostPoolRecords.size() > 0 ) {
              hypervisorType = getHypervisorType(hostPoolRecords.get(0).getHostId());
          }
  
          if (!isSupportedHypervisorType(hypervisorType)) {
              throw new CloudRuntimeException(hypervisorType + " is not a supported hypervisor type.");
          }
  
          StoragePool storagePool = (StoragePool)dataStore;
          StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(storagePool.getId());
          List<VMTemplateStoragePoolVO> unusedTemplatesInPool = tmpltMgr.getUnusedTemplatesInPool(storagePoolVO);
  
          for (VMTemplateStoragePoolVO templatePoolVO : unusedTemplatesInPool) {
              tmpltMgr.evictTemplateFromStoragePool(templatePoolVO);
          }
  
          Long clusterId = null;
          Long hostId = null;
  
          for (StoragePoolHostVO host : hostPoolRecords) {
              DeleteStoragePoolCommand deleteCmd = new DeleteStoragePoolCommand(storagePool);
  
              if (HypervisorType.VMware.equals(hypervisorType)) {
                  deleteCmd.setRemoveDatastore(true);
  
                  Map<String, String> details = new HashMap<>();
  
                  StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.DATASTORE_NAME);
  
                  details.put(DeleteStoragePoolCommand.DATASTORE_NAME, storagePoolDetail.getValue());
  
                  storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.IQN);
  
                  details.put(DeleteStoragePoolCommand.IQN, storagePoolDetail.getValue());
  
                  storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_VIP);
  
                  details.put(DeleteStoragePoolCommand.STORAGE_HOST, storagePoolDetail.getValue());
  
                  storagePoolDetail = storagePoolDetailsDao.findDetail(storagePool.getId(), SolidFireUtil.STORAGE_PORT);
  
                  details.put(DeleteStoragePoolCommand.STORAGE_PORT, storagePoolDetail.getValue());
  
                  deleteCmd.setDetails(details);
              }
  
              final Answer answer = agentMgr.easySend(host.getHostId(), deleteCmd);
  
              if (answer != null && answer.getResult()) {
                  HostVO hostVO = hostDao.findById(host.getHostId());
                  if (hostVO != null) {
                      clusterId = hostVO.getClusterId();
                      hostId = hostVO.getId();
                  }
                  logger.info("Successfully deleted storage pool using Host {} with ID {}", hostVO, host.getHostId());
                  break;
              }
              else {
                  HostVO hostVO = hostDao.findById(host.getHostId());
                  if (answer != null) {
                      logger.error("Failed to delete storage pool using Host {} with ID: {}: {}", hostVO, host.getHostId(), answer.getResult());
                  }
                  else {
                      logger.error("Failed to delete storage pool using Host {} with ID: {}", hostVO, host.getHostId());
                  }
              }
          }
  
          if (clusterId != null) {
              ClusterVO cluster = clusterDao.findById(clusterId);
  
              GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid());
  
              if (!lock.lock(SolidFireUtil.LOCK_TIME_IN_SECONDS)) {
                  String errMsg = "Couldn't lock the DB on the following string: " + cluster.getUuid();
  
                  logger.debug(errMsg);
  
                  throw new CloudRuntimeException(errMsg);
              }
  
              try {
                  long sfVolumeId = getVolumeId(storagePool.getId());
  
                  SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao);
  
                  List<SolidFireUtil.SolidFireVag> sfVags = SolidFireUtil.getAllVags(sfConnection);
  
                  for (SolidFireUtil.SolidFireVag sfVag : sfVags) {
                      if (SolidFireUtil.sfVagContains(sfVag, sfVolumeId, clusterId, hostDao)) {
                          SolidFireUtil.removeVolumeIdsFromSolidFireVag(sfConnection, sfVag.getId(), new Long[] { sfVolumeId });
                      }
                  }
              }
              finally {
                  lock.unlock();
                  lock.releaseRef();
              }
          }
  
          if (hostId != null) {
              handleTargetsForVMware(hostId, storagePool.getId());
          }
  
          deleteSolidFireVolume(storagePool.getId());
  
          return primaryDataStoreHelper.deletePrimaryDataStore(dataStore);
      }
  
      private void handleTargetsForVMware(long hostId, long storagePoolId) {
          HostVO host = hostDao.findById(hostId);
  
          if (host.getHypervisorType() == HypervisorType.VMware) {
              String storageAddress = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_VIP).getValue();
              int storagePort = Integer.parseInt(storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.STORAGE_PORT).getValue());
              String iqn = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.IQN).getValue();
  
              ModifyTargetsCommand cmd = new ModifyTargetsCommand();
  
              List<Map<String, String>> targets = new ArrayList<>();
  
              Map<String, String> target = new HashMap<>();
  
              target.put(ModifyTargetsCommand.STORAGE_HOST, storageAddress);
              target.put(ModifyTargetsCommand.STORAGE_PORT, String.valueOf(storagePort));
              target.put(ModifyTargetsCommand.IQN, iqn);
  
              targets.add(target);
  
              cmd.setTargets(targets);
              cmd.setApplyToAllHostsInCluster(true);
              cmd.setAdd(false);
              cmd.setTargetTypeToRemove(ModifyTargetsCommand.TargetTypeToRemove.DYNAMIC);
              cmd.setRemoveAsync(true);
  
              sendModifyTargetsCommand(cmd, host);
          }
      }
  
      private void sendModifyTargetsCommand(ModifyTargetsCommand cmd, Host host) {
          Answer answer = agentMgr.easySend(host.getId(), cmd);
  
          if (answer == null) {
              String msg = "Unable to get an answer to the modify targets command";
  
              logger.warn(msg);
          }
          else if (!answer.getResult()) {
              String msg = String.format("Unable to modify target on the following host: %s", host);
  
              logger.warn(msg);
          }
      }
  
      private void deleteSolidFireVolume(long storagePoolId) {
          SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
  
          long sfVolumeId = getVolumeId(storagePoolId);
  
          SolidFireUtil.deleteVolume(sfConnection, sfVolumeId);
      }
  
      private long getVolumeId(long storagePoolId) {
          StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.VOLUME_ID);
  
          String volumeId = storagePoolDetail.getValue();
  
          return Long.parseLong(volumeId);
      }
  
      private long getIopsValue(long storagePoolId, String iopsKey) {
          StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, iopsKey);
  
          String iops = storagePoolDetail.getValue();
  
          return Long.parseLong(iops);
      }
  
      private static boolean isSupportedHypervisorType(HypervisorType hypervisorType) {
          return HypervisorType.XenServer.equals(hypervisorType) || HypervisorType.VMware.equals(hypervisorType);
      }
  
      private HypervisorType getHypervisorType(long hostId) {
          HostVO host = hostDao.findById(hostId);
  
          if (host != null) {
              return host.getHypervisorType();
          }
  
          return HypervisorType.None;
      }
  
      /* (non-Javadoc)
       * @see org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle#migrateToObjectStore(org.apache.cloudstack.engine.subsystem.api.storage.DataStore)
       */
      @Override
      public boolean migrateToObjectStore(DataStore store) {
          return false;
      }
  
      @Override
      public void updateStoragePool(StoragePool storagePool, Map<String, String> details) {
          String strCapacityBytes = details.get(PrimaryDataStoreLifeCycle.CAPACITY_BYTES);
          String strCapacityIops = details.get(PrimaryDataStoreLifeCycle.CAPACITY_IOPS);
  
          Long capacityBytes = strCapacityBytes != null ? Long.parseLong(strCapacityBytes) : null;
          Long capacityIops = strCapacityIops != null ? Long.parseLong(strCapacityIops) : null;
  
          SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao);
  
          long size = capacityBytes != null ? capacityBytes : storagePool.getCapacityBytes();
  
          long currentMinIops = getIopsValue(storagePool.getId(), SolidFireUtil.MIN_IOPS);
          long currentMaxIops = getIopsValue(storagePool.getId(), SolidFireUtil.MAX_IOPS);
          long currentBurstIops = getIopsValue(storagePool.getId(), SolidFireUtil.BURST_IOPS);
  
          long minIops = currentMinIops;
          long maxIops = currentMaxIops;
          long burstIops = currentBurstIops;
  
          if (capacityIops != null) {
              if (capacityIops > SolidFireUtil.MAX_MIN_IOPS_PER_VOLUME) {
                  throw new CloudRuntimeException("This volume cannot exceed " + NumberFormat.getInstance().format(SolidFireUtil.MAX_MIN_IOPS_PER_VOLUME) + " IOPS.");
              }
  
              float maxPercentOfMin = currentMaxIops / (float)currentMinIops;
              float burstPercentOfMax = currentBurstIops / (float)currentMaxIops;
  
              minIops = capacityIops;
              maxIops = (long)(minIops * maxPercentOfMin);
              burstIops = (long)(maxIops * burstPercentOfMax);
  
              if (maxIops > SolidFireUtil.MAX_IOPS_PER_VOLUME) {
                  maxIops = SolidFireUtil.MAX_IOPS_PER_VOLUME;
              }
  
              if (burstIops > SolidFireUtil.MAX_IOPS_PER_VOLUME) {
                  burstIops = SolidFireUtil.MAX_IOPS_PER_VOLUME;
              }
          }
  
          SolidFireUtil.modifyVolume(sfConnection, getVolumeId(storagePool.getId()), size, null, minIops, maxIops, burstIops);
  
          SolidFireUtil.updateCsDbWithSolidFireIopsInfo(storagePool.getId(), primaryDataStoreDao, storagePoolDetailsDao, minIops, maxIops, burstIops);
      }
  
      @Override
      public void enableStoragePool(DataStore dataStore) {
          primaryDataStoreHelper.enable(dataStore);
      }
  
      @Override
      public void disableStoragePool(DataStore dataStore) {
          primaryDataStoreHelper.disable(dataStore);
      }
  }
  