@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);
    }
  }
  @Override
  public void downloadVolumeToStorage(
      DataObject volume, AsyncCompletionCallback<DownloadAnswer> callback) {
    boolean downloadJobExists = false;
    VolumeDataStoreVO volumeHost = null;
    DataStore store = volume.getDataStore();
    VolumeInfo volInfo = (VolumeInfo) volume;
    RegisterVolumePayload payload = (RegisterVolumePayload) volInfo.getpayload();
    String url = payload.getUrl();
    String checkSum = payload.getChecksum();
    ImageFormat format = ImageFormat.valueOf(payload.getFormat());

    volumeHost = _volumeStoreDao.findByStoreVolume(store.getId(), volume.getId());
    if (volumeHost == null) {
      volumeHost =
          new VolumeDataStoreVO(
              store.getId(),
              volume.getId(),
              new Date(),
              0,
              Status.NOT_DOWNLOADED,
              null,
              null,
              "jobid0000",
              null,
              url,
              checkSum);
      _volumeStoreDao.persist(volumeHost);
    } else if ((volumeHost.getJobId() != null) && (volumeHost.getJobId().length() > 2)) {
      downloadJobExists = true;
    }

    Long maxVolumeSizeInBytes = getMaxVolumeSizeInBytes();
    if (volumeHost != null) {
      start();
      Volume vol = _volumeDao.findById(volume.getId());
      DownloadCommand dcmd =
          new DownloadCommand(
              (VolumeObjectTO) (volume.getTO()), maxVolumeSizeInBytes, checkSum, url, format);
      dcmd.setProxy(getHttpProxy());
      if (downloadJobExists) {
        dcmd = new DownloadProgressCommand(dcmd, volumeHost.getJobId(), RequestType.GET_OR_RESTART);
        dcmd.setResourceType(ResourceType.VOLUME);
      }

      EndPoint ep = _epSelector.select(volume);
      if (ep == null) {
        s_logger.warn("There is no secondary storage VM for image store " + store.getName());
        return;
      }
      DownloadListener dl = new DownloadListener(ep, store, volume, _timer, this, dcmd, callback);
      ComponentContext.inject(dl); // auto-wired those injected fields in DownloadListener

      if (downloadJobExists) {
        dl.setCurrState(volumeHost.getDownloadState());
      }

      try {
        ep.sendMessageAsync(dcmd, new UploadListener.Callback(ep.getId(), dl));
      } catch (Exception e) {
        s_logger.warn(
            "Unable to start /resume download of volume "
                + volume.getId()
                + " to "
                + store.getName(),
            e);
        dl.setDisconnected();
        dl.scheduleStatusCheck(RequestType.GET_OR_RESTART);
      }
    }
  }
  @DB
  protected void scheduleSnapshots() {
    String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, _currentTimestamp);
    s_logger.debug("Snapshot scheduler.poll is being called at " + displayTime);

    List<SnapshotScheduleVO> snapshotsToBeExecuted =
        _snapshotScheduleDao.getSchedulesToExecute(_currentTimestamp);
    s_logger.debug(
        "Got " + snapshotsToBeExecuted.size() + " snapshots to be executed at " + displayTime);

    // This is done for recurring snapshots, which are executed by the system automatically
    // Hence set user id to that of system
    long userId = 1;

    for (SnapshotScheduleVO snapshotToBeExecuted : snapshotsToBeExecuted) {
      long policyId = snapshotToBeExecuted.getPolicyId();
      long volumeId = snapshotToBeExecuted.getVolumeId();
      VolumeVO volume = _volsDao.findById(volumeId);
      if (volume.getPoolId() == null) {
        // this volume is not attached
        continue;
      }
      if (_snapshotPolicyDao.findById(policyId) == null) {
        _snapshotScheduleDao.remove(snapshotToBeExecuted.getId());
      }
      if (s_logger.isDebugEnabled()) {
        Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
        displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
        s_logger.debug(
            "Scheduling 1 snapshot for volume "
                + volumeId
                + " for schedule id: "
                + snapshotToBeExecuted.getId()
                + " at "
                + displayTime);
      }
      long snapshotScheId = snapshotToBeExecuted.getId();
      SnapshotScheduleVO tmpSnapshotScheduleVO = null;
      try {
        tmpSnapshotScheduleVO = _snapshotScheduleDao.acquireInLockTable(snapshotScheId);

        Long eventId =
            EventUtils.saveScheduledEvent(
                User.UID_SYSTEM,
                Account.ACCOUNT_ID_SYSTEM,
                EventTypes.EVENT_SNAPSHOT_CREATE,
                "creating snapshot for volume Id:" + volumeId,
                0);

        Map<String, String> params = new HashMap<String, String>();
        params.put("volumeid", "" + volumeId);
        params.put("policyid", "" + policyId);
        params.put("ctxUserId", "1");
        params.put("ctxAccountId", "1");
        params.put("ctxStartEventId", String.valueOf(eventId));

        CreateSnapshotCmd cmd = new CreateSnapshotCmd();
        ApiDispatcher.getInstance().dispatchCreateCmd(cmd, params);
        params.put("id", "" + cmd.getEntityId());
        params.put("ctxStartEventId", "1");

        AsyncJobVO job = new AsyncJobVO();
        job.setUserId(userId);
        // Just have SYSTEM own the job for now.  Users won't be able to see this job, but
        // it's an internal job so probably not a huge deal.
        job.setAccountId(1L);
        job.setCmd(CreateSnapshotCmd.class.getName());
        job.setInstanceId(cmd.getEntityId());
        job.setCmdInfo(GsonHelper.getBuilder().create().toJson(params));

        long jobId = _asyncMgr.submitAsyncJob(job);

        tmpSnapshotScheduleVO.setAsyncJobId(jobId);
        _snapshotScheduleDao.update(snapshotScheId, tmpSnapshotScheduleVO);
      } finally {
        if (tmpSnapshotScheduleVO != null) {
          _snapshotScheduleDao.releaseFromLockTable(snapshotScheId);
        }
      }
    }
  }