@Override
  public void createAsync(
      DataStore dataStore,
      DataObject dataObject,
      AsyncCompletionCallback<CreateCmdResult> callback) {
    String iqn = null;
    String errMsg = null;

    if (dataObject.getType() == DataObjectType.VOLUME) {
      VolumeInfo volumeInfo = (VolumeInfo) dataObject;
      AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
      String sfAccountName = getSfAccountName(account.getUuid(), account.getAccountId());

      long storagePoolId = dataStore.getId();
      SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);

      if (!sfAccountExists(sfAccountName, sfConnection)) {
        SolidFireUtil.SolidFireAccount sfAccount =
            createSolidFireAccount(sfAccountName, sfConnection);

        updateCsDbWithAccountInfo(account.getId(), sfAccount);
      }

      SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(volumeInfo, sfConnection);

      iqn = sfVolume.getIqn();

      VolumeVO volume = this._volumeDao.findById(volumeInfo.getId());

      volume.set_iScsiName(iqn);
      volume.setFolder(String.valueOf(sfVolume.getId()));
      volume.setPoolType(StoragePoolType.IscsiLUN);
      volume.setPoolId(storagePoolId);

      _volumeDao.update(volume.getId(), volume);

      StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId());

      long capacityBytes = storagePool.getCapacityBytes();
      long usedBytes = storagePool.getUsedBytes();

      usedBytes += volumeInfo.getSize();

      storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes);

      _storagePoolDao.update(storagePoolId, storagePool);
    } else {
      errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
    }

    // path = iqn
    // size is pulled from DataObject instance, if errMsg is null
    CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg));

    result.setResult(errMsg);

    callback.complete(result);
  }
  protected Void createVolumeAsyncCallback(
      AsyncCallbackDispatcher<? extends BaseImageStoreDriverImpl, DownloadAnswer> callback,
      CreateContext<CreateCmdResult> context) {
    DownloadAnswer answer = callback.getResult();
    DataObject obj = context.data;
    DataStore store = obj.getDataStore();

    VolumeDataStoreVO volStoreVO = _volumeStoreDao.findByStoreVolume(store.getId(), obj.getId());
    if (volStoreVO != null) {
      if (volStoreVO.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED) {
        if (s_logger.isDebugEnabled()) {
          s_logger.debug(
              "Volume is already in DOWNLOADED state, ignore further incoming DownloadAnswer");
        }
        return null;
      }
      VolumeDataStoreVO updateBuilder = _volumeStoreDao.createForUpdate();
      updateBuilder.setDownloadPercent(answer.getDownloadPct());
      updateBuilder.setDownloadState(answer.getDownloadStatus());
      updateBuilder.setLastUpdated(new Date());
      updateBuilder.setErrorString(answer.getErrorString());
      updateBuilder.setJobId(answer.getJobId());
      updateBuilder.setLocalDownloadPath(answer.getDownloadPath());
      updateBuilder.setInstallPath(answer.getInstallPath());
      updateBuilder.setSize(answer.getTemplateSize());
      updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
      _volumeStoreDao.update(volStoreVO.getId(), updateBuilder);
      // update size in volume table
      VolumeVO volUpdater = volumeDao.createForUpdate();
      volUpdater.setSize(answer.getTemplateSize());
      volumeDao.update(obj.getId(), volUpdater);
    }

    AsyncCompletionCallback<CreateCmdResult> caller = context.getParentCallback();

    if (answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR
        || answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.ABANDONED
        || answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.UNKNOWN) {
      CreateCmdResult result = new CreateCmdResult(null, null);
      result.setSuccess(false);
      result.setResult(answer.getErrorString());
      caller.complete(result);
    } else if (answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED) {
      CreateCmdResult result = new CreateCmdResult(null, null);
      caller.complete(result);
    }
    return null;
  }
  @Override
  public void handleVolumeSync(HostVO ssHost) {
    if (ssHost == null) {
      s_logger.warn("Huh? ssHost is null");
      return;
    }
    long sserverId = ssHost.getId();
    if (!(ssHost.getType() == Host.Type.SecondaryStorage
        || ssHost.getType() == Host.Type.LocalSecondaryStorage)) {
      s_logger.warn("Huh? Agent id " + sserverId + " is not secondary storage host");
      return;
    }

    Map<Long, TemplateInfo> volumeInfos = listVolume(ssHost);
    if (volumeInfos == null) {
      return;
    }

    List<VolumeHostVO> dbVolumes = _volumeHostDao.listBySecStorage(sserverId);
    List<VolumeHostVO> toBeDownloaded = new ArrayList<VolumeHostVO>(dbVolumes);
    for (VolumeHostVO volumeHost : dbVolumes) {
      VolumeVO volume = _volumeDao.findById(volumeHost.getVolumeId());
      // Exists then don't download
      if (volumeInfos.containsKey(volume.getId())) {
        TemplateInfo volInfo = volumeInfos.remove(volume.getId());
        toBeDownloaded.remove(volumeHost);
        s_logger.info(
            "Volume Sync found " + volume.getUuid() + " already in the volume host table");
        if (volumeHost.getDownloadState() != Status.DOWNLOADED) {
          volumeHost.setErrorString("");
        }
        if (volInfo.isCorrupted()) {
          volumeHost.setDownloadState(Status.DOWNLOAD_ERROR);
          String msg = "Volume " + volume.getUuid() + " is corrupted on secondary storage ";
          volumeHost.setErrorString(msg);
          s_logger.info("msg");
          if (volumeHost.getDownloadUrl() == null) {
            msg =
                "Volume ("
                    + volume.getUuid()
                    + ") with install path "
                    + volInfo.getInstallPath()
                    + "is corrupted, please check in secondary storage: "
                    + volumeHost.getHostId();
            s_logger.warn(msg);
          } else {
            toBeDownloaded.add(volumeHost);
          }

        } else { // Put them in right status
          volumeHost.setDownloadPercent(100);
          volumeHost.setDownloadState(Status.DOWNLOADED);
          volumeHost.setInstallPath(volInfo.getInstallPath());
          volumeHost.setSize(volInfo.getSize());
          volumeHost.setPhysicalSize(volInfo.getPhysicalSize());
          volumeHost.setLastUpdated(new Date());
          _volumeHostDao.update(volumeHost.getId(), volumeHost);

          if (volume.getSize() == 0) {
            // Set volume size in volumes table
            volume.setSize(volInfo.getSize());
            _volumeDao.update(volumeHost.getVolumeId(), volume);
          }

          if (volInfo.getSize() > 0) {
            try {
              String url = _volumeHostDao.findByVolumeId(volume.getId()).getDownloadUrl();
              _resourceLimitMgr.checkResourceLimit(
                  _accountMgr.getAccount(volume.getAccountId()),
                  com.cloud.configuration.Resource.ResourceType.secondary_storage,
                  volInfo.getSize() - UriUtils.getRemoteSize(url));
            } catch (ResourceAllocationException e) {
              s_logger.warn(e.getMessage());
              _alertMgr.sendAlert(
                  _alertMgr.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED,
                  volume.getDataCenterId(),
                  volume.getPodId(),
                  e.getMessage(),
                  e.getMessage());
            } finally {
              _resourceLimitMgr.recalculateResourceCount(
                  volume.getAccountId(),
                  volume.getDomainId(),
                  com.cloud.configuration.Resource.ResourceType.secondary_storage.getOrdinal());
            }
          }
        }
        continue;
      }
      // Volume is not on secondary but we should download.
      if (volumeHost.getDownloadState() != Status.DOWNLOADED) {
        s_logger.info(
            "Volume Sync did not find "
                + volume.getName()
                + " ready on server "
                + sserverId
                + ", will request download to start/resume shortly");
        toBeDownloaded.add(volumeHost);
      }
    }

    // Download volumes which haven't been downloaded yet.
    if (toBeDownloaded.size() > 0) {
      for (VolumeHostVO volumeHost : toBeDownloaded) {
        if (volumeHost.getDownloadUrl() == null) { // If url is null we can't initiate the download
          continue;
        }
        s_logger.debug(
            "Volume "
                + volumeHost.getVolumeId()
                + " needs to be downloaded to "
                + ssHost.getName());
        downloadVolumeToStorage(
            _volumeDao.findById(volumeHost.getVolumeId()),
            ssHost,
            volumeHost.getDownloadUrl(),
            volumeHost.getChecksum(),
            volumeHost.getFormat());
      }
    }

    // Delete volumes which are not present on DB.
    for (Long uniqueName : volumeInfos.keySet()) {
      TemplateInfo vInfo = volumeInfos.get(uniqueName);
      DeleteVolumeCommand dtCommand =
          new DeleteVolumeCommand(ssHost.getStorageUrl(), vInfo.getInstallPath());
      try {
        _agentMgr.sendToSecStorage(ssHost, dtCommand, null);
      } catch (AgentUnavailableException e) {
        String err =
            "Failed to delete "
                + vInfo.getTemplateName()
                + " on secondary storage "
                + sserverId
                + " which isn't in the database";
        s_logger.error(err);
        return;
      }

      String description =
          "Deleted volume "
              + vInfo.getTemplateName()
              + " on secondary storage "
              + sserverId
              + " since it isn't in the database";
      s_logger.info(description);
    }
  }