/**
   * Validates the individual list of acl entries. It is not valid acl entries list, when an acl
   * entry contains more than one privilege or privileges other than USE and if the tenant provided
   * in the acl entry is not a valid tenant org.
   *
   * @param aclEntries acl entries to be validated.
   */
  private void validateAclEntries(List<ACLEntry> aclEntries) {
    if (CollectionUtils.isEmpty(aclEntries)) {
      return;
    }

    Iterator<ACLEntry> aclEntryIterator = aclEntries.iterator();
    while (aclEntryIterator.hasNext()) {
      ACLEntry aclEntry = aclEntryIterator.next();

      // If more than one privileges provided the ACL Entry, it is not supported
      // for vCenter ACL. Only USE ACL can be provided.
      if (aclEntry.getAces().size() != 1) {
        throw APIException.badRequests.unsupportedNumberOfPrivileges(
            URI.create(aclEntry.getTenant()), aclEntry.getAces());
      }

      if (!aclEntry.getAces().get(0).equalsIgnoreCase(ACL.USE.name())) {
        throw APIException.badRequests.unsupportedPrivilege(
            URI.create(aclEntry.getTenant()), aclEntry.getAces().get(0));
      }

      // Validate if the provided tenant is a valid tenant or not.
      URI tenantId = URI.create(aclEntry.getTenant());
      TenantOrg tenant = queryObject(TenantOrg.class, tenantId, true);
      ArgValidator.checkEntity(tenant, tenantId, isIdEmbeddedInURL(tenantId));
    }
  }
  /**
   * Gets the current acl assignments of the requested vCenter.
   *
   * @param vcenterId
   * @return the list of acl assignments of the requested vCenter.
   */
  private ACLAssignments getAclAssignmentsResponse(URI vcenterId) {
    Vcenter vcenter = queryObject(Vcenter.class, vcenterId, true);
    ArgValidator.checkEntity(vcenter, vcenterId, isIdEmbeddedInURL(vcenterId));

    ACLAssignments response = new ACLAssignments();
    response.setAssignments(_permissionsHelper.convertToACLEntries(vcenter.getAcls()));

    return response;
  }
  /**
   * Lists the id and name of all the vCenters that belong to the given tenant organization if the
   * requesting user is a SysAdmin or SecAdmin. If the requested user is a TenantAdmin and user is a
   * tenant of the "tenant" given in the query param then all the vCenters of the tenant query param
   * will be returned otherwise returned only the vCenters that the user's tenant shares.
   *
   * @param tid tenant to filter the vCenters. "No-Filter" or "null" indicates, listing all the
   *     vCenters in the system. "Not-Assigned" indicates, list all the vCenters with no tenants
   *     assigned to it.
   * @return a list of vCenters that belong to the tenant organization.
   * @throws DatabaseException when a DB error occurs
   */
  @GET
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  public VcenterList listVcenters(@QueryParam("tenant") final URI tid) throws DatabaseException {
    VcenterList list = new VcenterList();
    List<Vcenter> vcenters = null;

    if (isSecurityAdmin() || isSystemAdmin()) {
      _log.debug("Fetching vCenters for {}", tid);
      if (NullColumnValueGetter.isNullURI(tid)
          || Vcenter.NO_TENANT_SELECTOR.equalsIgnoreCase(tid.toString())) {
        vcenters = getDataObjects(Vcenter.class);
        list.setVcenters(
            map(
                ResourceTypeEnum.VCENTER,
                getNamedElementsList(Vcenter.class, DATAOBJECT_NAME_FIELD, vcenters)));
      } else if (Vcenter.TENANT_SELECTOR_FOR_UNASSIGNED.equalsIgnoreCase(tid.toString())) {
        vcenters = getDataObjects(Vcenter.class);
        list.setVcenters(
            map(
                ResourceTypeEnum.VCENTER,
                getNamedElementsWithNoAcls(Vcenter.class, DATAOBJECT_NAME_FIELD, vcenters)));
      } else {
        ArgValidator.checkEntity(_dbClient.queryObject(tid), tid, isIdEmbeddedInURL(tid));
        list.setVcenters(
            map(
                ResourceTypeEnum.VCENTER,
                listChildrenWithAcls(tid, Vcenter.class, DATAOBJECT_NAME_FIELD)));
      }
      return list;
    }

    vcenters = getDataObjects(Vcenter.class);
    if (!CollectionUtils.isEmpty(vcenters)) {
      List<Vcenter> tenantVcenterList = null;
      if (shouldTenantAdminUseTenantParam(tid)) {
        // If the tenant admin can use the tid query param, then the filtering should
        // happen based on the tenant query param.
        tenantVcenterList = filterVcentersByTenant(vcenters, tid);
      } else {
        // Get the vCenters based on the User's tenant org. If the user is not a tenant admin,
        // insufficient
        // permission exception will be thrown.
        tenantVcenterList = filterVcentersByTenant(vcenters, NullColumnValueGetter.getNullURI());
      }
      list.setVcenters(
          map(
              ResourceTypeEnum.VCENTER,
              getNamedElementsList(Vcenter.class, DATAOBJECT_NAME_FIELD, tenantVcenterList)));
    }
    return list;
  }
  /**
   * 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));
  }
  /**
   * List the vCenter data centers of the vCenter.
   *
   * @param id the URN of a ViPR vCenter
   * @param tid tenant to filter the vCenter data centers. "No-Filter" or "null" indicates, listing
   *     all the vCenters in the system. "Not-Assigned" indicates, list all the vCenters with no
   *     tenants assigned to it.
   * @prereq none
   * @brief List vCenter data centers
   * @return All the list of vCenter data centers.
   * @throws DatabaseException when a DB error occurs.
   */
  @GET
  @Path("/{id}/vcenter-data-centers")
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  public VcenterDataCenterList getVcenterDataCenters(
      @PathParam("id") URI id, @QueryParam("tenant") URI tid) throws DatabaseException {
    Vcenter vcenter = queryObject(Vcenter.class, id, false);
    ArgValidator.checkEntity(vcenter, id, isIdEmbeddedInURL(id));

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

    URI tenantId;
    if (isSecurityAdmin() || isSystemAdmin()) {
      tenantId = tid;
    } else {
      if (shouldTenantAdminUseTenantParam(tid)) {
        tenantId = tid;
      } else {
        tenantId = URI.create(getUserFromContext().getTenantId());
      }
    }

    _log.debug("Fetching the vCenterDataCenters for the tenant {}", tenantId);

    // get the vcenters
    VcenterDataCenterList list = new VcenterDataCenterList();
    List<NamedElementQueryResultList.NamedElement> elements =
        listChildren(id, VcenterDataCenter.class, DATAOBJECT_NAME_FIELD, "vcenter");

    // Filter the vCenterDataCenters based on the tenant.
    list.setDataCenters(
        map(
            ResourceTypeEnum.VCENTERDATACENTER,
            id,
            filterTenantResourcesByTenant(tenantId, VcenterDataCenter.class, elements)));
    return list;
  }
  /**
   * List the clusters in a vCenter
   *
   * @param id the URN of a ViPR vCenter
   * @prereq none
   * @brief List vCenter clusters
   * @return The list of clusters of the vCenter.
   * @throws DatabaseException when a DB error occurs.
   */
  @GET
  @Path("/{id}/clusters")
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  public ClusterList getVcenterClusters(@PathParam("id") URI id) throws DatabaseException {
    Vcenter vcenter = queryObject(Vcenter.class, id, false);
    ArgValidator.checkEntity(vcenter, id, isIdEmbeddedInURL(id));

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

    URI tenantId = URI.create(getUserFromContext().getTenantId());

    List<NamedElementQueryResultList.NamedElement> vCentersDataCenters =
        filterTenantResourcesByTenant(
            tenantId,
            VcenterDataCenter.class,
            listChildren(id, VcenterDataCenter.class, DATAOBJECT_NAME_FIELD, "vcenter"));

    ClusterList list = new ClusterList();
    Iterator<NamedElementQueryResultList.NamedElement> dataCentersIterator =
        vCentersDataCenters.iterator();
    while (dataCentersIterator.hasNext()) {
      NamedElementQueryResultList.NamedElement dataCenterElement = dataCentersIterator.next();
      list.getClusters()
          .addAll(
              map(
                  ResourceTypeEnum.CLUSTER,
                  listChildren(
                      dataCenterElement.getId(),
                      Cluster.class,
                      DATAOBJECT_NAME_FIELD,
                      "vcenterDataCenter")));
    }

    return list;
  }