/**
   * Deletes the Snapshot with the given id.( It is asynchronous operation )
   *
   * @param snapshotId
   */
  public void deleteSnapshot(String snapshotId) {

    _log.info("CinderApi - start deleteSnapshot");

    String deleteSnapshotUri =
        endPoint.getBaseUri()
            + String.format(
                CinderConstants.URI_DELETE_SNAPSHOT,
                new Object[] {endPoint.getCinderTenantId(), snapshotId});

    ClientResponse deleteResponse = getClient().delete(URI.create(deleteSnapshotUri));

    String s = deleteResponse.getEntity(String.class);
    _log.debug("Got the response {}", s);

    if (deleteResponse.getStatus() == ClientResponse.Status.NOT_FOUND.getStatusCode()) {
      throw CinderException.exceptions.snapshotNotFound(snapshotId);
    }

    if (deleteResponse.getStatus() != ClientResponse.Status.ACCEPTED.getStatusCode()) {
      throw CinderException.exceptions.snapshotDeleteFailed(s);
    }

    _log.info("CinderApi - end deleteSnapshot");
  }
  public void expandVolume(String volumeId, long new_size) {
    _log.info("CinderApi - expandVolume START");

    Gson gson = new Gson();

    VolumeExpandRequest request = new VolumeExpandRequest();
    request.extend.new_size = new_size;

    String expandVolumeUri =
        endPoint.getBaseUri()
            + String.format(
                CinderConstants.URI_VOLUME_ACTION,
                new Object[] {endPoint.getCinderTenantId(), volumeId});

    _log.debug("Expanding volume with uri : {}", expandVolumeUri);
    String json = gson.toJson(request);
    _log.debug("Expanding volume with body : {}", json);
    ClientResponse js_response = getClient().postWithHeader(URI.create(expandVolumeUri), json);

    String s = js_response.getEntity(String.class);
    _log.debug("Got the response {}", s);

    _log.debug("Response status {}", String.valueOf(js_response.getStatus()));

    if (js_response.getStatus() != ClientResponse.Status.ACCEPTED.getStatusCode()) {
      // This means volume expand request not accepted
      throw CinderException.exceptions.volumeExpandFailed(s);
    }

    _log.info("CinderApi - expandVolume END");
  }
  /**
   * Create Snapshot operation ( It is asynchronous operation )
   *
   * @param volumeName
   * @param volumeTypeId
   * @return
   * @throws Exception
   */
  public String createSnapshot(String volumeId, String snapshotName) throws Exception {
    _log.info("CinderApi - start createSnapshot");

    Gson gson = new Gson();

    VolumeShowResponse volumeDetails = showVolume(volumeId);
    String volumeName = volumeDetails.volume.name;

    SnapshotCreateRequest request = new SnapshotCreateRequest();
    request.snapshot.name = snapshotName;
    request.snapshot.description = "Snapshot of volume " + volumeName;
    request.snapshot.volume_id = volumeId;
    request.snapshot.force = true;

    String snapshotCreateUri =
        endPoint.getBaseUri()
            + String.format(CinderConstants.URI_CREATE_SNAPSHOT, endPoint.getCinderTenantId());

    _log.debug("Creating snapshot with uri : {}", snapshotCreateUri);
    String json = gson.toJson(request);
    _log.debug("Creating snapshot with body : {}", json);
    ClientResponse js_response = getClient().postWithHeader(URI.create(snapshotCreateUri), json);

    String s = js_response.getEntity(String.class);
    _log.debug("Got the response {}", s);

    _log.debug("Response status {}", String.valueOf(js_response.getStatus()));

    String snapshotId = "";
    if (js_response.getStatus() == ClientResponse.Status.ACCEPTED.getStatusCode()) {
      // This means snapshot creation request accepted
      SnapshotCreateResponse response = gson.fromJson(s, SnapshotCreateResponse.class);
      snapshotId = response.snapshot.id;
    } else {
      throw CinderException.exceptions.snapshotCreationFailed(s);
    }

    return snapshotId;
  }
  /**
   * Detaches the specified volume from the specified initiator. It is an asynchronous operation.
   *
   * @param volumeId the volume id
   * @param initiator the initiator
   * @param host the host
   * @return the volume attachment response
   * @throws Exception
   */
  public void detachVolume(String volumeId, String initiator, String[] wwpns, String host)
      throws Exception {
    _log.info("CinderApi - start detachVolume");
    Gson gson = new Gson();

    VolumeDetachRequest volumeDetach = new VolumeDetachRequest();
    if (initiator != null) {
      volumeDetach.terminateConnection.connector.initiator = initiator;
    } else if (wwpns != null) {
      volumeDetach.terminateConnection.connector.wwpns = Arrays.copyOf(wwpns, wwpns.length);
    }
    volumeDetach.terminateConnection.connector.host = host;

    String volumeDetachmentUri =
        endPoint.getBaseUri()
            + String.format(
                CinderConstants.URI_DETACH_VOLUME,
                new Object[] {endPoint.getCinderTenantId(), volumeId});

    _log.debug("detaching volume from initiator with uri {}", volumeDetachmentUri);
    String json = gson.toJson(volumeDetach);
    _log.info("detaching volume with body {}", json);
    ClientResponse js_response = getClient().postWithHeader(URI.create(volumeDetachmentUri), json);
    String s = js_response.getEntity(String.class);
    _log.debug("Got the response {}", s);
    _log.debug("Response status {}", String.valueOf(js_response.getStatus()));

    if (js_response.getStatus() != ClientResponse.Status.ACCEPTED.getStatusCode()) {
      // if volume is not found on cinder, mark it as passed.
      if (js_response.getStatus() == ClientResponse.Status.NOT_FOUND.getStatusCode()) {
        _log.info(
            "Volume {} is not found on Cinder. It could have been deleted manually.", volumeId);
      } else {
        throw CinderException.exceptions.volumeDetachFailed(s);
      }
    }
    _log.info("CinderApi - end detachVolume");
  }
  /**
   * Create volume operation ( It is asynchronous operation )
   *
   * @param volumeName : Name of the new volume.
   * @param capacity : Capacity/Size of the volume
   * @param volumeTypeName : The volume type/storage system on which new volume should be created.
   * @param sourceVolId : The volume id that needs to be cloned.
   * @param sourceSnapId : The snapshot id that needs to be cloned.
   * @return volumeId of the new volume under creation
   * @throws Exception
   */
  private String createVolume(
      String volumeName,
      long capacity,
      String volumeTypeId,
      String sourceVolId,
      String sourceSnapId)
      throws Exception {
    _log.info("CinderApi - start createVolume");

    Gson gson = new Gson();

    VolumeCreateRequest volumeCreate = new VolumeCreateRequest();
    volumeCreate.volume.display_name = volumeName;
    volumeCreate.volume.size = capacity;
    // Volume type wont be available in cinder if volumeTypeId starts with "DEFAULT"
    // In that case, no need to pass volume type id in create volume request.
    if (!volumeTypeId.toUpperCase().startsWith(CinderConstants.DEFAULT)) {
      volumeCreate.volume.volume_type = volumeTypeId;
    }

    // volume clone
    if (null != sourceVolId) {
      volumeCreate.volume.source_volid = sourceVolId;
    }

    // create volume from snapshot
    if (null != sourceSnapId) {
      volumeCreate.volume.snapshot_id = sourceSnapId;
    }

    String volumeCreateUri =
        endPoint.getBaseUri()
            + String.format(CinderConstants.URI_CREATE_VOLUME, endPoint.getCinderTenantId());

    _log.debug("creting volume with uri {}", volumeCreateUri);
    String json = gson.toJson(volumeCreate);
    _log.debug("creating volume with body {}", json);
    ClientResponse js_response = getClient().postWithHeader(URI.create(volumeCreateUri), json);
    String s = js_response.getEntity(String.class);
    _log.debug("Got the response {}", s);

    String newVolumeId = "";
    if (js_response.getStatus() == ClientResponse.Status.ACCEPTED.getStatusCode()) {
      // This means volume creation request accepted
      VolumeCreateResponse response = gson.fromJson(s, VolumeCreateResponse.class);
      newVolumeId = response.volume.id;
    } else {
      if (null != sourceVolId) {
        throw CinderException.exceptions.volumeCloneFailed(s);
      }

      if (null != sourceSnapId) {
        throw CinderException.exceptions.createVolumeFromSnapshotFailed(s);
      }

      throw CinderException.exceptions.volumeCreationFailed(s);
    }

    _log.info("CinderApi - end createVolume");
    return newVolumeId;
  }