@Override
  public StorageOperationResponse<DeleteSnapshotsResponse> deleteSnapshots(
      DeleteSnapshotsRequest request) {

    logger.log(
        IJavaEeLog.SEVERITY_DEBUG,
        this.getClass().getName(),
        "deleteSnapshots: snapshots:" + request.snapshotsToBeDeleted,
        null);
    List<StorageLogMessage> logMessages = new ArrayList<StorageLogMessage>();
    List<StorageSnapshotVolume> storageSnapshots = request.snapshotsToBeDeleted;
    for (StorageSnapshotVolume snapshotVolume : storageSnapshots) {
      try {
        this.openstackClient.deleteSnapshot(snapshotVolume.storageVolumeId);
      } catch (CloudClientException e) {
        logger.traceThrowable(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            "deleteSnapshots: " + e.getMessage(),
            null,
            e);
        logMessages.add(
            new StorageLogMessage(
                IJavaEeLog.SEVERITY_ERROR, "OSBlock", System.currentTimeMillis(), e.getMessage()));
      }
    }
    if (logMessages.size() > 0) {
      return StorageAdapterImplHelper.createFailedResponse(
          logMessages, DeleteSnapshotsResponse.class);
    } else {
      DeleteSnapshotsResponse payload = new DeleteSnapshotsResponse();
      StorageOperationResponse<DeleteSnapshotsResponse> response =
          new StorageOperationResponse<DeleteSnapshotsResponse>(payload);
      response.setPercentCompleted(100);
      response.setStatus(StorageOperationStatus.COMPLETED);
      return response;
    }
  }
  /**
   * @param operationId
   * @param context
   * @return
   * @throws InfrastructAdapterException
   */
  public StorageOperationResponse<PostProcessCloneVolumesResponse> getOperationStatus(
      StorageOperationId operationId, OpenstackFileCloneVolumesContext context)
      throws InfrastructAdapterException {

    logger.log(
        IJavaEeLog.SEVERITY_DEBUG,
        this.getClass().getName(),
        "getOperationStatus: operationId: " + operationId + " context: " + context,
        null);
    List<StorageLogMessage> logMessages = new ArrayList<StorageLogMessage>();
    Status snapshotState = null;
    Share share = null;
    Status shareState = null;
    String snapshotId = null;
    String sourceVolumeId = null;
    String targetVolumeId = null;
    int progress = 0;
    int operationProgress = 0;
    boolean pending = false;
    boolean failed = false;
    boolean snapshotsReady = true;

    List<OpenstackFileCloneVolumeStatus> volumeStatus = context.volumeStatus;

    // STEP 1 : Check sourceSnapshot State
    for (OpenstackFileCloneVolumeStatus status : volumeStatus) {

      try {
        sourceVolumeId = status.volumeToBeCloned.sourceVolumeId;
        snapshotId = status.sourceSnapshotId;
        //	    	   String sourceSnapshotId = status.sourceSnapshotId;
        logger.log(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            " //STEP 1 : Check sourceSnapshot State " + snapshotId,
            null);
        if (status.sourceSnapshotComplete) continue; // snapshot already completed
        // snapshot = openstackClient.getSnapshot(snapshotId);
        snapshotState = openstackClient.getSnapshotStatus(snapshotId);
        logger.log(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            "getOperationStatus: operationId: "
                + operationId
                + " checking snapshot: "
                + snapshotId
                + " state: "
                + snapshotState,
            null);
        if (snapshotState.equals(Status.CREATING)) {

          //   logger.log(IJavaEeLog.SEVERITY_DEBUG, this.getClass().getName(), "getOperationStatus:
          // operationId: " + operationId + "checking snapshot:" + snapshotId + " progress:" +
          // snapshot.getProgress(), null);
          String percent = "0"; // snapshot.getProgress(); //progress not tracked in Openstack
          //	    		   if (percent.isEmpty()) {
          //	    			   percent = "0";
          //	    		   } else {
          //	    			   percent = percent.substring(0,percent.length() -2);
          //	    		   }
          progress = Integer.parseInt(percent);
          if (progress < operationProgress) operationProgress = progress;
          pending = true;
          snapshotsReady = false;
          continue;
        } else if (snapshotState.equals(Status.AVAILABLE)) {
          status.sourceSnapshotComplete = true;
          logger.log(
              IJavaEeLog.SEVERITY_DEBUG,
              this.getClass().getName(),
              "getOperationStatus: operationId: "
                  + operationId
                  + " completed source snapshot: "
                  + snapshotId,
              null);
          //	    		  if (isRemoteClone(status.volumeToBeCloned)) {
          //	    			  createTargetSnapshot(snapshotId, status, operationId.toString());
          //	    		   } else {

          createTargetShare(context, status, true);
          //	    		  }
        } else if (snapshotState.equals(Status.ERROR)) {
          logger.log(
              IJavaEeLog.SEVERITY_DEBUG,
              this.getClass().getName(),
              "getOperationStatus: operationId: " + operationId + " failed: " + snapshotId,
              null);
          logMessages.add(
              new StorageLogMessage(
                  IJavaEeLog.SEVERITY_ERROR,
                  "OSFile",
                  System.currentTimeMillis(),
                  "Failed to Clone volume "
                      + status.volumeToBeCloned.sourceVolumeId
                      + ". Snapshot process terminated with ERROR"));
          failed = true;
          break;
        } else {
          throw new IllegalStateException(
              "Failed to Clone volume "
                  + status.volumeToBeCloned.sourceVolumeId
                  + ". Snapshot process returned unexpected status: "
                  + snapshotState);
        }
      } catch (Exception e) {
        failed = true;
        logger.traceThrowable(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            "getOperationStatus:" + e.getMessage(),
            null,
            e);
        logMessages.add(
            new StorageLogMessage(
                IJavaEeLog.SEVERITY_ERROR,
                "OSFile",
                System.currentTimeMillis(),
                "getOperationStatus:" + e.getMessage()));
      }
    }
    // STEP 2: Check Target Snapshot state (for cross-region only) //TODO
    for (OpenstackFileCloneVolumeStatus status : context.volumeStatus) {
      if (isRemoteClone(status.volumeToBeCloned)) {
        if (status.targetSnapshotId != null && !status.targetSnapshotComplete) {
          try {
            snapshotId = status.targetSnapshotId;
            // snapshot = openstackClient.getSnapshot(snapshotId);
            snapshotState = openstackClient.getSnapshotStatus(snapshotId);
            logger.log(
                IJavaEeLog.SEVERITY_DEBUG,
                this.getClass().getName(),
                "getOperationStatus: operationId: "
                    + operationId
                    + " checking snapshot: "
                    + snapshotId
                    + " state: "
                    + snapshotState,
                null);
            if (snapshotState.equals(Status.CREATING)) {
              // logger.log(IJavaEeLog.SEVERITY_DEBUG, this.getClass().getName(),
              // "getOperationStatus: operationId: " + operationId + "checking snapshot:" +
              // snapshotId + " progress:" + snapshot.getProgress(), null);
              // TODO: openstack doesnt have percent complete I think so map percentages based on
              // state 0, 50,100
              String percent = "0"; // snapshot.getStatus()
              if (percent.isEmpty()) {
                percent = "0";
              } else {
                percent = percent.substring(0, percent.length() - 2);
              }
              progress = Integer.parseInt(percent);
              if (progress < operationProgress) operationProgress = progress;
              pending = true;
              snapshotsReady = false;
              continue;
            } else if (snapshotState.equals(Status.AVAILABLE)) {
              status.targetSnapshotComplete = true;
              logger.log(
                  IJavaEeLog.SEVERITY_DEBUG,
                  this.getClass().getName(),
                  "getOperationStatus: operationId: "
                      + operationId
                      + " completed target snapshot: "
                      + snapshotId,
                  null);
              createTargetShare(context, status, true);

            } else if (snapshotState.equals(Status.ERROR)) {
              logger.log(
                  IJavaEeLog.SEVERITY_DEBUG,
                  this.getClass().getName(),
                  "getOperationStatus: operationId: " + operationId + " failed: " + snapshotId,
                  null);
              logMessages.add(
                  new StorageLogMessage(
                      IJavaEeLog.SEVERITY_ERROR,
                      "OSFile",
                      System.currentTimeMillis(),
                      "Failed to Clone volume "
                          + status.volumeToBeCloned.sourceVolumeId
                          + ". Snapshot process terminated with ERROR"));
              failed = true;
              break;
            } else {
              throw new IllegalStateException(
                  "Failed to Clone volume "
                      + status.volumeToBeCloned.sourceVolumeId
                      + ". Snapshot process returned unexpected status: "
                      + snapshotState);
            }
          } catch (CloudClientException e) {
            failed = true;
            logger.traceThrowable(
                IJavaEeLog.SEVERITY_DEBUG,
                this.getClass().getName(),
                "getOperationStatus:" + e.getMessage(),
                null,
                e);
            logMessages.add(
                new StorageLogMessage(
                    IJavaEeLog.SEVERITY_ERROR,
                    "OSFile",
                    System.currentTimeMillis(),
                    e.getMessage()));
            break;
          }
        }
      }
    }
    // STEP 3: Check Target Volume state
    for (OpenstackFileCloneVolumeStatus status : context.volumeStatus) {
      if (status.targetVolumeId == null) continue;
      try {
        share = openstackClient.getShare(status.targetVolumeId);
        if (share == null) {
          failed = true;
          logMessages.add(
              new StorageLogMessage(
                  IJavaEeLog.SEVERITY_ERROR,
                  "OSFile",
                  System.currentTimeMillis(),
                  "Volume " + status.targetVolumeId + " not found"));
          break;
        }
        //    } catch (CloudClientException e) {
      } catch (Exception e) {
        failed = true;
        logger.traceThrowable(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            "getOperationStatus:" + e.getMessage(),
            null,
            e);
        logMessages.add(
            new StorageLogMessage(
                IJavaEeLog.SEVERITY_ERROR, "OSFile", System.currentTimeMillis(), e.getMessage()));
        break;
      }
      shareState = share.getStatus();
      logger.log(
          IJavaEeLog.SEVERITY_DEBUG,
          this.getClass().getName(),
          "getOperationStatus: operationId: "
              + operationId
              + " checking volume: "
              + share.getId()
              + " state: "
              + shareState,
          null);
      if ((shareState.equals(Status.CREATING))) {
        pending = true;
        logger.log(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            "getOperationStatus: operationId: " + operationId + " in progress: " + share.getId(),
            null);
      } else if (shareState.equals(Status.ERROR)) {

        logger.log(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            "getOperationStatus: operationId: "
                + operationId
                + " create volume failed: "
                + share.getId(),
            null);
        logMessages.add(
            new StorageLogMessage(
                IJavaEeLog.SEVERITY_ERROR,
                "OSFile",
                System.currentTimeMillis(),
                "Failed to Clone volume " + share.getId() + ". Process terminated with ERROR"));
        failed = true;
        break;
      } else if (shareState.equals(Status.AVAILABLE)) {
        logger.log(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            "getOperationStatus: operationId: "
                + operationId
                + " completed volume: "
                + share.getId(),
            null);
        status.targetVolumeComplete = true;

        // once volume is ready we can delete the temporary snapshot as it is no longer needed.
        if (status.targetSnapshotComplete) {
          snapshotId = status.targetSnapshotId;
        } else {
          snapshotId = status.sourceSnapshotId;
        }
        try {
          boolean forDelete = false;

          if (forDelete) {
            openstackClient.deleteSnapshot(snapshotId);
          }
        } catch (Exception e) {
          logger.traceThrowable(
              IJavaEeLog.SEVERITY_DEBUG,
              this.getClass().getName(),
              "getOperationStatus:" + e.getMessage(),
              null,
              e);
          // here we just log warning as failing to delete the temporary snapshot does not fail the
          // clone operation
          logMessages.add(
              new StorageLogMessage(
                  IJavaEeLog.SEVERITY_WARNING,
                  "OSFile",
                  System.currentTimeMillis(),
                  "Failed delete temorary snapshot "
                      + share.getSnapshotId()
                      + ". "
                      + e.getMessage()));
        }
      } else {
        logMessages.add(
            new StorageLogMessage(
                IJavaEeLog.SEVERITY_ERROR,
                "OSFile",
                System.currentTimeMillis(),
                "Unexpected volume state: " + shareState.name()));
        failed = true;
        break;
      }
    }

    if (failed) {
      // cleanup
      cancelVolumes(operationId, context);
      return StorageAdapterImplHelper.createFailedResponse(
          logMessages, PostProcessCloneVolumesResponse.class);
    } else if (pending) {
      PostProcessCloneVolumesResponse payload = new PostProcessCloneVolumesResponse();
      StorageOperationResponse<PostProcessCloneVolumesResponse> response =
          new StorageOperationResponse<PostProcessCloneVolumesResponse>(payload);
      response.setPercentCompleted(progress);
      response.setId(operationId);
      response.setContext(context);
      response.setStatus(StorageOperationStatus.EXECUTING);
      response.setLogMessages(logMessages);
      return response;

    } else {
      // operation completed successfully
      PostProcessCloneVolumesResponse payload = new PostProcessCloneVolumesResponse();
      payload.mountConfiguration = new ArrayList<MountData>();
      for (OpenstackFileCloneVolumeStatus state : context.volumeStatus) {
        state.volumeToBeCloned.targetVolumeId = state.targetVolumeId;
        sourceVolumeId = state.volumeToBeCloned.sourceVolumeId;
        targetVolumeId = state.volumeToBeCloned.targetVolumeId;

        Share sourceShare = openstackClient.getShare(sourceVolumeId);
        String sourceExport = sourceShare.getExport();
        Share targetShare = openstackClient.getShare(targetVolumeId);
        String targetExport = targetShare.getExport();

        List<MountData> sourceMnts = state.volumeToBeCloned.sourceMountConfiguration;
        if (sourceMnts != null && !sourceMnts.isEmpty()) {
          for (int i = 0; i < sourceMnts.size(); i++) {
            MountData srcMD = sourceMnts.get(i);
            String sourceExportPath = srcMD.exportPath;
            if (!sourceExportPath.contains(sourceExport)) continue;
            MountData targetMD = state.volumeToBeCloned.targetMountConfiguration.get(i);
            String targetExportPath = sourceExportPath.replace(sourceExport, targetExport);
            targetMD.exportPath = targetExportPath;
            payload.mountConfiguration.add(targetMD);
          }
        }

        try {
          List<? extends ShareAccessMapping> listAccess =
              openstackClient.listAccess(sourceVolumeId);
          for (ShareAccessMapping shareAccessMapping : listAccess) {
            String accessTo = shareAccessMapping.getAccessTo();
            Boolean active = openstackClient.allowAccess(targetVolumeId, accessTo);
          }
        } catch (Exception e) {
          logger.traceThrowable(
              IJavaEeLog.SEVERITY_DEBUG,
              this.getClass().getName(),
              "cloneVolumes:" + e.getMessage(),
              null,
              e);
          //	    	       		ArrayList<StorageLogMessage> logMessages = cancel(snapshots);
          //	    	       		logMessages.add(0, new StorageLogMessage(IJavaEeLog.SEVERITY_ERROR,
          // "OSFile", System.currentTimeMillis(), e.getMessage()));
          return StorageAdapterImplHelper.createFailedResponse(
              logMessages, PostProcessCloneVolumesResponse.class);
        }
      }
      StorageOperationResponse<PostProcessCloneVolumesResponse> response =
          new StorageOperationResponse<PostProcessCloneVolumesResponse>(payload);
      response.setId(operationId);
      response.setPercentCompleted(100);
      response.setLogMessages(logMessages);
      response.setStatus(StorageOperationStatus.COMPLETED);
      return response;
    }
  }
  @Override
  public StorageOperationResponse<CloneVolumesResponse> cloneVolumes(CloneVolumesRequest request) {

    String msg = "";
    PrepareCloneVolumesRequest prepareRequest =
        (PrepareCloneVolumesRequest) request.prepareCloneVolumesResult;
    List<VolumeToBeCloned> volumesToBeCloned = prepareRequest.volumesToBeCloned;
    ArrayList<StorageLogMessage> logMessages = new ArrayList<StorageLogMessage>();

    logger.log(
        IJavaEeLog.SEVERITY_DEBUG,
        this.getClass().getName(),
        "cloneVolumes: request:" + prepareRequest,
        null);
    msg = "CloneVolumes operation started";
    logMessages.add(
        new StorageLogMessage(IJavaEeLog.SEVERITY_INFO, "OSFile", System.currentTimeMillis(), msg));
    String operationId = OpenstackAdapterUtil.generateOperationId();
    ArrayList<String> snapshots = new ArrayList<String>();

    OpenstackFileCloneVolumesContext context = new OpenstackFileCloneVolumesContext();
    context.volumeStatus = new ArrayList<OpenstackFileCloneVolumeStatus>();
    OpenstackFileCloneVolumeStatus status;
    for (VolumeToBeCloned inputVolume : volumesToBeCloned) {
      status = new OpenstackFileCloneVolumeStatus();
      status.volumeToBeCloned = inputVolume;
      status.customCloningProperties = prepareRequest.customCloningProperties;
      context.volumeStatus.add(status);
      try {
        if (inputVolume.isSourceVolumeSnapshot) {
          if (openstackClient.getSnapshot(inputVolume.sourceVolumeId) != null) {
            status.sourceSnapshotComplete = true;
            status.sourceSnapshotId = inputVolume.sourceVolumeId;
            if (isRemoteClone(status.volumeToBeCloned)) {
              createTargetSnapshot(status.sourceSnapshotId, status, operationId);
              snapshots.add(status.targetSnapshotId);
              msg = "Target snapshot" + status.targetSnapshotId + " created";
              logMessages.add(
                  new StorageLogMessage(
                      IJavaEeLog.SEVERITY_INFO, "LVM", System.currentTimeMillis(), msg));
            }

          } else {
            msg = "Snapshot not found: " + inputVolume.sourceVolumeId;
            logMessages.add(
                new StorageLogMessage(
                    IJavaEeLog.SEVERITY_ERROR, "OSFile", System.currentTimeMillis(), msg));
            return StorageAdapterImplHelper.createFailedResponse(
                logMessages, CloneVolumesResponse.class);
          }
        } else {
          ShareSnapshot snapshot = null;
          try {
            String snapshotName = "Snapshot of share " + inputVolume.sourceVolumeId;
            String snapshotDescription =
                "LVM Snapshot to clone share " + inputVolume.sourceVolumeId;
            snapshot =
                openstackClient.createShareSnapshot(
                    inputVolume.sourceVolumeId, snapshotName, snapshotDescription);
          } catch (CloudClientException e) {

            throw e;
          }
          snapshots.add(snapshot.getId());
          status.sourceSnapshotId = snapshot.getId();
        }
      } catch (CloudClientException e) {
        logger.traceThrowable(
            IJavaEeLog.SEVERITY_DEBUG,
            this.getClass().getName(),
            "cloneVolumes:" + e.getMessage(),
            null,
            e);
        logMessages.add(
            0,
            new StorageLogMessage(
                IJavaEeLog.SEVERITY_ERROR, "OSFile", System.currentTimeMillis(), e.getMessage()));
        return StorageAdapterImplHelper.createFailedResponse(
            logMessages, CloneVolumesResponse.class);
      }
    }
    msg = "CloneVolumes operation ended";
    logMessages.add(
        new StorageLogMessage(IJavaEeLog.SEVERITY_INFO, "OSFile", System.currentTimeMillis(), msg));

    CloneVolumesResponse payload = new CloneVolumesResponse();
    context.operationId = operationId;
    context.customCloningProperties = prepareRequest.customCloningProperties;
    payload.cloneVolumeResult = context;
    StorageOperationResponse<CloneVolumesResponse> response =
        new StorageOperationResponse<CloneVolumesResponse>(payload);
    response.setLogMessages(logMessages);
    response.setPercentCompleted(100);
    response.setStatus(StorageOperationStatus.COMPLETED);
    response.setContext(context);
    return response;
  }
  /**
   * @param operationId
   * @param context
   * @return
   * @throws InfrastructAdapterException
   */
  public StorageOperationResponse<PostProcessCloneVolumesResponse> cancelVolumes(
      StorageOperationId operationId, OpenstackFileCloneVolumesContext context)
      throws InfrastructAdapterException {

    logger.log(
        IJavaEeLog.SEVERITY_DEBUG,
        this.getClass().getName(),
        "cancelVolumes: operationId: " + operationId + " volumeStauts: " + context.volumeStatus,
        null);
    List<StorageLogMessage> logMessages = new ArrayList<StorageLogMessage>();
    for (OpenstackFileCloneVolumeStatus status : context.volumeStatus) {
      if (status.sourceSnapshotId != null) {
        try {
          openstackClient.deleteSnapshot(status.sourceSnapshotId);
        } catch (CloudClientException e) {
          logger.traceThrowable(
              IJavaEeLog.SEVERITY_DEBUG,
              this.getClass().getName(),
              "cancelVolumes:" + e.getMessage(),
              null,
              e);
          logMessages.add(
              new StorageLogMessage(
                  IJavaEeLog.SEVERITY_ERROR, "OSFile", System.currentTimeMillis(), e.getMessage()));
        }
      }
      if (status.targetSnapshotId != null) {
        try {
          openstackClient.deleteSnapshot(status.targetSnapshotId);

        } catch (CloudClientException e) {
          logger.traceThrowable(
              IJavaEeLog.SEVERITY_DEBUG,
              this.getClass().getName(),
              "cancelVolumes:" + e.getMessage(),
              null,
              e);
          logMessages.add(
              new StorageLogMessage(
                  IJavaEeLog.SEVERITY_ERROR, "OSFile", System.currentTimeMillis(), e.getMessage()));
        }
      }
      if (status.targetVolumeId != null) {
        try {
          openstackClient.deleteShare(status.targetVolumeId);
          //  	    } catch (CloudClientException e) {
        } catch (Exception e) {
          logger.traceThrowable(
              IJavaEeLog.SEVERITY_DEBUG,
              this.getClass().getName(),
              "cancelVolumes:" + e.getMessage(),
              null,
              e);
          logMessages.add(
              new StorageLogMessage(
                  IJavaEeLog.SEVERITY_ERROR, "OSFile", System.currentTimeMillis(), e.getMessage()));
        }
      }
    }
    if (logMessages.isEmpty()) {
      PostProcessCloneVolumesResponse payload = new PostProcessCloneVolumesResponse();
      payload.mountConfiguration = new ArrayList<MountData>();
      StorageOperationResponse<PostProcessCloneVolumesResponse> response =
          new StorageOperationResponse<PostProcessCloneVolumesResponse>(payload);
      response.setPercentCompleted(100);
      response.setStatus(StorageOperationStatus.CANCELLED);
      return response;
    } else {
      return StorageAdapterImplHelper.createFailedResponse(
          logMessages, PostProcessCloneVolumesResponse.class);
    }
  }