/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource; import static com.emc.storageos.api.mapper.BucketMapper.map; import static com.emc.storageos.api.mapper.TaskMapper.toTask; import java.net.URI; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.api.mapper.functions.MapBucket; import com.emc.storageos.api.service.authorization.PermissionsHelper; import com.emc.storageos.api.service.impl.placement.BucketRecommendation; import com.emc.storageos.api.service.impl.placement.BucketScheduler; import com.emc.storageos.api.service.impl.placement.VirtualPoolUtil; import com.emc.storageos.api.service.impl.resource.utils.BucketACLUtility; import com.emc.storageos.api.service.impl.response.BulkList; import com.emc.storageos.api.service.impl.response.ProjOwnedResRepFilter; import com.emc.storageos.api.service.impl.response.ResRepFilter; import com.emc.storageos.api.service.impl.response.SearchedResRepList; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.model.Bucket; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.client.model.NamedURI; import com.emc.storageos.db.client.model.OpStatusMap; import com.emc.storageos.db.client.model.Operation; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Task; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.util.TaskUtils; import com.emc.storageos.db.client.util.NameGenerator; import com.emc.storageos.db.client.util.SizeUtil; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.ecs.api.ECSException; import com.emc.storageos.model.BulkIdParam; import com.emc.storageos.model.RelatedResourceRep; import com.emc.storageos.model.ResourceOperationTypeEnum; import com.emc.storageos.model.ResourceTypeEnum; import com.emc.storageos.model.TaskResourceRep; import com.emc.storageos.model.object.BucketACE; import com.emc.storageos.model.object.BucketACL; import com.emc.storageos.model.object.BucketBulkRep; import com.emc.storageos.model.object.BucketDeleteParam; import com.emc.storageos.model.object.BucketParam; import com.emc.storageos.model.object.BucketRestRep; import com.emc.storageos.model.object.BucketUpdateParam; import com.emc.storageos.model.object.ObjectBucketACLUpdateParams; import com.emc.storageos.security.audit.AuditLogManager; import com.emc.storageos.security.authentication.StorageOSUser; import com.emc.storageos.security.authorization.ACL; import com.emc.storageos.security.authorization.CheckPermission; import com.emc.storageos.security.authorization.DefaultPermissions; import com.emc.storageos.security.authorization.Role; import com.emc.storageos.services.OperationTypeEnum; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.svcs.errorhandling.resources.InternalException; import com.emc.storageos.volumecontroller.AttributeMatcher; import com.emc.storageos.volumecontroller.ObjectController; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; @Path("/object/buckets") @DefaultPermissions(readRoles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, readAcls = { ACL.OWN, ACL.ALL }, writeRoles = { Role.TENANT_ADMIN }, writeAcls = { ACL.OWN, ACL.ALL }) public class BucketService extends TaskResourceService { private static final Logger _log = LoggerFactory.getLogger(BucketService.class); private static final String EVENT_SERVICE_TYPE = "object"; private static final String SLASH = "/"; private static final String UNDER_SCORE = "_"; private static final String SPECIAL_CHAR_REGEX = "[^\\dA-Za-z\\_]"; private BucketScheduler _bucketScheduler; private NameGenerator _nameGenerator; @Override public String getServiceType() { return EVENT_SERVICE_TYPE; } @SuppressWarnings("unchecked") @Override public Class<Bucket> getResourceClass() { return Bucket.class; } @Override public BucketBulkRep queryBulkResourceReps(List<URI> ids) { Iterator<Bucket> _dbIterator = _dbClient.queryIterativeObjects(getResourceClass(), ids); return new BucketBulkRep(BulkList.wrapping(_dbIterator, MapBucket.getInstance())); } @Override protected BucketBulkRep queryFilteredBulkResourceReps( List<URI> ids) { Iterator<Bucket> _dbIterator = _dbClient.queryIterativeObjects(getResourceClass(), ids); BulkList.ResourceFilter<Bucket> filter = new BulkList.ProjectResourceFilter<Bucket>( getUserFromContext(), _permissionsHelper); return new BucketBulkRep(BulkList.wrapping(_dbIterator, MapBucket.getInstance(), filter)); } public NameGenerator getNameGenerator() { return _nameGenerator; } public void setNameGenerator(NameGenerator nameGenerator) { _nameGenerator = nameGenerator; } public void setBucketScheduler(BucketScheduler bucketScheduler) { _bucketScheduler = bucketScheduler; } /** * Creates bucket. * * <p> * NOTE: This is an asynchronous operation. * * @param param Bucket parameters * @param id the URN of a ViPR Project * @brief Create Bucket * @return Task resource representation * @throws InternalException */ @POST @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.TENANT_ADMIN }, acls = { ACL.OWN, ACL.ALL }) public TaskResourceRep createBucket(BucketParam param, @QueryParam("project") URI id) throws InternalException { // check project ArgValidator.checkFieldUriType(id, Project.class, "project"); // Check for all mandatory field ArgValidator.checkFieldNotNull(param.getLabel(), "name"); Project project = _permissionsHelper.getObjectById(id, Project.class); ArgValidator.checkEntity(project, id, isIdEmbeddedInURL(id)); ArgValidator.checkFieldNotNull(project.getTenantOrg(), "project"); TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, project.getTenantOrg().getURI()); final String namespace = tenant.getNamespace(); if (null == namespace || namespace.isEmpty()) { throw APIException.badRequests.objNoNamespaceForTenant(tenant.getId()); } // Check if there already exist a bucket with same name in a Project. checkForDuplicateName(param.getLabel().replaceAll(SPECIAL_CHAR_REGEX, ""), Bucket.class, id, "project", _dbClient); return initiateBucketCreation(param, project, tenant, null); } private TaskResourceRep initiateBucketCreation(BucketParam param, Project project, TenantOrg tenant, DataObject.Flag[] flags) throws InternalException { ArgValidator.checkFieldUriType(param.getVpool(), VirtualPool.class, "vpool"); ArgValidator.checkFieldUriType(param.getVarray(), VirtualArray.class, "varray"); Long softQuota = SizeUtil.translateSize(param.getSoftQuota()); Long hardQuota = SizeUtil.translateSize(param.getHardQuota()); Integer retention = Integer.valueOf(param.getRetention()); // Hard Quota should be more than SoftQuota verifyQuotaValues(softQuota, hardQuota, param.getLabel()); // check varray VirtualArray neighborhood = _dbClient.queryObject(VirtualArray.class, param.getVarray()); ArgValidator.checkEntity(neighborhood, param.getVarray(), false); _permissionsHelper.checkTenantHasAccessToVirtualArray(tenant.getId(), neighborhood); // check vpool reference VirtualPool vpool = _dbClient.queryObject(VirtualPool.class, param.getVpool()); _permissionsHelper.checkTenantHasAccessToVirtualPool(tenant.getId(), vpool); ArgValidator.checkEntity(vpool, param.getVpool(), false); if (!VirtualPool.Type.object.name().equals(vpool.getType())) { throw APIException.badRequests.virtualPoolNotForObjectStorage(VirtualPool.Type.object.name()); } // verify retention. Its validated only if Retention is configured. if (retention != 0 && vpool.getMaxRetention() != 0 && retention > vpool.getMaxRetention()) { throw APIException.badRequests.insufficientRetentionForVirtualPool(vpool.getLabel(), "bucket"); } VirtualPoolCapabilityValuesWrapper capabilities = new VirtualPoolCapabilityValuesWrapper(); capabilities.put(VirtualPoolCapabilityValuesWrapper.RESOURCE_COUNT, Integer.valueOf(1)); capabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_PROVISIONING, Boolean.FALSE); capabilities.put(VirtualPoolCapabilityValuesWrapper.QUOTA, hardQuota.toString()); Map<String, Object> attributeMap = new HashMap<String, Object>(); List<BucketRecommendation> placement = _bucketScheduler.placeBucket(neighborhood, vpool, capabilities, attributeMap); if (placement.isEmpty()) { StringBuffer errorMessage = new StringBuffer(); if (attributeMap.get(AttributeMatcher.ERROR_MESSAGE) != null) { errorMessage = (StringBuffer) attributeMap.get(AttributeMatcher.ERROR_MESSAGE); } throw APIException.badRequests.noStoragePools(neighborhood.getLabel(), vpool.getLabel(), errorMessage.toString()); } // Randomly select a recommended pool Collections.shuffle(placement); BucketRecommendation recommendation = placement.get(0); String task = UUID.randomUUID().toString(); Bucket bucket = prepareBucket(param, project, tenant, neighborhood, vpool, flags, recommendation); _log.info(String.format( "createBucket --- Bucket: %1$s, StoragePool: %2$s, StorageSystem: %3$s", bucket.getId(), recommendation.getSourceStoragePool(), recommendation.getSourceStorageSystem())); Operation op = _dbClient.createTaskOpStatus(Bucket.class, bucket.getId(), task, ResourceOperationTypeEnum.CREATE_BUCKET); op.setDescription("Bucket Create"); // Controller invocation StorageSystem system = _dbClient.queryObject(StorageSystem.class, recommendation.getSourceStorageSystem()); ObjectController controller = getController(ObjectController.class, system.getSystemType()); controller.createBucket(recommendation.getSourceStorageSystem(), recommendation.getSourceStoragePool(), bucket.getId(), bucket.getName(), bucket.getNamespace(), bucket.getRetention(), bucket.getHardQuota(), bucket.getSoftQuota(), bucket.getOwner(), task); auditOp(OperationTypeEnum.CREATE_BUCKET, true, AuditLogManager.AUDITOP_BEGIN, param.getLabel(), param.getHardQuota(), neighborhood.getId().toString(), project == null ? null : project.getId().toString()); return toTask(bucket, task, op); } /** * Get info for Bucket * * @param id the URN of a ViPR Bucket * @return Bucket details */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}") @CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.ANY }) public BucketRestRep getBucket(@PathParam("id") URI id) { ArgValidator.checkFieldUriType(id, Bucket.class, "id"); Bucket fs = queryResource(id); return map(fs); } @Override protected Bucket queryResource(URI id) { ArgValidator.checkUri(id); Bucket bucket = _permissionsHelper.getObjectById(id, Bucket.class); ArgValidator.checkEntityNotNull(bucket, id, isIdEmbeddedInURL(id)); return bucket; } @Override protected URI getTenantOwner(URI id) { Bucket bucket = queryResource(id); return bucket.getTenant().getURI(); } /** * Deactivate Bucket, this will move the Bucket to a "marked-for-delete" state * * <p> * NOTE: This is an asynchronous operation. * * @param id the URN of a ViPR Bucket * @param param Bucket delete param for optional force delete * @brief Delete Bucket * @return Task resource representation * @throws InternalException */ @POST @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}/deactivate") @CheckPermission(roles = { Role.TENANT_ADMIN }, acls = { ACL.OWN, ACL.ALL }) public TaskResourceRep deactivateBucket(@PathParam("id") URI id, BucketDeleteParam param) throws InternalException { String task = UUID.randomUUID().toString(); _log.info(String.format( "BucketDelete --- Bucket id: %1$s, Task: %2$s, ForceDelete: %3$s", id, task, param.getForceDelete())); ArgValidator.checkFieldUriType(id, Bucket.class, "id"); Bucket bucket = queryResource(id); StorageSystem device = _dbClient.queryObject(StorageSystem.class, bucket.getStorageDevice()); Operation op = _dbClient.createTaskOpStatus(Bucket.class, bucket.getId(), task, ResourceOperationTypeEnum.DELETE_BUCKET); op.setDescription("Bucket deactivate"); ObjectController controller = getController(ObjectController.class, device.getSystemType()); controller.deleteBucket(bucket.getStorageDevice(), id, param.getDeleteType(), task); auditOp(OperationTypeEnum.DELETE_BUCKET, true, AuditLogManager.AUDITOP_BEGIN, bucket.getId().toString(), device.getId().toString()); return toTask(bucket, task, op); } /** * Retrieve resource representations based on input ids. * * @param param POST data containing the id list. * @brief List data of Bucket resources * @return list of representations. * * @throws DatabaseException When an error occurs querying the database. */ @POST @Path("/bulk") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Override public BucketBulkRep getBulkResources(BulkIdParam param) { return (BucketBulkRep) super.getBulkResources(param); } /** * Allocate, initialize and persist state of the Bucket being created. * * @param param * @param project * @param tenantOrg * @param neighborhood * @param vpool * @param flags * @param placement * @return */ private Bucket prepareBucket(BucketParam param, Project project, TenantOrg tenantOrg, VirtualArray neighborhood, VirtualPool vpool, DataObject.Flag[] flags, BucketRecommendation placement) { _log.debug("Preparing Bucket creation for Param : {}", param); StoragePool pool = null; Bucket bucket = new Bucket(); bucket.setId(URIUtil.createId(Bucket.class)); bucket.setLabel(param.getLabel().replaceAll(SPECIAL_CHAR_REGEX, "")); bucket.setHardQuota(SizeUtil.translateSize(param.getHardQuota())); bucket.setSoftQuota(SizeUtil.translateSize(param.getSoftQuota())); bucket.setRetention(Integer.valueOf(param.getRetention())); bucket.setOwner(param.getOwner()); bucket.setNamespace(tenantOrg.getNamespace()); bucket.setVirtualPool(param.getVpool()); if (project != null) { bucket.setProject(new NamedURI(project.getId(), bucket.getLabel())); } bucket.setTenant(new NamedURI(tenantOrg.getId(), param.getLabel())); bucket.setVirtualArray(neighborhood.getId()); if (null != placement.getSourceStoragePool()) { pool = _dbClient.queryObject(StoragePool.class, placement.getSourceStoragePool()); if (null != pool) { bucket.setProtocol(new StringSet()); bucket.getProtocol().addAll(VirtualPoolUtil.getMatchingProtocols(vpool.getProtocols(), pool.getProtocols())); } } bucket.setStorageDevice(placement.getSourceStorageSystem()); bucket.setPool(placement.getSourceStoragePool()); bucket.setOpStatus(new OpStatusMap()); // Bucket name to be used at Storage System String bucketName = project.getLabel() + UNDER_SCORE + param.getLabel(); bucket.setName(bucketName.replaceAll(SPECIAL_CHAR_REGEX, "")); // Update Bucket path StringBuilder bucketPath = new StringBuilder(); bucketPath.append(tenantOrg.getNamespace()).append(SLASH).append(project.getLabel()).append(SLASH).append(param.getLabel()); bucket.setPath(bucketPath.toString()); if (flags != null) { bucket.addInternalFlags(flags); } _dbClient.createObject(bucket); return bucket; } /** * Updates Bucket values like Quota and Retention. * * @param id Bucket ID * @param param Bucket update parameter * @return Task resource representation * @throws InternalException if update fails */ @PUT @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.TENANT_ADMIN }, acls = { ACL.OWN, ACL.ALL }) public TaskResourceRep updateBucket(@PathParam("id") URI id, BucketUpdateParam param) throws InternalException { Bucket bucket = null; ArgValidator.checkFieldUriType(id, Bucket.class, "id"); bucket = _dbClient.queryObject(Bucket.class, id); ArgValidator.checkEntity(bucket, id, isIdEmbeddedInURL(id)); Long softQuota = SizeUtil.translateSize(param.getSoftQuota()); Long hardQuota = SizeUtil.translateSize(param.getHardQuota()); Integer retention = null != param.getRetention() ? Integer.valueOf(param.getRetention()) : 0; // if no softquota is provided, use the old value if (softQuota == 0) { softQuota = bucket.getSoftQuota(); } // if no hardquota is provided, use the old value if (hardQuota == 0) { hardQuota = bucket.getHardQuota(); } // Hard Quota should be more than SoftQuota verifyQuotaValues(softQuota, hardQuota, bucket.getLabel()); // if no retention is provided, use the old value if (retention == 0) { retention = bucket.getRetention(); } VirtualPool cos = _dbClient.queryObject(VirtualPool.class, bucket.getVirtualPool()); // verify retention. Its validated only if Retention is configured. if (retention != 0 && cos.getMaxRetention() != 0 && retention > cos.getMaxRetention()) { throw APIException.badRequests.insufficientRetentionForVirtualPool(cos.getLabel(), "bucket"); } String task = UUID.randomUUID().toString(); _log.info(String.format( "BucketUpdate --- Bucket id: %1$s, Task: %2$s", id, task)); StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, bucket.getStorageDevice()); Operation op = _dbClient.createTaskOpStatus(Bucket.class, bucket.getId(), task, ResourceOperationTypeEnum.UPDATE_BUCKET); op.setDescription("Bucket update"); ObjectController controller = getController(ObjectController.class, storageSystem.getSystemType()); controller.updateBucket(bucket.getStorageDevice(), id, softQuota, hardQuota, retention, task); auditOp(OperationTypeEnum.UPDATE_BUCKET, true, AuditLogManager.AUDITOP_BEGIN, bucket.getId().toString(), bucket.getStorageDevice().toString()); return toTask(bucket, task, op); } /** * Add/Update the ACL settings for bucket * * @param id * @param param * @return * @throws InternalException */ @PUT @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}/acl") @CheckPermission(roles = { Role.TENANT_ADMIN }, acls = { ACL.OWN, ACL.ALL }) public TaskResourceRep updateBucketACL(@PathParam("id") URI id, ObjectBucketACLUpdateParams param) throws InternalException { _log.info("Update bucket acl request received. BucketId: {}", id.toString()); _log.info("Request body: {}", param.toString()); Bucket bucket = null; ArgValidator.checkFieldUriType(id, Bucket.class, "id"); bucket = _dbClient.queryObject(Bucket.class, id); ArgValidator.checkEntity(bucket, id, isIdEmbeddedInURL(id)); if (bucket.getVersion() == null) { syncBucketACL(bucket); } // Verify the Bucket ACL Settings BucketACLUtility bucketACLUtil = new BucketACLUtility(_dbClient, bucket.getName(), bucket.getId()); bucketACLUtil.verifyBucketACL(param); _log.info("Request payload verified. No errors found."); StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, bucket.getStorageDevice()); ObjectController controller = getController(ObjectController.class, storageSystem.getSystemType()); String task = UUID.randomUUID().toString(); _log.info(String.format( "Bucket ACL Update --- Bucket id: %1$s, Task: %2$s", id, task)); Operation op = _dbClient.createTaskOpStatus(Bucket.class, bucket.getId(), task, ResourceOperationTypeEnum.UPDATE_BUCKET_ACL); op.setDescription("Bucket ACL update"); controller.updateBucketACL(bucket.getStorageDevice(), id, param, task); auditOp(OperationTypeEnum.UPDATE_BUCKET_ACL, true, AuditLogManager.AUDITOP_BEGIN, bucket.getId().toString(), bucket.getStorageDevice().toString()); return toTask(bucket, task, op); } /** * Gets the ACL settings for bucket * * @param id * @return * @throws InternalException */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}/acl") @CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.ANY }) public BucketACL getBucketACL(@PathParam("id") URI id) throws InternalException { _log.info("Request recieved to get Bucket ACL with Id: {}", id); // Validate the Bucket Bucket bucket = null; ArgValidator.checkFieldUriType(id, Bucket.class, "id"); bucket = _dbClient.queryObject(Bucket.class, id); ArgValidator.checkEntity(bucket, id, isIdEmbeddedInURL(id)); if (bucket.getVersion() == null) { syncBucketACL(bucket); } BucketACL bucketAcl = new BucketACL(); BucketACLUtility bucketACLUtil = new BucketACLUtility(_dbClient, bucket.getName(), bucket.getId()); List<BucketACE> bucketAces = bucketACLUtil.queryExistingBucketACL(); _log.info("Number of existing ACLs found : {} ", bucketAces.size()); if (!bucketAces.isEmpty()) { bucketAcl.setBucketACL(bucketAces); } return bucketAcl; } @DELETE @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("/{id}/acl") @CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.ANY }) public TaskResourceRep deleteBucketACL(@PathParam("id") URI id) { _log.info("Request recieved to delete ACL for the Bucket Id: {}", id); // Validate the Bucket Bucket bucket = null; ArgValidator.checkFieldUriType(id, Bucket.class, "id"); bucket = _dbClient.queryObject(Bucket.class, id); ArgValidator.checkEntity(bucket, id, isIdEmbeddedInURL(id)); StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, bucket.getStorageDevice()); ObjectController controller = getController(ObjectController.class, storageSystem.getSystemType()); String task = UUID.randomUUID().toString(); _log.info(String.format( "Delete Bucket ACL --- Bucket id: %1$s, Task: %2$s", id, task)); Operation op = _dbClient.createTaskOpStatus(Bucket.class, bucket.getId(), task, ResourceOperationTypeEnum.DELETE_BUCKET_ACL); op.setDescription("Delete Bucket ACL"); controller.deleteBucketACL(bucket.getStorageDevice(), id, task); auditOp(OperationTypeEnum.DELETE_BUCKET_ACL, true, AuditLogManager.AUDITOP_BEGIN, bucket.getId().toString(), bucket.getStorageDevice().toString()); return toTask(bucket, task, op); } @Override protected ResourceTypeEnum getResourceType() { return ResourceTypeEnum.BUCKET; } /** * Bucket is not a zone level resource */ @Override protected boolean isZoneLevelResource() { return false; } /** * Get search results by project alone. * * @return SearchedResRepList */ @Override protected SearchedResRepList getProjectSearchResults(URI projectId) { SearchedResRepList resRepList = new SearchedResRepList(getResourceType()); _dbClient.queryByConstraint( ContainmentConstraint.Factory.getProjectBucketConstraint(projectId), resRepList); return resRepList; } /** * Get object specific permissions filter * */ @Override public ResRepFilter<? extends RelatedResourceRep> getPermissionFilter(StorageOSUser user, PermissionsHelper permissionsHelper) { return new ProjOwnedResRepFilter(user, permissionsHelper, Bucket.class); } /** * Hard Quota should be more than SoftQuota * * @param softQuota Soft Quota value * @param hardQuota Hard Quota value * @param bucketName Bucket name * @throws APIException If SoftQuota is more than HardQuota */ private void verifyQuotaValues(Long softQuota, Long hardQuota, String bucketName) throws APIException { if (softQuota < 0 || hardQuota < 0 || softQuota > hardQuota) { throw APIException.badRequests.invalidQuotaRequestForObjectStorage(bucketName); } } private void syncBucketACL(Bucket bucket) throws InternalException { // Make sure that we don't have some pending // operation against the bucket checkForPendingTasks(Arrays.asList(bucket.getTenant().getURI()), Arrays.asList(bucket)); StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, bucket.getStorageDevice()); ObjectController controller = getController(ObjectController.class, storageSystem.getSystemType()); String task = UUID.randomUUID().toString(); _log.info(String.format( "SYNC Bucket ACL --- Bucket id: %1$s, Task: %2$s", bucket.getId(), task)); Operation op = _dbClient.createTaskOpStatus(Bucket.class, bucket.getId(), task, ResourceOperationTypeEnum.SYNC_BUCKET_ACL); op.setDescription("Sync Bucket ACL"); controller.syncBucketACL(bucket.getStorageDevice(), bucket.getId(), task); auditOp(OperationTypeEnum.SYNC_BUCKET_ACL, true, AuditLogManager.AUDITOP_BEGIN, bucket.getId().toString(), bucket.getStorageDevice().toString()); toTask(bucket, task, op); // Waiting till the task is ready to proceed. boolean breakLoop = false; boolean failedOp = true; long startTime = System.currentTimeMillis(); String READY = "ready"; String ERROR = "error"; String message = ""; int MAX_SYNC_TIMEOUT = 8000; while (!breakLoop) { Task dbTask = TaskUtils.findTaskForRequestId(_dbClient, bucket.getId(), task); if (READY.equals(dbTask.getStatus())) { breakLoop = true; failedOp = false; } if (ERROR.equals(dbTask.getStatus())) { breakLoop = true; message = dbTask.getMessage(); } if ((System.currentTimeMillis() - startTime) > MAX_SYNC_TIMEOUT) { breakLoop = true; message = "Request Time-Out Wait untill bucket sync task is finished."; } try { Thread.sleep(100); } catch (InterruptedException ex) { // When we catch the InterruptException and swallow it, we essentially prevent any higher level methods/thread groups from // noticing the interrupt. Which may cause problems. // By calling Thread.currentThread().interrupt(), we set the interrupt flag of the thread, so higher level interrupt // handlers will notice it and can handle it appropriately. Thread.currentThread().interrupt(); } } if (failedOp) { throw ECSException.exceptions.bucketACLUpdateFailed(bucket.getName(), "Could not get ACL from ECS {} " + message + " Please try again later."); } } }