@Override
  @Transactional(readOnly = true)
  public void preRecoverVm(VmInstanceInventory vm) {
    String rootVolUuid = vm.getRootVolumeUuid();

    String sql =
        "select ps.uuid from PrimaryStorageVO ps, VolumeVO vol where ps.uuid = vol.primaryStorageUuid"
            + " and vol.uuid = :uuid and ps.type = :pstype";
    TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
    q.setParameter("uuid", rootVolUuid);
    q.setParameter("pstype", LocalStorageConstants.LOCAL_STORAGE_TYPE);
    String psuuid = dbf.find(q);
    if (psuuid == null) {
      return;
    }

    sql =
        "select count(ref) from LocalStorageResourceRefVO ref where ref.resourceUuid = :uuid and ref.resourceType = :rtype";
    TypedQuery<Long> rq = dbf.getEntityManager().createQuery(sql, Long.class);
    rq.setParameter("uuid", rootVolUuid);
    rq.setParameter("rtype", VolumeVO.class.getSimpleName());
    long count = rq.getSingleResult();
    if (count == 0) {
      throw new OperationFailureException(
          errf.stringToOperationError(
              String.format(
                  "unable to recover the vm[uuid:%s, name:%s]. The vm's root volume is on the local"
                      + " storage[uuid:%s]; however, the host on which the root volume is has been deleted",
                  vm.getUuid(), vm.getName(), psuuid)));
    }
  }
  @Override
  public void preAttachVolume(VmInstanceInventory vm, final VolumeInventory volume) {
    SimpleQuery<LocalStorageResourceRefVO> q = dbf.createQuery(LocalStorageResourceRefVO.class);
    q.add(
        LocalStorageResourceRefVO_.resourceUuid,
        Op.IN,
        list(vm.getRootVolumeUuid(), volume.getUuid()));
    q.groupBy(LocalStorageResourceRefVO_.hostUuid);
    long count = q.count();

    if (count < 2) {
      return;
    }

    q = dbf.createQuery(LocalStorageResourceRefVO.class);
    q.select(LocalStorageResourceRefVO_.hostUuid);
    q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, vm.getRootVolumeUuid());
    String rootHost = q.findValue();

    q = dbf.createQuery(LocalStorageResourceRefVO.class);
    q.select(LocalStorageResourceRefVO_.hostUuid);
    q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, volume.getUuid());
    String dataHost = q.findValue();

    if (!rootHost.equals(dataHost)) {
      throw new OperationFailureException(
          errf.stringToOperationError(
              String.format(
                  "cannot attach the data volume[uuid:%s] to the vm[uuid:%s]. Both vm's root volume and the data volume are"
                      + " on local primary storage, but they are on different hosts. The root volume[uuid:%s] is on the host[uuid:%s] but the data volume[uuid: %s]"
                      + " is on the host[uuid: %s]",
                  volume.getUuid(),
                  vm.getUuid(),
                  vm.getRootVolumeUuid(),
                  rootHost,
                  volume.getUuid(),
                  dataHost)));
    }
  }
  @Override
  @Transactional(readOnly = true)
  public List<VolumeVO> returnAttachableVolumes(VmInstanceInventory vm, List<VolumeVO> candidates) {
    // find instantiated volumes
    List<String> volUuids =
        CollectionUtils.transformToList(
            candidates,
            new Function<String, VolumeVO>() {
              @Override
              public String call(VolumeVO arg) {
                return VolumeStatus.Ready == arg.getStatus() ? arg.getUuid() : null;
              }
            });

    if (volUuids.isEmpty()) {
      return candidates;
    }

    List<VolumeVO> uninstantiatedVolumes =
        CollectionUtils.transformToList(
            candidates,
            new Function<VolumeVO, VolumeVO>() {
              @Override
              public VolumeVO call(VolumeVO arg) {
                return arg.getStatus() == VolumeStatus.NotInstantiated ? arg : null;
              }
            });

    String sql =
        "select ref.hostUuid from LocalStorageResourceRefVO ref where ref.resourceUuid = :volUuid and ref.resourceType = :rtype";
    TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
    q.setParameter("volUuid", vm.getRootVolumeUuid());
    q.setParameter("rtype", VolumeVO.class.getSimpleName());
    List<String> ret = q.getResultList();
    if (ret.isEmpty()) {
      return candidates;
    }

    String hostUuid = ret.get(0);
    sql =
        "select ref.resourceUuid from LocalStorageResourceRefVO ref where ref.resourceUuid in (:uuids) and ref.resourceType = :rtype"
            + " and ref.hostUuid != :huuid";
    q = dbf.getEntityManager().createQuery(sql, String.class);
    q.setParameter("uuids", volUuids);
    q.setParameter("huuid", hostUuid);
    q.setParameter("rtype", VolumeVO.class.getSimpleName());
    final List<String> toExclude = q.getResultList();

    candidates =
        CollectionUtils.transformToList(
            candidates,
            new Function<VolumeVO, VolumeVO>() {
              @Override
              public VolumeVO call(VolumeVO arg) {
                return toExclude.contains(arg.getUuid()) ? null : arg;
              }
            });

    candidates.addAll(uninstantiatedVolumes);

    return candidates;
  }