@Override
  public void removeVcenterCluster(URI datacenterUri, URI clusterUri) throws InternalException {
    VcenterApiClient vcenterApiClient = null;
    try {
      VcenterDataCenter vcenterDataCenter =
          _dbClient.queryObject(VcenterDataCenter.class, datacenterUri);
      Cluster cluster = _dbClient.queryObject(Cluster.class, clusterUri);
      Vcenter vcenter = _dbClient.queryObject(Vcenter.class, vcenterDataCenter.getVcenter());
      _log.info(
          "Request to remove cluster "
              + vcenter.getLabel()
              + "/"
              + vcenterDataCenter.getLabel()
              + "/"
              + cluster.getLabel());

      vcenterApiClient = new VcenterApiClient(_coordinator.getPropertyInfo());
      vcenterApiClient.setup(vcenter.getIpAddress(), vcenter.getUsername(), vcenter.getPassword());
      vcenterApiClient.removeCluster(vcenterDataCenter.getLabel(), cluster.getExternalId());
    } catch (VcenterObjectConnectionException e) {
      throw VcenterControllerException.exceptions.objectConnectionException(
          e.getLocalizedMessage(), e);
    } catch (VcenterObjectNotFoundException e) {
      throw VcenterControllerException.exceptions.objectNotFoundException(
          e.getLocalizedMessage(), e);
    } catch (VcenterServerConnectionException e) {
      throw VcenterControllerException.exceptions.serverConnectionException(
          e.getLocalizedMessage(), e);
    } catch (Exception e) {
      _log.error("removeVcenterCluster exception " + e);
      throw VcenterControllerException.exceptions.unexpectedException(e.getLocalizedMessage(), e);
    } finally {
      if (vcenterApiClient != null) vcenterApiClient.destroy();
    }
  }
  public void vcenterClusterAddHostOperation(
      URI vcenterId, URI vcenterDataCenterId, URI clusterId, URI hostId, String stepId) {
    VcenterApiClient vcenterApiClient = null;
    try {
      WorkflowStepCompleter.stepExecuting(stepId);

      VcenterDataCenter vcenterDataCenter =
          _dbClient.queryObject(VcenterDataCenter.class, vcenterDataCenterId);
      Cluster cluster = _dbClient.queryObject(Cluster.class, clusterId);
      Vcenter vcenter = _dbClient.queryObject(Vcenter.class, vcenterId);
      Host host = _dbClient.queryObject(Host.class, hostId);

      vcenterApiClient = new VcenterApiClient(_coordinator.getPropertyInfo());
      vcenterApiClient.setup(vcenter.getIpAddress(), vcenter.getUsername(), vcenter.getPassword());
      String key =
          vcenterApiClient.addHost(
              vcenterDataCenter.getLabel(),
              cluster.getExternalId(),
              host.getHostName(),
              host.getUsername(),
              host.getPassword());
      _log.info("Successfully added or located host " + host.getHostName() + " " + key);

      host.setVcenterDataCenter(vcenterDataCenter.getId());
      _dbClient.updateAndReindexObject(host);

      WorkflowStepCompleter.stepSucceded(stepId);
    } catch (Exception e) {
      _log.error("vcenterClusterAddHostOperation exception " + e);
      WorkflowStepCompleter.stepFailed(
          stepId, VcenterControllerException.exceptions.hostException(e.getLocalizedMessage(), e));
    } finally {
      if (vcenterApiClient != null) vcenterApiClient.destroy();
    }
  }
  public void createUpdateVcenterClusterOperation(
      boolean createCluster, URI vcenterId, URI vcenterDataCenterId, URI clusterId, String stepId) {
    VcenterApiClient vcenterApiClient = null;
    try {
      WorkflowStepCompleter.stepExecuting(stepId);

      VcenterDataCenter vcenterDataCenter =
          _dbClient.queryObject(VcenterDataCenter.class, vcenterDataCenterId);
      Cluster cluster = _dbClient.queryObject(Cluster.class, clusterId);
      Vcenter vcenter = _dbClient.queryObject(Vcenter.class, vcenterId);

      vcenterApiClient = new VcenterApiClient(_coordinator.getPropertyInfo());
      vcenterApiClient.setup(vcenter.getIpAddress(), vcenter.getUsername(), vcenter.getPassword());
      String vcenterClusterId = null;
      if (createCluster) {
        _log.info("Create cluster with name " + cluster.getLabel());
        vcenterClusterId =
            vcenterApiClient.createCluster(vcenterDataCenter.getLabel(), cluster.getLabel());
      } else {
        // Use MoRef if present but don't stop if we fail to find cluster based off this because
        // we'll fall back and try on name
        String externalId = cluster.getExternalId();
        if (externalId != null && !externalId.trim().equals("")) {
          _log.info("Update cluster with MoRef " + externalId);
          try {
            vcenterClusterId =
                vcenterApiClient.updateCluster(vcenterDataCenter.getLabel(), externalId);
          } catch (VcenterObjectNotFoundException e) {
            _log.info(
                "Ignore VcenterObjectNotFoundException updateCluster when using MoRef... Try name based search next");
          }
        }
        if (vcenterClusterId == null) {
          _log.info("Update cluster with name " + cluster.getLabel());
          vcenterClusterId =
              vcenterApiClient.updateCluster(vcenterDataCenter.getLabel(), cluster.getLabel());
        }
      }
      _log.info(
          "Successfully created or updated cluster " + cluster.getLabel() + " " + vcenterClusterId);

      cluster.setVcenterDataCenter(vcenterDataCenter.getId());
      cluster.setExternalId(vcenterClusterId);
      _dbClient.updateAndReindexObject(cluster);

      WorkflowStepCompleter.stepSucceded(stepId);
    } catch (Exception e) {
      _log.error("createUpdateVcenterClusterOperation exception " + e);
      WorkflowStepCompleter.stepFailed(
          stepId,
          VcenterControllerException.exceptions.clusterException(e.getLocalizedMessage(), e));
    } finally {
      if (vcenterApiClient != null) vcenterApiClient.destroy();
    }
  }
  // Find a host connected and powered on then refresh it
  public void vcenterClusterSelectHostOperation(
      URI vcenterId, URI vcenterDataCenterId, URI clusterId, URI[] hostUris, String stepId) {
    VcenterApiClient vcenterApiClient = null;
    try {
      WorkflowStepCompleter.stepExecuting(stepId);

      VcenterDataCenter vcenterDataCenter =
          _dbClient.queryObject(VcenterDataCenter.class, vcenterDataCenterId);
      Cluster cluster = _dbClient.queryObject(Cluster.class, clusterId);
      Vcenter vcenter = _dbClient.queryObject(Vcenter.class, vcenterId);
      Collection<Host> hosts = _dbClient.queryObject(Host.class, hostUris);

      vcenterApiClient = new VcenterApiClient(_coordinator.getPropertyInfo());
      vcenterApiClient.setup(vcenter.getIpAddress(), vcenter.getUsername(), vcenter.getPassword());

      Host hostForStorageOperations = null;
      for (Host host : hosts) {
        try {
          vcenterApiClient.checkHostConnectedPoweredOn(
              vcenterDataCenter.getLabel(), cluster.getExternalId(), host.getHostName());
          hostForStorageOperations = host;
          _log.info("Host " + host.getHostName() + " to be used for storage operations");
          break;
        } catch (Exception e) {
          _log.info(
              "Host "
                  + host.getHostName()
                  + " not valid for storage operations due to exception "
                  + e.getLocalizedMessage());
        }
      }
      if (hostForStorageOperations == null) {
        _log.error(
            "No host valid for performing storage operations thus cannot perform datastore operations");
        throw new Exception(
            "No host valid for performing storage operations thus cannot perform datastore operations");
      }
      vcenterApiClient.refreshRescanHostStorage(
          vcenterDataCenter.getLabel(),
          cluster.getExternalId(),
          hostForStorageOperations.getHostName());

      // persist hostForStorageOperations ID in wf data
      _workflowService.storeStepData(stepId, hostForStorageOperations.getId());

      WorkflowStepCompleter.stepSucceded(stepId);
    } catch (Exception e) {
      _log.error("vcenterClusterSelectHostOperation exception " + e);
      WorkflowStepCompleter.stepFailed(
          stepId, VcenterControllerException.exceptions.hostException(e.getLocalizedMessage(), e));
    } finally {
      if (vcenterApiClient != null) vcenterApiClient.destroy();
    }
  }
  /**
   * Validates vCenter user credentials from create or update parameters.
   *
   * @param param either vCenter create or update param.
   * @param vcenter vCenter object.
   */
  private void validateVcenterCredentials(VcenterParam param, Vcenter vcenter) {
    if (StringUtils.isBlank(param.getPassword()) && vcenter != null) {
      param.setPassword(StringUtils.trimToNull(vcenter.getPassword()));
    }

    if (StringUtils.isBlank(param.getUserName()) && vcenter != null) {
      param.setUserName(StringUtils.trimToNull(vcenter.getUsername()));
    }

    ArgValidator.checkFieldNotNull(param.getUserName(), "username");
    ArgValidator.checkFieldNotNull(param.getPassword(), "password");
  }
  public void vcenterClusterCreateDatastoreOperation(
      URI vcenterId,
      URI vcenterDataCenterId,
      URI clusterId,
      URI volumeId,
      String selectHostStepId,
      String stepId) {
    VcenterApiClient vcenterApiClient = null;
    try {
      WorkflowStepCompleter.stepExecuting(stepId);

      URI hostId = (URI) _workflowService.loadStepData(selectHostStepId);
      if (hostId == null) {
        _log.error("Workflow loadStepData on " + selectHostStepId + " is null");
        throw new Exception("Workflow loadStepData on " + selectHostStepId + " is null");
      }

      VcenterDataCenter vcenterDataCenter =
          _dbClient.queryObject(VcenterDataCenter.class, vcenterDataCenterId);
      Cluster cluster = _dbClient.queryObject(Cluster.class, clusterId);
      Vcenter vcenter = _dbClient.queryObject(Vcenter.class, vcenterId);
      Host host = _dbClient.queryObject(Host.class, hostId);
      Volume volume = _dbClient.queryObject(Volume.class, volumeId);

      vcenterApiClient = new VcenterApiClient(_coordinator.getPropertyInfo());
      vcenterApiClient.setup(vcenter.getIpAddress(), vcenter.getUsername(), vcenter.getPassword());
      String key =
          vcenterApiClient.createDatastore(
              vcenterDataCenter.getLabel(),
              cluster.getExternalId(),
              host.getHostName(),
              volume.getWWN(),
              volume.getLabel());
      _log.info("Successfully created or located datastore " + volume.getLabel() + " " + key);

      host.setVcenterDataCenter(vcenterDataCenter.getId());
      _dbClient.updateAndReindexObject(host);

      WorkflowStepCompleter.stepSucceded(stepId);
    } catch (Exception e) {
      _log.error("vcenterClusterCreateDatastoreOperation exception " + e);
      WorkflowStepCompleter.stepFailed(
          stepId, VcenterControllerException.exceptions.hostException(e.getLocalizedMessage(), e));
    } finally {
      if (vcenterApiClient != null) vcenterApiClient.destroy();
    }
  }
  private List<String> getVirtualMachines(
      URI datacenterUri, URI clusterUri, URI hostUri, boolean runningOnly)
      throws InternalException {
    VcenterApiClient vcenterApiClient = null;
    try {
      Host host = _dbClient.queryObject(Host.class, hostUri);
      VcenterDataCenter vcenterDataCenter =
          _dbClient.queryObject(VcenterDataCenter.class, datacenterUri);
      Cluster cluster = _dbClient.queryObject(Cluster.class, clusterUri);
      Vcenter vcenter = _dbClient.queryObject(Vcenter.class, vcenterDataCenter.getVcenter());

      _log.info(
          "Request to get virtual machines for "
              + vcenter.getLabel()
              + "/"
              + vcenterDataCenter.getLabel()
              + "/"
              + cluster.getLabel()
              + "/"
              + host.getHostName());

      vcenterApiClient = new VcenterApiClient(_coordinator.getPropertyInfo());
      vcenterApiClient.setup(vcenter.getIpAddress(), vcenter.getUsername(), vcenter.getPassword());
      return runningOnly
          ? vcenterApiClient.getRunningVirtualMachines(
              vcenterDataCenter.getLabel(), cluster.getExternalId(), host.getHostName())
          : vcenterApiClient.getVirtualMachines(
              vcenterDataCenter.getLabel(), cluster.getExternalId(), host.getHostName());
    } catch (VcenterObjectConnectionException e) {
      throw VcenterControllerException.exceptions.objectConnectionException(
          e.getLocalizedMessage(), e);
    } catch (VcenterObjectNotFoundException e) {
      throw VcenterControllerException.exceptions.objectNotFoundException(
          e.getLocalizedMessage(), e);
    } catch (VcenterServerConnectionException e) {
      throw VcenterControllerException.exceptions.serverConnectionException(
          e.getLocalizedMessage(), e);
    } catch (Exception e) {
      _log.error("getVirtualMachines exception " + e);
      throw VcenterControllerException.exceptions.unexpectedException(e.getLocalizedMessage(), e);
    } finally {
      if (vcenterApiClient != null) vcenterApiClient.destroy();
    }
  }