/**
   * Deactivates the vCenter, its vCenter data centers, clusters and hosts.
   *
   * @param id the URN of a ViPR vCenter to be deactivated
   * @prereq none
   * @brief Delete vCenter
   * @return OK if deactivation completed successfully
   * @throws DatabaseException when a DB error occurs
   */
  @POST
  @Path("/{id}/deactivate")
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @CheckPermission(roles = {Role.SYSTEM_ADMIN, Role.TENANT_ADMIN})
  public TaskResourceRep deactivateVcenter(
      @PathParam("id") URI id,
      @DefaultValue("false") @QueryParam("detach-storage") boolean detachStorage)
      throws DatabaseException {
    if (ComputeSystemHelper.isVcenterInUse(_dbClient, id) && !detachStorage) {
      throw APIException.badRequests.resourceHasActiveReferences(Vcenter.class.getSimpleName(), id);
    } else {
      Vcenter vcenter = queryObject(Vcenter.class, id, true);

      // check the user permissions for this tenant org
      verifyAuthorizedSystemOrTenantOrgUser(
          _permissionsHelper.convertToACLEntries(vcenter.getAcls()));

      checkIfOtherTenantsUsingTheVcenter(vcenter);

      String taskId = UUID.randomUUID().toString();
      Operation op =
          _dbClient.createTaskOpStatus(
              Vcenter.class, vcenter.getId(), taskId, ResourceOperationTypeEnum.DELETE_VCENTER);

      ComputeSystemController controller = getController(ComputeSystemController.class, null);
      controller.detachVcenterStorage(vcenter.getId(), true, taskId);

      auditOp(OperationTypeEnum.DELETE_VCENTER, true, null, vcenter.auditParameters());

      TaskResourceRep taskResourceRep = toTask(vcenter, taskId, op);
      updateTaskTenant(taskResourceRep);

      return taskResourceRep;
    }
  }
  /**
   * Creates a manual (fake) vcenter discover task, so that there wont be any vcenter discovery
   * happening because of this task.
   *
   * @param vcenter vcenter to create its manual/fake discovery task.
   * @return returns fake/manual vcenter discovery task.
   */
  private TaskResourceRep createManualReadyTask(Vcenter vcenter) {
    // if not discoverable, manually create a ready task
    Operation op = new Operation();
    op.setResourceType(ResourceOperationTypeEnum.DISCOVER_VCENTER);
    op.ready("Vcenter not discoverable.");

    String taskId = UUID.randomUUID().toString();
    _dbClient.createTaskOpStatus(Host.class, vcenter.getId(), taskId, op);

    return toTask(vcenter, taskId, op);
  }
  /**
   * Detaches storage from the vcenter.
   *
   * @param id the URN of a ViPR vcenter
   * @brief Detach storage from vcenter
   * @return task
   * @throws DatabaseException when a DB error occurs
   */
  @POST
  @Path("/{id}/detach-storage")
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @CheckPermission(roles = {Role.SYSTEM_ADMIN, Role.TENANT_ADMIN})
  public TaskResourceRep detachStorage(@PathParam("id") URI id) throws DatabaseException {
    Vcenter vcenter = queryObject(Vcenter.class, id, true);
    ArgValidator.checkEntity(vcenter, id, true);

    checkIfOtherTenantsUsingTheVcenter(vcenter);

    String taskId = UUID.randomUUID().toString();
    Operation op =
        _dbClient.createTaskOpStatus(
            Vcenter.class,
            vcenter.getId(),
            taskId,
            ResourceOperationTypeEnum.DETACH_VCENTER_DATACENTER_STORAGE);
    ComputeSystemController controller = getController(ComputeSystemController.class, null);
    controller.detachVcenterStorage(vcenter.getId(), false, taskId);
    return toTask(vcenter, taskId, op);
  }
  /**
   * Add or remove individual Access Control List entry(s). When the vCenter is created with no
   * shared access (Vcenter.shared = Boolean.FALSE), there cannot be multiple Access Control List
   * Entries associated with this vCenter.
   *
   * @param changes Access Control List assignment changes. Request body must include at least one
   *     add or remove operation
   * @param id the URN of a ViPR Project.
   * @return the vCenter discovery async task.
   */
  @PUT
  @Path("/{id}/acl")
  @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @CheckPermission(roles = {Role.SECURITY_ADMIN, Role.SYSTEM_ADMIN})
  public TaskResourceRep updateAclAssignments(
      @PathParam("id") URI id, ACLAssignmentChanges changes) {
    // Make sure the vCenter is a valid one.
    Vcenter vcenter = queryObject(Vcenter.class, id, true);
    ArgValidator.checkEntity(vcenter, id, isIdEmbeddedInURL(id));

    // Validate the acl assignment changes. It is not valid when an
    // acl entry contains more than one privilege or privileges
    // other than USE.
    validateAclAssignments(changes);

    // Make sure that the vCenter with respect to the tenants
    // that we are removing is not in use (means the datacenters
    // and its clusters and hosts with the removing tenant do not
    // have any exports).
    checkVcenterUsage(vcenter, changes);

    _permissionsHelper.updateACLs(
        vcenter, changes, new PermissionsHelper.UsageACLFilter(_permissionsHelper));

    _dbClient.updateAndReindexObject(vcenter);

    auditOp(
        OperationTypeEnum.UPDATE_VCENTER,
        true,
        null,
        vcenter.getId().toString(),
        vcenter.getLabel(),
        changes);

    // Rediscover the vCenter, this will update the updated
    // list of tenants based its latest acls to its datacenters
    // and hosts and clusters.
    return doDiscoverVcenter(queryObject(Vcenter.class, vcenter.getId(), true));
  }
  /**
   * Vcenter Discovery
   *
   * @param vcenter the Vcenter to be discovered. provided, a new taskId is generated.
   * @return the task used to track the discovery job
   */
  protected TaskResourceRep doDiscoverVcenter(Vcenter vcenter) {
    ComputeSystemController controller = getController(ComputeSystemController.class, "vcenter");
    DiscoveredObjectTaskScheduler scheduler =
        new DiscoveredObjectTaskScheduler(_dbClient, new DiscoverJobExec(controller));
    String taskId = UUID.randomUUID().toString();
    ArrayList<AsyncTask> tasks = new ArrayList<AsyncTask>(1);
    tasks.add(new AsyncTask(Vcenter.class, vcenter.getId(), taskId));

    TaskList taskList = scheduler.scheduleAsyncTasks(tasks);

    TaskResourceRep taskResourceRep = taskList.getTaskList().iterator().next();
    updateTaskTenant(taskResourceRep);

    return taskResourceRep;
  }
  /**
   * Creates a new vCenter. Discovery is initiated after the vCenter is created.
   *
   * @param createParam the parameter that has the attributes of the vCenter to be created.
   * @param validateConnection specifies if the connection to the vCenter to be validated before
   *     creating the vCenter or not. Default value is "false", so connection to the vCenter will
   *     not be validated if it is not specified.
   * @return the vCenter discovery async task.
   */
  @POST
  @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @CheckPermission(roles = {Role.SYSTEM_ADMIN, Role.TENANT_ADMIN})
  public TaskResourceRep createVcenter(
      VcenterCreateParam createParam,
      @QueryParam("validate_connection") @DefaultValue("false") final Boolean validateConnection) {
    validateVcenter(createParam, null, validateConnection);

    // create and persist the vcenter
    Vcenter vcenter = createNewVcenter(null, createParam);
    vcenter.setRegistrationStatus(DiscoveredDataObject.RegistrationStatus.REGISTERED.toString());
    _dbClient.createObject(vcenter);
    auditOp(OperationTypeEnum.CREATE_VCENTER, true, null, vcenter.auditParameters());

    return doDiscoverVcenter(queryObject(Vcenter.class, vcenter.getId(), true));
  }
  /**
   * Check if the vCenter being updated is used by any of its vCenterDataCenters or clusters or
   * hosts or not. This validates only with respect to the tenant that is being removed from the
   * vCenter acls. If the tenant that is getting removed teh vCenter has any exports with the
   * vCenter's vCenterDataCenter or its clusters or hosts.
   *
   * @param vcenter the vCenter being updated.
   * @param changes new acl assignment changes for the vCenter.
   */
  private void checkVcenterUsage(Vcenter vcenter, ACLAssignmentChanges changes) {
    // Make a copy of the vCenter's existing tenant list.
    List<ACLEntry> existingAclEntries = _permissionsHelper.convertToACLEntries(vcenter.getAcls());
    if (CollectionUtils.isEmpty(existingAclEntries)) {
      // If there no existing acl entries for the vCenter
      // there is nothing to validate if it is in user or not.
      _log.debug("vCenter {} does not have any existing acls", vcenter.getLabel());
      return;
    }

    // If there are no tenants to be removed from the vCenter acls,
    // there is nothing to check for usage.
    if (CollectionUtils.isEmpty(changes.getRemove())) {
      _log.debug("There are not acls to remove from vCenter {}", vcenter.getLabel());
      return;
    }

    Set<String> tenantsInUse = new HashSet<String>();

    Set<URI> removingTenants = _permissionsHelper.getUsageURIsFromAclEntries(changes.getRemove());
    Set<URI> existingTenants = _permissionsHelper.getUsageURIsFromAclEntries(existingAclEntries);

    Iterator<URI> removingTenantsIterator = removingTenants.iterator();
    while (removingTenantsIterator.hasNext()) {
      URI removingTenant = removingTenantsIterator.next();
      if (!existingTenants.contains(removingTenant)) {
        continue;
      }

      // Check if vCenter is in use for the removing tenant or not.
      // This checks for all the datacenters of this vcenter that belong to the
      // removing tenant and finds if the datacenter or it clusters or hosts
      // use the exports from the removing tenant or not.
      if (ComputeSystemHelper.isVcenterInUseForTheTenant(
          _dbClient, vcenter.getId(), removingTenant)) {
        TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, removingTenant);
        tenantsInUse.add(tenant.getLabel());
      }
    }

    if (!CollectionUtils.isEmpty(tenantsInUse)) {
      throw APIException.badRequests.cannotRemoveTenant("vCener", vcenter.getLabel(), tenantsInUse);
    }
  }