/* * Copyright (c) 2017 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource; import static com.emc.storageos.api.mapper.DbObjectMapper.toLink; import static com.emc.storageos.api.mapper.DbObjectMapper.toNamedRelatedResource; import static com.emc.storageos.api.mapper.FilePolicyMapper.map; import static com.emc.storageos.api.mapper.TaskMapper.toTask; import java.net.URI; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; 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.core.MediaType; import javax.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.emc.storageos.api.mapper.FilePolicyMapper; import com.emc.storageos.api.mapper.functions.MapFilePolicy; import com.emc.storageos.api.service.authorization.PermissionsHelper; import com.emc.storageos.api.service.impl.placement.FileMirrorRecommendation; import com.emc.storageos.api.service.impl.placement.FileMirrorRecommendation.Target; import com.emc.storageos.api.service.impl.placement.FilePlacementManager; import com.emc.storageos.api.service.impl.placement.FileRecommendation; import com.emc.storageos.api.service.impl.resource.utils.FilePolicyServiceUtils; import com.emc.storageos.api.service.impl.response.BulkList; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.model.FilePolicy; import com.emc.storageos.db.client.model.FilePolicy.FilePolicyApplyLevel; import com.emc.storageos.db.client.model.FilePolicy.FilePolicyPriority; import com.emc.storageos.db.client.model.FilePolicy.FilePolicyType; import com.emc.storageos.db.client.model.FilePolicy.FileReplicationType; import com.emc.storageos.db.client.model.FilePolicy.SnapshotExpireType; import com.emc.storageos.db.client.model.FileReplicationTopology; import com.emc.storageos.db.client.model.OpStatusMap; import com.emc.storageos.db.client.model.Operation; import com.emc.storageos.db.client.model.PolicyStorageResource; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Task; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.fileorchestrationcontroller.FileOrchestrationController; import com.emc.storageos.fileorchestrationcontroller.FileOrchestrationUtils; import com.emc.storageos.fileorchestrationcontroller.FileStorageSystemAssociation; import com.emc.storageos.fileorchestrationcontroller.FileStorageSystemAssociation.TargetAssociation; import com.emc.storageos.model.BulkIdParam; import com.emc.storageos.model.ResourceOperationTypeEnum; import com.emc.storageos.model.ResourceTypeEnum; import com.emc.storageos.model.TaskResourceRep; import com.emc.storageos.model.auth.ACLAssignmentChanges; import com.emc.storageos.model.auth.ACLAssignments; import com.emc.storageos.model.file.policy.FilePolicyAssignParam; import com.emc.storageos.model.file.policy.FilePolicyBulkRep; import com.emc.storageos.model.file.policy.FilePolicyCreateParam; import com.emc.storageos.model.file.policy.FilePolicyCreateResp; import com.emc.storageos.model.file.policy.FilePolicyListRestRep; import com.emc.storageos.model.file.policy.FilePolicyRestRep; import com.emc.storageos.model.file.policy.FilePolicyStorageResourceRestRep; import com.emc.storageos.model.file.policy.FilePolicyStorageResources; import com.emc.storageos.model.file.policy.FilePolicyUnAssignParam; import com.emc.storageos.model.file.policy.FilePolicyUpdateParam; import com.emc.storageos.model.file.policy.FileReplicationPolicyParam; import com.emc.storageos.model.file.policy.FileReplicationTopologyParam; import com.emc.storageos.model.file.policy.FileSnapshotPolicyExpireParam; import com.emc.storageos.security.audit.AuditLogManager; import com.emc.storageos.security.authentication.StorageOSUser; 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.BadRequestException; import com.emc.storageos.volumecontroller.impl.monitoring.RecordableEventManager; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; /** * @author jainm15 */ @Path("/file/file-policies") @DefaultPermissions(readRoles = { Role.TENANT_ADMIN, Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }, writeRoles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public class FilePolicyService extends TaskResourceService { private static final Logger _log = LoggerFactory.getLogger(FilePolicyService.class); protected static final String EVENT_SERVICE_SOURCE = "FilePolicyService"; private static final String EVENT_SERVICE_TYPE = "FilePolicy"; // File service implementations static volatile private Map<String, FileServiceApi> _fileServiceApis; @Autowired private RecordableEventManager _evtMgr; @Autowired private NetworkService networkSvc; private FilePlacementManager _filePlacementManager; public void setFilePlacementManager(FilePlacementManager placementManager) { _filePlacementManager = placementManager; } @Override public String getServiceType() { return EVENT_SERVICE_TYPE; } public static void setFileServiceApis(Map<String, FileServiceApi> _fileServiceApis) { FilePolicyService._fileServiceApis = _fileServiceApis; } public static FileServiceApi getDefaultFileServiceApi() { return _fileServiceApis.get("default"); } @Override protected FilePolicy queryResource(URI id) { ArgValidator.checkUri(id); FilePolicy filePolicy = _permissionsHelper.getObjectById(id, FilePolicy.class); ArgValidator.checkEntityNotNull(filePolicy, id, isIdEmbeddedInURL(id)); return filePolicy; } @Override protected URI getTenantOwner(URI id) { return null; } @Override protected ResourceTypeEnum getResourceType() { return ResourceTypeEnum.FILE_POLICY; } @SuppressWarnings("unchecked") @Override protected Class<FilePolicy> getResourceClass() { return FilePolicy.class; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public FilePolicyBulkRep queryBulkResourceReps(List<URI> ids) { Iterator<FilePolicy> _dbIterator = _dbClient.queryIterativeObjects( getResourceClass(), ids); BulkList.ResourceFilter filter = new BulkList.FilePolicyResourceFilter(getUserFromContext(), _permissionsHelper); return new FilePolicyBulkRep(BulkList.wrapping(_dbIterator, MapFilePolicy.getInstance(_dbClient), filter)); } @Override public FilePolicyBulkRep queryFilteredBulkResourceReps(List<URI> ids) { return queryBulkResourceReps(ids); } /** * Retrieve resource representations based on input ids. * * @param param * POST data containing the id list. * @brief List of file policies of given ids. * @return list of representations. */ @POST @Path("/bulk") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Override public FilePolicyBulkRep getBulkResources(BulkIdParam param) { return (FilePolicyBulkRep) super.getBulkResources(param); } /** * @brief Create File Snapshot, Replication Policy * * @param param * FilePolicyCreateParam * @return FilePolicyCreateResp */ @POST @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public FilePolicyCreateResp createFilePolicy(FilePolicyCreateParam param) { FilePolicyCreateResp resp = new FilePolicyCreateResp(); // Make policy name as mandatory field ArgValidator.checkFieldNotNull(param.getPolicyName(), "policyName"); // Make apply at as mandatory field ArgValidator.checkFieldNotNull(param.getApplyAt(), "apply_at"); // Check for duplicate policy name if (param.getPolicyName() != null && !param.getPolicyName().isEmpty()) { checkForDuplicateName(param.getPolicyName(), FilePolicy.class); } // check policy type is valid or not ArgValidator.checkFieldValueFromEnum(param.getPolicyType(), "policy_type", EnumSet.allOf(FilePolicyType.class)); // check the policy apply level is valid or not ArgValidator.checkFieldValueFromEnum(param.getApplyAt(), "apply_at", EnumSet.allOf(FilePolicyApplyLevel.class)); _log.info("file policy creation started -- "); if (param.getPolicyType().equals(FilePolicyType.file_replication.name())) { return createFileReplicationPolicy(param); } else if (param.getPolicyType().equals(FilePolicyType.file_snapshot.name())) { return createFileSnapshotPolicy(param); } return resp; } /** * Gets the ids and self links of all file policies. * * @brief List file policies * @return A list of file policy reference specifying the ids and self links. */ @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }) public FilePolicyListRestRep getFilePolicies() { return getFilePoliciesForGivenUser(); } private boolean userHasTenantAdminRoles() { StorageOSUser user = getUserFromContext(); if (_permissionsHelper.userHasGivenRole(user, null, Role.TENANT_ADMIN)) { return true; } return false; } private boolean userHasSystemAdminRoles() { StorageOSUser user = getUserFromContext(); if (_permissionsHelper.userHasGivenRole(user, null, Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN)) { return true; } return false; } protected FilePolicyListRestRep getFilePoliciesForGivenUser() { FilePolicyListRestRep filePolicyList = new FilePolicyListRestRep(); List<URI> ids = _dbClient.queryByType(FilePolicy.class, true); List<FilePolicy> filePolicies = _dbClient.queryObject(FilePolicy.class, ids); StorageOSUser user = getUserFromContext(); // full list if role is {Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR} AND no tenant restriction from input // else only return the list, which input tenant has access. if (_permissionsHelper.userHasGivenRole(user, null, Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR)) { for (FilePolicy filePolicy : filePolicies) { filePolicyList.add(toNamedRelatedResource(filePolicy, filePolicy.getFilePolicyName())); } } else { // otherwise, filter by only authorized to use URI tenant = null; tenant = URI.create(user.getTenantId()); Set<FilePolicy> policySet = new HashSet<FilePolicy>(); for (FilePolicy filePolicy : filePolicies) { if (_permissionsHelper.tenantHasUsageACL(tenant, filePolicy)) { policySet.add(filePolicy); } } // Also adding vpools which sub-tenants of the user have access to. List<URI> subtenants = _permissionsHelper.getSubtenantsWithRoles(user); for (FilePolicy filePolicy : filePolicies) { if (_permissionsHelper.tenantHasUsageACL(subtenants, filePolicy)) { policySet.add(filePolicy); } } for (FilePolicy filePolicy : policySet) { filePolicyList.add(toNamedRelatedResource(filePolicy, filePolicy.getFilePolicyName())); } } return filePolicyList; } /** * @brief Get details of a file policy. * * @param id * of the file policy. * @return File policy information. */ @GET @Path("/{id}") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }) public FilePolicyRestRep getFilePolicy(@PathParam("id") URI id) { _log.info("Request recieved to get the file policy of id: {}", id); FilePolicy filepolicy = queryResource(id); ArgValidator.checkEntity(filepolicy, id, true); return map(filepolicy, _dbClient); } /** * @brief Delete file policy. * @param id * of the file policy. * @return */ @DELETE @Path("/{id}") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public Response deleteFilePolicy(@PathParam("id") URI id) { FilePolicy filepolicy = queryResource(id); ArgValidator.checkEntity(filepolicy, id, true); ArgValidator.checkReference(FilePolicy.class, filepolicy.getFilePolicyName(), checkForDelete(filepolicy)); StringSet assignedResources = filepolicy.getAssignedResources(); if (assignedResources != null && !assignedResources.isEmpty()) { _log.error("Delete file pocicy failed because the policy has associacted resources"); throw APIException.badRequests.failedToDeleteFilePolicy(filepolicy.getFilePolicyName(), "This policy has assigned resources."); } _dbClient.markForDeletion(filepolicy); auditOp(OperationTypeEnum.DELETE_FILE_POLICY, true, null, filepolicy.getId().toString(), filepolicy.getLabel()); return Response.ok().build(); } /** * @brief Assign File Policy to vpool, project, file system * * @param id * of the file policy. * @param param * FilePolicyAssignParam * @return FilePolicyAssignResp */ @POST @Path("/{id}/assign-policy") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.TENANT_ADMIN }) public TaskResourceRep assignFilePolicy(@PathParam("id") URI id, FilePolicyAssignParam param) { ArgValidator.checkFieldUriType(id, FilePolicy.class, "id"); FilePolicy filepolicy = this._dbClient.queryObject(FilePolicy.class, id); ArgValidator.checkEntity(filepolicy, id, true); // Verify user has permission to assign policy canUserAssignPolicyAtGivenLevel(filepolicy); String applyAt = filepolicy.getApplyAt(); FilePolicyApplyLevel appliedAt = FilePolicyApplyLevel.valueOf(applyAt); switch (appliedAt) { case vpool: return assignFilePolicyToVpools(param, filepolicy); case project: return assignFilePolicyToProjects(param, filepolicy); default: throw APIException.badRequests.invalidFilePolicyApplyLevel(appliedAt.name()); } } /** * @brief Unassign File Policy from vpool, project, file system. * @param id * of the file policy. * @param FilePolicyUnAssignParam * @return TaskResourceRep */ @POST @Path("/{id}/unassign-policy") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.TENANT_ADMIN }) public TaskResourceRep unassignFilePolicy(@PathParam("id") URI id, FilePolicyUnAssignParam param) { _log.info("Unassign File Policy :{} request received.", id); String task = UUID.randomUUID().toString(); ArgValidator.checkFieldUriType(id, FilePolicy.class, "id"); FilePolicy filepolicy = this._dbClient.queryObject(FilePolicy.class, id); ArgValidator.checkEntity(filepolicy, id, true); StringBuilder errorMsg = new StringBuilder(); Operation op = _dbClient.createTaskOpStatus(FilePolicy.class, filepolicy.getId(), task, ResourceOperationTypeEnum.UNASSIGN_FILE_POLICY); op.setDescription("unassign File Policy from resources "); // As the action done by tenant/system admin // Set corresponding tenant uri as task's tenant!!! Task taskObj = op.getTask(filepolicy.getId()); StorageOSUser user = getUserFromContext(); URI userTenantUri = URI.create(user.getTenantId()); FilePolicyServiceUtils.updateTaskTenant(_dbClient, filepolicy, "unassign", taskObj, userTenantUri); if (filepolicy.getAssignedResources() == null || filepolicy.getAssignedResources().isEmpty()) { _log.info("File Policy: " + id + " doesn't have any assigned resources."); _dbClient.ready(FilePolicy.class, filepolicy.getId(), task); return toTask(filepolicy, task, op); } ArgValidator.checkFieldNotNull(param.getUnassignfrom(), "unassign_from"); Set<URI> unassignFrom = param.getUnassignfrom(); if (unassignFrom != null) { for (URI uri : unassignFrom) { canUserUnAssignPolicyAtGivenLevel(filepolicy, uri); if (!filepolicy.getAssignedResources().contains(uri.toString())) { errorMsg.append("Provided resource URI is either being not assigned to the file policy:" + filepolicy.getId() + " or it is a invalid URI"); _log.error(errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyUnAssignParam(filepolicy.getFilePolicyName(), errorMsg.toString()); } } } FileOrchestrationController controller = getController(FileOrchestrationController.class, FileOrchestrationController.FILE_ORCHESTRATION_DEVICE); try { controller.unassignFilePolicy(filepolicy.getId(), unassignFrom, task); auditOp(OperationTypeEnum.UNASSIGN_FILE_POLICY, true, "BEGIN", filepolicy.getId().toString(), filepolicy.getLabel()); } catch (BadRequestException e) { op = _dbClient.error(FilePolicy.class, filepolicy.getId(), task, e); _log.error("Error Unassigning File policy {}, {}", e.getMessage(), e); throw e; } catch (Exception e) { _log.error("Error Unassigning Files Policy {}, {}", e.getMessage(), e); throw APIException.badRequests.unableToProcessRequest(e.getMessage()); } return toTask(filepolicy, task, op); } /** * Add or remove individual File Policy ACL entry(s). Request body must include at least one add or remove * operation. * * @param id * the URN of a ViPR File Policy * @param changes * ACL assignment changes * @brief Add or remove ACL entries from file store VirtualPool * @return No data returned in response body */ @PUT @Path("/{id}/acl") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SECURITY_ADMIN, Role.SYSTEM_ADMIN, Role.RESTRICTED_SECURITY_ADMIN }, blockProxies = true) public ACLAssignments updateAcls(@PathParam("id") URI id, ACLAssignmentChanges changes) { FilePolicy policy = queryResource(id); ArgValidator.checkEntityNotNull(policy, id, isIdEmbeddedInURL(id)); _permissionsHelper.updateACLs(policy, changes, new PermissionsHelper.UsageACLFilter(_permissionsHelper)); _dbClient.updateObject(policy); return getAclsOnPolicy(id); } /** * Get File Policy ACLs * * @param id * the URI of a ViPR FilePolicy * @brief Show ACL entries for File policy * @return ACL Assignment details */ @GET @Path("/{id}/acl") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SECURITY_ADMIN, Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }) public ACLAssignments getAcls(@PathParam("id") URI id) { return getAclsOnPolicy(id); } protected ACLAssignments getAclsOnPolicy(URI id) { FilePolicy policy = queryResource(id); ArgValidator.checkEntityNotNull(policy, id, isIdEmbeddedInURL(id)); ACLAssignments response = new ACLAssignments(); response.setAssignments(_permissionsHelper.convertToACLEntries(policy.getAcls())); return response; } /** * @brief Update the file policy * * @param id * the URI of a ViPR FilePolicy * @param param * FilePolicyUpdateParam * @return FilePolicyCreateResp */ @PUT @Path("/{id}") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }) public TaskResourceRep updateFilePolicy(@PathParam("id") URI id, FilePolicyUpdateParam param) { ArgValidator.checkFieldUriType(id, FilePolicy.class, "id"); FilePolicy filePolicy = this._dbClient.queryObject(FilePolicy.class, id); ArgValidator.checkEntity(filePolicy, id, true); _log.info("validate and update file policy parameters started -- "); if (filePolicy.getFilePolicyType().equals(FilePolicyType.file_replication.name())) { updateFileReplicationPolicy(filePolicy, param); } else if (filePolicy.getFilePolicyType().equals(FilePolicyType.file_snapshot.name())) { updateFileSnapshotPolicy(filePolicy, param); } // if No storage resource, update the original policy template!! _dbClient.updateObject(filePolicy); String task = UUID.randomUUID().toString(); Operation op = _dbClient.createTaskOpStatus(FilePolicy.class, filePolicy.getId(), task, ResourceOperationTypeEnum.UPDATE_FILE_POLICY_BY_POLICY_STORAGE_RESOURCE); op.setDescription("update file protection policy by policy storage resource"); // As the action done by system admin // Set system uri as task's tenant!!! Task taskObj = op.getTask(filePolicy.getId()); StorageOSUser user = getUserFromContext(); URI userTenantUri = URI.create(user.getTenantId()); FilePolicyServiceUtils.updateTaskTenant(_dbClient, filePolicy, "update", taskObj, userTenantUri); if (filePolicy.getPolicyStorageResources() != null && !filePolicy.getPolicyStorageResources().isEmpty()) { _log.info("Updating the storage system policy started.."); updateStorageSystemFileProtectionPolicy(filePolicy, param, task); return toTask(filePolicy, task, op); } else { op = _dbClient.ready(FilePolicy.class, filePolicy.getId(), task); return toTask(filePolicy, task, op); } } /** * @brief Get the list of policy storage resources of a file policy. * * @param id of the file policy. * @return List of policy storage resource information. */ @GET @Path("/{id}/policy-storage-resources") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }) public FilePolicyStorageResources getFilePolicyStorageResources(@PathParam("id") URI id) { _log.info("Request recieved to list storage resources for the policy {}", id); FilePolicy filepolicy = queryResource(id); ArgValidator.checkEntity(filepolicy, id, true); FilePolicyStorageResources resources = new FilePolicyStorageResources(); List<FilePolicyStorageResourceRestRep> policyResources = new ArrayList<FilePolicyStorageResourceRestRep>(); for (PolicyStorageResource storageRes : FileOrchestrationUtils.getFilePolicyStorageResources(_dbClient, filepolicy)) { policyResources.add(FilePolicyMapper.mapPolicyStorageResource(storageRes, filepolicy, _dbClient)); } resources.setStorageResources(policyResources); return resources; } /** * Validate and create replication policy. * * @param param * @return */ private FilePolicyCreateResp createFileReplicationPolicy(FilePolicyCreateParam param) { StringBuilder errorMsg = new StringBuilder(); FilePolicy fileReplicationPolicy = new FilePolicy(); if (param.getReplicationPolicyParams() == null) { errorMsg.append("Required parameter replication_params was missing or empty"); _log.error("Failed to create snapshot policy due to {} ", errorMsg.toString()); throw APIException.badRequests.invalidFileReplicationPolicyParam(param.getPolicyName(), errorMsg.toString()); } // Make sure replication type and copy mode are provided ArgValidator.checkFieldNotNull(param.getReplicationPolicyParams().getReplicationCopyMode(), "replication_copy_mode"); ArgValidator.checkFieldNotNull(param.getReplicationPolicyParams().getReplicationType(), "replication_type"); // Validate replication policy schedule parameters boolean isValidSchedule = FilePolicyServiceUtils.validateAndUpdatePolicyScheduleParam( param.getReplicationPolicyParams().getPolicySchedule(), fileReplicationPolicy, errorMsg); if (!isValidSchedule && errorMsg.length() > 0) { _log.error("Failed to create file replication policy due to {} ", errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyScheduleParam(param.getPolicyName(), errorMsg.toString()); } // validate replication type and copy mode parameters ArgValidator.checkFieldValueFromEnum(param.getReplicationPolicyParams().getReplicationType(), "replicationType", EnumSet.allOf(FilePolicy.FileReplicationType.class)); ArgValidator.checkFieldValueFromEnum(param.getReplicationPolicyParams().getReplicationCopyMode(), "replicationCopyMode", EnumSet.allOf(FilePolicy.FileReplicationCopyMode.class)); fileReplicationPolicy.setId(URIUtil.createId(FilePolicy.class)); fileReplicationPolicy.setFilePolicyName(param.getPolicyName()); fileReplicationPolicy.setLabel(param.getPolicyName()); fileReplicationPolicy.setFilePolicyType(param.getPolicyType()); if (param.getPriority() != null) { ArgValidator.checkFieldValueFromEnum(param.getPriority(), "priority", EnumSet.allOf(FilePolicyPriority.class)); fileReplicationPolicy.setPriority(param.getPriority()); } fileReplicationPolicy.setNumWorkerThreads((long) param.getNumWorkerThreads()); if (param.getPolicyDescription() != null && !param.getPolicyDescription().isEmpty()) { fileReplicationPolicy.setFilePolicyDescription(param.getPolicyDescription()); } fileReplicationPolicy.setScheduleFrequency(param.getReplicationPolicyParams().getPolicySchedule().getScheduleFrequency()); fileReplicationPolicy.setFileReplicationType(param.getReplicationPolicyParams().getReplicationType()); fileReplicationPolicy.setFileReplicationCopyMode(param.getReplicationPolicyParams().getReplicationCopyMode()); fileReplicationPolicy.setApplyAt(param.getApplyAt()); _dbClient.createObject(fileReplicationPolicy); _log.info("Policy {} created successfully", fileReplicationPolicy); return new FilePolicyCreateResp(fileReplicationPolicy.getId(), toLink(ResourceTypeEnum.FILE_POLICY, fileReplicationPolicy.getId()), fileReplicationPolicy.getLabel()); } /** * Validate and create snapshot policy. * * @param param * @return */ private FilePolicyCreateResp createFileSnapshotPolicy(FilePolicyCreateParam param) { StringBuilder errorMsg = new StringBuilder(); FilePolicy fileSnapshotPolicy = new FilePolicy(); if (param.getSnapshotPolicyPrams() == null) { errorMsg.append("Required parameter snapshot_params was missing or empty"); _log.error("Failed to create snapshot policy due to {} ", errorMsg.toString()); throw APIException.badRequests.invalidFileSnapshotPolicyParam(param.getPolicyName(), errorMsg.toString()); } // Validate snapshot policy schedule parameters boolean isValidSchedule = FilePolicyServiceUtils.validateAndUpdatePolicyScheduleParam( param.getSnapshotPolicyPrams().getPolicySchedule(), fileSnapshotPolicy, errorMsg); if (!isValidSchedule && errorMsg.length() > 0) { _log.error("Failed to create file snapshot policy due to {} ", errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyScheduleParam(param.getPolicyName(), errorMsg.toString()); } // Validate snapshot policy expire parameters.. FilePolicyServiceUtils.validateSnapshotPolicyExpireParam(param.getSnapshotPolicyPrams()); fileSnapshotPolicy.setId(URIUtil.createId(FilePolicy.class)); fileSnapshotPolicy.setLabel(param.getPolicyName()); fileSnapshotPolicy.setFilePolicyType(param.getPolicyType()); fileSnapshotPolicy.setFilePolicyName(param.getPolicyName()); fileSnapshotPolicy.setLabel(param.getPolicyName()); if (param.getPolicyDescription() != null && !param.getPolicyDescription().isEmpty()) { fileSnapshotPolicy.setFilePolicyDescription(param.getPolicyDescription()); } fileSnapshotPolicy.setScheduleFrequency(param.getSnapshotPolicyPrams().getPolicySchedule().getScheduleFrequency()); fileSnapshotPolicy.setSnapshotExpireType(param.getSnapshotPolicyPrams().getSnapshotExpireParams().getExpireType()); fileSnapshotPolicy.setSnapshotNamePattern(param.getSnapshotPolicyPrams().getSnapshotNamePattern()); fileSnapshotPolicy.setApplyAt(param.getApplyAt()); if (!param.getSnapshotPolicyPrams().getSnapshotExpireParams().getExpireType() .equalsIgnoreCase(SnapshotExpireType.NEVER.toString())) { fileSnapshotPolicy.setSnapshotExpireTime((long) param.getSnapshotPolicyPrams().getSnapshotExpireParams().getExpireValue()); } else { fileSnapshotPolicy.setSnapshotExpireTime(0L); } _dbClient.createObject(fileSnapshotPolicy); _log.info("Snapshot policy {} created successfully", fileSnapshotPolicy); return new FilePolicyCreateResp(fileSnapshotPolicy.getId(), toLink(ResourceTypeEnum.FILE_POLICY, fileSnapshotPolicy.getId()), fileSnapshotPolicy.getLabel()); } /** * Update the replication policy. * * @param fileReplicationPolicy * @param param * @return */ private void updateFileReplicationPolicy(FilePolicy fileReplicationPolicy, FilePolicyUpdateParam param) { StringBuilder errorMsg = new StringBuilder(); // validate and update common parameters!! updatePolicyCommonParameters(fileReplicationPolicy, param); if (param.getPriority() != null) { ArgValidator.checkFieldValueFromEnum(param.getPriority(), "priority", EnumSet.allOf(FilePolicyPriority.class)); fileReplicationPolicy.setPriority(param.getPriority()); } if (param.getNumWorkerThreads() > 0) { fileReplicationPolicy.setNumWorkerThreads((long) param.getNumWorkerThreads()); } // validate and update replication parameters!!! if (param.getReplicationPolicyParams() != null) { FileReplicationPolicyParam replParam = param.getReplicationPolicyParams(); if (replParam.getReplicationCopyMode() != null && !replParam.getReplicationCopyMode().isEmpty()) { ArgValidator.checkFieldValueFromEnum(replParam.getReplicationCopyMode(), "replicationCopyMode", EnumSet.allOf(FilePolicy.FileReplicationCopyMode.class)); fileReplicationPolicy.setFileReplicationCopyMode(replParam.getReplicationCopyMode()); } if (replParam.getReplicationType() != null && !replParam.getReplicationType().isEmpty()) { ArgValidator.checkFieldValueFromEnum(replParam.getReplicationType(), "replicationType", EnumSet.allOf(FilePolicy.FileReplicationType.class)); // Dont change the replication type, if policy was applied to storage resources!! if (!replParam.getReplicationType().equalsIgnoreCase(fileReplicationPolicy.getFileReplicationType())) { if (fileReplicationPolicy.getPolicyStorageResources() != null && !fileReplicationPolicy.getPolicyStorageResources().isEmpty()) { errorMsg.append("Active resources exist on the policy"); _log.error("Failed to update file replication policy due to {} ", errorMsg.toString()); throw APIException.badRequests.invalidFileReplicationPolicyParam(fileReplicationPolicy.getFilePolicyName(), errorMsg.toString()); } else { fileReplicationPolicy.setFileReplicationType(replParam.getReplicationType()); } } } // Validate replication policy schedule parameters if (replParam.getPolicySchedule() != null) { boolean isValidSchedule = FilePolicyServiceUtils.validateAndUpdatePolicyScheduleParam( replParam.getPolicySchedule(), fileReplicationPolicy, errorMsg); if (!isValidSchedule && errorMsg.length() > 0) { _log.error("Failed to update file replication policy due to {} ", errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyScheduleParam(fileReplicationPolicy.getFilePolicyName(), errorMsg.toString()); } if (replParam.getPolicySchedule().getScheduleFrequency() != null && !replParam.getPolicySchedule().getScheduleFrequency().isEmpty()) { fileReplicationPolicy .setScheduleFrequency(replParam.getPolicySchedule().getScheduleFrequency()); } } } } /** * Update snapshot policy. * * @param param * @param fileSnapshotPolicy * @return */ private void updateFileSnapshotPolicy(FilePolicy fileSnapshotPolicy, FilePolicyUpdateParam param) { StringBuilder errorMsg = new StringBuilder(); // validate and update common parameters!! updatePolicyCommonParameters(fileSnapshotPolicy, param); // Validate snapshot policy expire parameters.. if (param.getSnapshotPolicyPrams() != null) { // Validate snapshot policy schedule parameters if (param.getSnapshotPolicyPrams().getPolicySchedule() != null) { boolean isValidSchedule = FilePolicyServiceUtils.validateAndUpdatePolicyScheduleParam( param.getSnapshotPolicyPrams().getPolicySchedule(), fileSnapshotPolicy, errorMsg); if (!isValidSchedule) { _log.error("Failed to update file snapshot policy due to {} ", errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyScheduleParam(fileSnapshotPolicy.getFilePolicyName(), errorMsg.toString()); } if (param.getSnapshotPolicyPrams().getPolicySchedule().getScheduleFrequency() != null && !param.getSnapshotPolicyPrams().getPolicySchedule().getScheduleFrequency().isEmpty()) { fileSnapshotPolicy.setScheduleFrequency(param.getSnapshotPolicyPrams().getPolicySchedule().getScheduleFrequency()); } } if (param.getSnapshotPolicyPrams().getSnapshotExpireParams() != null) { // Validate the snapshot expire parameters!! FileSnapshotPolicyExpireParam snapExpireParam = param.getSnapshotPolicyPrams().getSnapshotExpireParams(); FilePolicyServiceUtils.validateSnapshotPolicyExpireParam(param.getSnapshotPolicyPrams()); if (snapExpireParam.getExpireType() != null) { fileSnapshotPolicy.setSnapshotExpireType(snapExpireParam.getExpireType()); if (!SnapshotExpireType.NEVER.toString().equalsIgnoreCase(snapExpireParam.getExpireType())) { fileSnapshotPolicy.setSnapshotExpireTime((long) snapExpireParam.getExpireValue()); } else { fileSnapshotPolicy.setSnapshotExpireTime(0L); } } } if (param.getSnapshotPolicyPrams().getSnapshotNamePattern() != null) { fileSnapshotPolicy.setSnapshotNamePattern(param.getSnapshotPolicyPrams().getSnapshotNamePattern()); } } } private void updateStorageSystemFileProtectionPolicy(FilePolicy policy, FilePolicyUpdateParam param, String task) { try { FileServiceApi fileServiceApi = getDefaultFileServiceApi(); fileServiceApi.updateFileProtectionPolicy(policy.getId(), param, task); _log.info("Updated file protection policy {}", policy.getFilePolicyName()); } catch (BadRequestException e) { Operation op = _dbClient.error(FilePolicy.class, policy.getId(), task, e); _log.error("Error updating file policy on backend storage.", e); throw e; } catch (Exception e) { _log.error("Error updating file policy on backend storage.", e); throw APIException.badRequests.unableToProcessRequest(e.getMessage()); } } private boolean updatePolicyCommonParameters(FilePolicy existingPolicy, FilePolicyUpdateParam param) { // Verify and updated the policy name!!! if (param.getPolicyName() != null && !param.getPolicyName().isEmpty() && !existingPolicy.getLabel().equalsIgnoreCase(param.getPolicyName())) { checkForDuplicateName(param.getPolicyName(), FilePolicy.class); existingPolicy.setLabel(param.getPolicyName()); existingPolicy.setFilePolicyName(param.getPolicyName()); existingPolicy.setLabel(param.getPolicyName()); } if (param.getPolicyDescription() != null && !param.getPolicyDescription().isEmpty()) { existingPolicy.setFilePolicyDescription(param.getPolicyDescription()); } if (param.getApplyAt() != null && !param.getApplyAt().isEmpty() && !param.getApplyAt().equalsIgnoreCase(existingPolicy.getApplyAt())) { if (existingPolicy.getAssignedResources() != null && !existingPolicy.getAssignedResources().isEmpty()) { String errorMsg = "Policy has active resources, can not change applied at to " + param.getApplyAt(); _log.error(errorMsg); throw APIException.badRequests.unableToProcessRequest(errorMsg); } existingPolicy.setApplyAt(param.getApplyAt()); } return true; } private List<FileReplicationTopology> queryDBReplicationTopologies(FilePolicy policy) { _log.info("Querying all DB replication topologies Using policy Id {}", policy.getId()); try { ContainmentConstraint containmentConstraint = ContainmentConstraint.Factory .getFileReplicationPolicyTopologyConstraint(policy.getId()); List<FileReplicationTopology> topologies = CustomQueryUtility.queryActiveResourcesByConstraint(_dbClient, FileReplicationTopology.class, containmentConstraint); return topologies; } catch (Exception e) { _log.error("Error while querying {}", e); } return null; } private void updateFileReplicationTopologyInfo(FilePolicyAssignParam param, FilePolicy filepolicy) { if (FilePolicyType.file_replication.name().equalsIgnoreCase(filepolicy.getFilePolicyType()) && filepolicy.getFileReplicationType().equalsIgnoreCase(FileReplicationType.REMOTE.name())) { if (param.getFileReplicationtopologies() != null && !param.getFileReplicationtopologies().isEmpty()) { List<FileReplicationTopology> dbTopologies = queryDBReplicationTopologies(filepolicy); for (FileReplicationTopologyParam topologyParam : param.getFileReplicationtopologies()) { // Get existing topologies for given policy Boolean foundExistingTopology = false; if (dbTopologies != null && !dbTopologies.isEmpty()) { for (FileReplicationTopology topology : dbTopologies) { if (topology.getSourceVArray() != null && topology.getSourceVArray().toString().equalsIgnoreCase(topologyParam.getSourceVArray().toString())) { _log.info("Updating the existing topology..."); if (topologyParam.getTargetVArrays() != null && !topologyParam.getTargetVArrays().isEmpty()) { StringSet requestTargetVarraySet = new StringSet(); for (Iterator<URI> iterator = topologyParam.getTargetVArrays().iterator(); iterator.hasNext();) { URI targetVArray = iterator.next(); requestTargetVarraySet.add(targetVArray.toString()); } // Thow an error if admin want to change the topology for policy with resources!! if (filepolicy.getPolicyStorageResources() != null && !filepolicy.getPolicyStorageResources().isEmpty()) { if (topology.getTargetVArrays() != null && !topology.getTargetVArrays().containsAll(requestTargetVarraySet)) { StringBuffer errorMsg = new StringBuffer(); errorMsg.append("Topology can not be changed for policy {} with existing resources " + filepolicy.getFilePolicyName()); _log.error(errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyAssignParam(filepolicy.getFilePolicyName(), errorMsg.toString()); } } topology.setTargetVArrays(requestTargetVarraySet); _dbClient.updateObject(topology); } if (filepolicy.getReplicationTopologies() == null || !filepolicy.getReplicationTopologies().contains(topology.getId().toString())) { filepolicy.addReplicationTopology(topology.getId().toString()); _dbClient.updateObject(filepolicy); } foundExistingTopology = true; break; } } } if (!foundExistingTopology) { // Create DB entry for Replication topology FileReplicationTopology dbReplTopology = new FileReplicationTopology(); dbReplTopology.setId(URIUtil.createId(FileReplicationTopology.class)); dbReplTopology.setPolicy(filepolicy.getId()); dbReplTopology.setSourceVArray(topologyParam.getSourceVArray()); StringSet targetArrays = new StringSet(); if (topologyParam.getTargetVArrays() != null && !topologyParam.getTargetVArrays().isEmpty()) { for (URI uriTargetArray : topologyParam.getTargetVArrays()) { targetArrays.add(uriTargetArray.toString()); } dbReplTopology.setTargetVArrays(targetArrays); } _dbClient.createObject(dbReplTopology); if (filepolicy.getReplicationTopologies() == null || !filepolicy.getReplicationTopologies().contains(dbReplTopology.getId().toString())) { filepolicy.addReplicationTopology(dbReplTopology.getId().toString()); _dbClient.updateObject(filepolicy); } } } } } } /** * Assigning policy at vpool level * * @param param * @param filepolicy */ private TaskResourceRep assignFilePolicyToVpools(FilePolicyAssignParam param, FilePolicy filePolicy) { StringBuilder errorMsg = new StringBuilder(); StringBuilder recommendationErrorMsg = new StringBuilder(); ArgValidator.checkFieldNotNull(param.getVpoolAssignParams(), "vpool_assign_param"); // Policy has to be applied on specified file vpools.. ArgValidator.checkFieldNotEmpty(param.getVpoolAssignParams().getAssigntoVpools(), "assign_to_vpools"); Set<URI> vpoolURIs = param.getVpoolAssignParams().getAssigntoVpools(); Map<URI, List<URI>> vpoolToStorageSystemMap = new HashMap<URI, List<URI>>(); List<URI> filteredVpoolURIs = new ArrayList<URI>(); StringBuffer vPoolWithNoStoragePools = new StringBuffer(); for (URI vpoolURI : vpoolURIs) { ArgValidator.checkFieldUriType(vpoolURI, VirtualPool.class, "vpool"); VirtualPool virtualPool = _permissionsHelper.getObjectById(vpoolURI, VirtualPool.class); ArgValidator.checkEntity(virtualPool, vpoolURI, false); if (filePolicy.getAssignedResources() != null && filePolicy.getAssignedResources().contains(virtualPool.getId().toString())) { _log.info("File policy: {} has already been assigned to vpool: {} ", filePolicy.getFilePolicyName(), virtualPool.getLabel()); continue; } // Verify the vpool has any replication policy!!! // only single replication policy per vpool. if (filePolicy.getFilePolicyType().equalsIgnoreCase(FilePolicyType.file_replication.name()) && FilePolicyServiceUtils.vPoolHasReplicationPolicy(_dbClient, vpoolURI)) { errorMsg.append("Provided vpool : " + virtualPool.getLabel() + " already assigned with replication policy."); _log.error(errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyAssignParam(filePolicy.getFilePolicyName(), errorMsg.toString()); } if (filePolicy.getFilePolicyType().equalsIgnoreCase(FilePolicyType.file_snapshot.name()) && FilePolicyServiceUtils.vPoolHasSnapshotPolicyWithSameSchedule(_dbClient, vpoolURI, filePolicy)) { errorMsg.append("Snapshot policy with similar schedule is already present on vpool " + virtualPool.getLabel()); _log.error(errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyAssignParam(filePolicy.getFilePolicyName(), errorMsg.toString()); } FilePolicyServiceUtils.validateVpoolSupportPolicyType(filePolicy, virtualPool); List<URI> storageSystems = getAssociatedStorageSystemsByVPool(virtualPool); if (storageSystems != null && !storageSystems.isEmpty()) { vpoolToStorageSystemMap.put(vpoolURI, storageSystems); filteredVpoolURIs.add(vpoolURI); } else { vPoolWithNoStoragePools.append(virtualPool.getLabel()).append(","); } } if (filteredVpoolURIs.isEmpty()) { String errorMessage = "No matching storage pools exists for given vpools "; _log.error(errorMessage); throw APIException.badRequests.noStoragePoolsExists(vPoolWithNoStoragePools.toString()); } if (param.getApplyOnTargetSite() != null) { filePolicy.setApplyOnTargetSite(param.getApplyOnTargetSite()); } FileServiceApi fileServiceApi = getDefaultFileServiceApi(); FilePolicyType policyType = FilePolicyType.valueOf(filePolicy.getFilePolicyType()); String task = UUID.randomUUID().toString(); TaskResourceRep taskResponse = null; switch (policyType) { case file_snapshot: taskResponse = createAssignFilePolicyTask(filePolicy, task); AssignFileSnapshotPolicyToVpoolSchedulingThread.executeApiTask(this, _asyncTaskService.getExecutorService(), _dbClient, filePolicy.getId(), vpoolToStorageSystemMap, fileServiceApi, taskResponse, task); break; case file_replication: // update replication topology info updateFileReplicationTopologyInfo(param, filePolicy); List<URI> validRecommendationVpools = new ArrayList<URI>(); List<FileStorageSystemAssociation> associations = new ArrayList<FileStorageSystemAssociation>(); for (URI vpoolURI : filteredVpoolURIs) { VirtualPool vpool = _permissionsHelper.getObjectById(vpoolURI, VirtualPool.class); StringSet sourceVArraysSet = getSourceVArraySet(vpool, filePolicy); VirtualPoolCapabilityValuesWrapper capabilities = new VirtualPoolCapabilityValuesWrapper(); updatePolicyCapabilities(_dbClient, sourceVArraysSet, vpool, filePolicy, capabilities, errorMsg); // Replication policy has to be created on each applicable source storage system!! List<URI> storageSystems = getAssociatedStorageSystemsByVPool(vpool); if (storageSystems != null && !storageSystems.isEmpty()) { List<FileStorageSystemAssociation> vpoolAssociations = new ArrayList<FileStorageSystemAssociation>(); for (URI storageSystem : storageSystems) { capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_PROTECTION_SOURCE_STORAGE_SYSTEM, storageSystem); for (Iterator<String> iterator = sourceVArraysSet.iterator(); iterator.hasNext();) { String vArrayURI = iterator.next(); VirtualArray srcVarray = _dbClient.queryObject(VirtualArray.class, URI.create(vArrayURI)); try { List<FileRecommendation> newRecs = _filePlacementManager.getRecommendationsForFileCreateRequest( srcVarray, null, vpool, capabilities); if (newRecs != null && !newRecs.isEmpty()) { vpoolAssociations.addAll(convertRecommendationsToStorageSystemAssociations(newRecs, filePolicy.getApplyAt(), vpool.getId(), null)); } } catch (Exception ex) { _log.error("No recommendations found for storage system {} and virtualArray {} with error {} ", storageSystem, srcVarray.getLabel(), ex.getMessage()); if (ex.getMessage() != null) { recommendationErrorMsg.append(ex.getMessage()); } // Continue to get the recommendations for next storage system!! continue; } } } if (!vpoolAssociations.isEmpty()) { validRecommendationVpools.add(vpoolURI); associations.addAll(vpoolAssociations); } } else { String errorMessage = "No matching storage pools exists for vpool " + vpool.getLabel(); _log.error(errorMessage); recommendationErrorMsg.append(errorMessage); } } // If there is no recommendations found to assign replication policy // Throw an exception!! if (associations == null || associations.isEmpty()) { // If no other resources are assigned to replication policy // Remove the replication topology from the policy FileOrchestrationUtils.removeTopologyInfo(filePolicy, _dbClient); _log.error("No matching storage pools recommendations found for policy {} with due to {}", filePolicy.getFilePolicyName(), recommendationErrorMsg.toString()); throw APIException.badRequests.noFileStorageRecommendationsFound(filePolicy.getFilePolicyName()); } taskResponse = createAssignFilePolicyTask(filePolicy, task); fileServiceApi.assignFileReplicationPolicyToVirtualPools(associations, validRecommendationVpools, filePolicy.getId(), task); break; default: break; } auditOp(OperationTypeEnum.ASSIGN_FILE_POLICY, true, AuditLogManager.AUDITOP_BEGIN, filePolicy.getLabel()); if (taskResponse != null) { // As the action done by system admin // Set system uri as task's tenant!!! Task taskObj = _dbClient.queryObject(Task.class, taskResponse.getId()); StorageOSUser user = getUserFromContext(); URI userTenantUri = URI.create(user.getTenantId()); FilePolicyServiceUtils.updateTaskTenant(_dbClient, filePolicy, "assign", taskObj, userTenantUri); } return taskResponse; } private static boolean updatePolicyCapabilities(DbClient dbClient, StringSet sourceVArraySet, VirtualPool vPool, FilePolicy policy, VirtualPoolCapabilityValuesWrapper capabilities, StringBuilder errorMsg) { // Update replication policy capabilities!! capabilities.put(VirtualPoolCapabilityValuesWrapper.VPOOL_PROJECT_POLICY_ASSIGN, true); capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_TYPE, policy.getFileReplicationType()); capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_COPY_MODE, policy.getFileReplicationCopyMode()); if (vPool.getFrRpoType() != null) { // rpo type can be DAYS or HOURS capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_RPO_TYPE, vPool.getFrRpoType()); } if (vPool.getFrRpoValue() != null) { capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_RPO_VALUE, vPool.getFrRpoValue()); } capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_APPLIED_AT, policy.getApplyAt()); // Update target varrays for file placement!! if (policy.getFileReplicationType() != null && policy.getFileReplicationType().equalsIgnoreCase(FileReplicationType.REMOTE.name())) { if (policy.getReplicationTopologies() != null && !policy.getReplicationTopologies().isEmpty()) { Set<String> targetVArrays = new HashSet<String>(); for (String strTopology : policy.getReplicationTopologies()) { FileReplicationTopology dbTopology = dbClient.queryObject(FileReplicationTopology.class, URI.create(strTopology)); if (sourceVArraySet.contains(dbTopology.getSourceVArray().toString())) { targetVArrays.addAll(dbTopology.getTargetVArrays()); break; } } if (targetVArrays.isEmpty()) { errorMsg.append("Target Varrays are not defined in replication topology for source varrays " + sourceVArraySet + ". "); return false; } capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_TARGET_VARRAYS, targetVArrays); capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_REPLICATION_TARGET_VPOOL, vPool.getId()); } else { errorMsg.append("Replication Topology is not defined for policy " + policy.getFilePolicyName() + ". "); return false; } } return true; } private List<FileStorageSystemAssociation> convertRecommendationsToStorageSystemAssociations(List<FileRecommendation> recs, String appliedAt, URI vPoolURI, URI projectURI) { List<FileStorageSystemAssociation> associations = new ArrayList<FileStorageSystemAssociation>(); for (FileRecommendation rec : recs) { FileMirrorRecommendation mirrorRec = (FileMirrorRecommendation) rec; FileStorageSystemAssociation association = new FileStorageSystemAssociation(); association.setSourceSystem(mirrorRec.getSourceStorageSystem()); association.setSourceVNAS(mirrorRec.getvNAS()); if (appliedAt.equalsIgnoreCase(FilePolicyApplyLevel.vpool.name())) { association.setAppliedAtResource(vPoolURI); } else if (appliedAt.equalsIgnoreCase(FilePolicyApplyLevel.project.name())) { association.setProjectvPool(vPoolURI); association.setAppliedAtResource(projectURI); } Map<URI, Target> virtualArrayTargetMap = mirrorRec.getVirtualArrayTargetMap(); // Getting the first target because we support one-to-one replication now. URI targetVArray = virtualArrayTargetMap.entrySet().iterator().next().getKey(); Target target = virtualArrayTargetMap.entrySet().iterator().next().getValue(); URI targetStorageDevice = target.getTargetStorageDevice(); URI targetVNasURI = target.getTargetvNASURI(); TargetAssociation targetAssociation = new TargetAssociation(); targetAssociation.setStorageSystemURI(targetStorageDevice); targetAssociation.setvArrayURI(targetVArray); targetAssociation.setvNASURI(targetVNasURI); association.addTargetAssociation(targetAssociation); associations.add(association); } return associations; } /** * Assign policy at project level * * @param param * @param filepolicy */ private TaskResourceRep assignFilePolicyToProjects(FilePolicyAssignParam param, FilePolicy filePolicy) { StringBuilder errorMsg = new StringBuilder(); StringBuilder recommendationErrorMsg = new StringBuilder(); ArgValidator.checkFieldNotNull(param.getProjectAssignParams(), "project_assign_param"); ArgValidator.checkFieldUriType(param.getProjectAssignParams().getVpool(), VirtualPool.class, "vpool"); URI vpoolURI = param.getProjectAssignParams().getVpool(); VirtualPool vpool = null; if (NullColumnValueGetter.isNullURI(filePolicy.getFilePolicyVpool())) { ArgValidator.checkFieldUriType(vpoolURI, VirtualPool.class, "vpool"); vpool = _permissionsHelper.getObjectById(vpoolURI, VirtualPool.class); ArgValidator.checkEntity(vpool, vpoolURI, false); // Check if the vpool supports provided policy type.. FilePolicyServiceUtils.validateVpoolSupportPolicyType(filePolicy, vpool); // Check if the vpool supports policy at project level.. if (!vpool.getAllowFilePolicyAtProjectLevel()) { errorMsg.append("Provided vpool :" + vpool.getLabel() + " doesn't support policy at project level"); _log.error(errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyAssignParam(filePolicy.getFilePolicyName(), errorMsg.toString()); } } else if (vpoolURI != null) { vpool = _dbClient.queryObject(VirtualPool.class, filePolicy.getFilePolicyVpool()); if (!vpoolURI.equals(filePolicy.getFilePolicyVpool())) { errorMsg.append("File policy: " + filePolicy.getFilePolicyName() + " is already assigned at project level under the vpool: " + vpool.getLabel()); _log.error(errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyAssignParam(filePolicy.getFilePolicyName(), errorMsg.toString()); } } ArgValidator.checkFieldNotEmpty(param.getProjectAssignParams().getAssigntoProjects(), "assign_to_projects"); Set<URI> projectURIs = param.getProjectAssignParams().getAssigntoProjects(); List<URI> filteredProjectURIs = new ArrayList<URI>(); for (URI projectURI : projectURIs) { ArgValidator.checkFieldUriType(projectURI, Project.class, "project"); Project project = _permissionsHelper.getObjectById(projectURI, Project.class); ArgValidator.checkEntity(project, projectURI, false); if (filePolicy.getAssignedResources() != null && filePolicy.getAssignedResources().contains(project.getId().toString())) { _log.info("Policy {} is already assigned to project {} ", filePolicy.getFilePolicyName(), project.getLabel()); continue; } // Verify the vpool - project has any replication policy!!! // only single replication policy per vpool-project combination. if (filePolicy.getFilePolicyType().equalsIgnoreCase(FilePolicyType.file_replication.name()) && FilePolicyServiceUtils.projectHasReplicationPolicy(_dbClient, projectURI, vpool.getId())) { errorMsg.append("Virtual pool " + filePolicy.getFilePolicyVpool().toString() + " project " + project.getLabel() + "pair is already assigned with replication policy."); _log.error(errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyAssignParam(filePolicy.getFilePolicyName(), errorMsg.toString()); } if (filePolicy.getFilePolicyType().equalsIgnoreCase(FilePolicyType.file_snapshot.name()) && FilePolicyServiceUtils.projectHasSnapshotPolicyWithSameSchedule(_dbClient, projectURI, vpool.getId(), filePolicy)) { errorMsg.append("Snapshot policy with similar schedule is already present on project " + project.getLabel()); _log.error(errorMsg.toString()); throw APIException.badRequests.invalidFilePolicyAssignParam(filePolicy.getFilePolicyName(), errorMsg.toString()); } filteredProjectURIs.add(projectURI); } if (param.getApplyOnTargetSite() != null) { filePolicy.setApplyOnTargetSite(param.getApplyOnTargetSite()); } // Verify the virtual pool has storage pools!!! List<URI> storageSystems = getAssociatedStorageSystemsByVPool(vpool); if (storageSystems == null || storageSystems.isEmpty()) { String errorMessage = "No matching storage pools exists for given vpools "; _log.error(errorMessage); throw APIException.badRequests.noStoragePoolsExists(vpool.getLabel()); } String task = UUID.randomUUID().toString(); TaskResourceRep taskResponse = createAssignFilePolicyTask(filePolicy, task); FileServiceApi fileServiceApi = getDefaultFileServiceApi(); FilePolicyType policyType = FilePolicyType.valueOf(filePolicy.getFilePolicyType()); switch (policyType) { case file_snapshot: Map<URI, List<URI>> vpoolToStorageSystemMap = new HashMap<URI, List<URI>>(); vpoolToStorageSystemMap.put(vpoolURI, getAssociatedStorageSystemsByVPool(vpool)); AssignFileSnapshotPolicyToProjectSchedulingThread.executeApiTask(this, _asyncTaskService.getExecutorService(), _dbClient, filePolicy.getId(), vpoolToStorageSystemMap, filteredProjectURIs, fileServiceApi, taskResponse, task); break; case file_replication: if (filteredProjectURIs.isEmpty()) { throw APIException.badRequests.invalidFilePolicyAssignParam(filePolicy.getFilePolicyName(), "No projects to assign to policy."); } // update replication topology info updateFileReplicationTopologyInfo(param, filePolicy); List<URI> validRecommendationProjects = new ArrayList<URI>(); List<FileStorageSystemAssociation> associations = new ArrayList<FileStorageSystemAssociation>(); VirtualPoolCapabilityValuesWrapper capabilities = new VirtualPoolCapabilityValuesWrapper(); StringSet sourceVArraysSet = getSourceVArraySet(vpool, filePolicy); updatePolicyCapabilities(_dbClient, sourceVArraysSet, vpool, filePolicy, capabilities, errorMsg); // Replication policy has to be created on each applicable source storage system!! if (storageSystems != null && !storageSystems.isEmpty()) { for (Iterator<String> iterator = sourceVArraysSet.iterator(); iterator.hasNext();) { String vArrayURI = iterator.next(); for (URI projectURI : filteredProjectURIs) { List<FileStorageSystemAssociation> projectAssociations = new ArrayList<FileStorageSystemAssociation>(); Project project = _dbClient.queryObject(Project.class, projectURI); VirtualArray srcVarray = _dbClient.queryObject(VirtualArray.class, URI.create(vArrayURI)); for (URI storageSystem : storageSystems) { capabilities.put(VirtualPoolCapabilityValuesWrapper.FILE_PROTECTION_SOURCE_STORAGE_SYSTEM, storageSystem); try { List<FileRecommendation> newRecs = _filePlacementManager.getRecommendationsForFileCreateRequest( srcVarray, project, vpool, capabilities); if (newRecs != null && !newRecs.isEmpty()) { projectAssociations .addAll(convertRecommendationsToStorageSystemAssociations(newRecs, filePolicy.getApplyAt(), vpool.getId(), projectURI)); } } catch (Exception ex) { _log.error("No recommendations found for storage system {} and virtualArray {} with error {} ", storageSystem, srcVarray.getLabel(), ex.getMessage()); if (ex.getMessage() != null) { recommendationErrorMsg.append(ex.getMessage()); } // Continue to get the recommedations for next storage system!! continue; } } if (!projectAssociations.isEmpty()) { associations.addAll(projectAssociations); validRecommendationProjects.add(projectURI); } } } } else { String errorMessage = "No matching storage pools exists for vpool " + vpool.getLabel(); _log.error(errorMessage); recommendationErrorMsg.append(errorMessage); } // If there is no recommendations found to assign replication policy // Throw an exception!! if (associations == null || associations.isEmpty()) { // If no other resources are assigned to replication policy // Remove the replication topology from the policy FileOrchestrationUtils.removeTopologyInfo(filePolicy, _dbClient); _log.error("No matching storage pools recommendations found for policy {} with due to {}", filePolicy.getFilePolicyName(), recommendationErrorMsg.toString()); throw APIException.badRequests.noFileStorageRecommendationsFound(filePolicy.getFilePolicyName()); } fileServiceApi.assignFileReplicationPolicyToProjects(associations, vpoolURI, validRecommendationProjects, filePolicy.getId(), task); break; default: break; } auditOp(OperationTypeEnum.ASSIGN_FILE_POLICY, true, AuditLogManager.AUDITOP_BEGIN, filePolicy.getLabel()); if (taskResponse != null) { // As the action done by system admin // Set system uri as task's tenant!!! Task taskObj = _dbClient.queryObject(Task.class, taskResponse.getId()); StorageOSUser user = getUserFromContext(); URI userTenantUri = URI.create(user.getTenantId()); FilePolicyServiceUtils.updateTaskTenant(_dbClient, filePolicy, "assign", taskObj, userTenantUri); } return taskResponse; } private void canUserUnAssignPolicyAtGivenLevel(FilePolicy policy, URI res) { FilePolicyApplyLevel applyLevel = FilePolicyApplyLevel.valueOf(policy.getApplyAt()); switch (applyLevel) { case vpool: if (!userHasSystemAdminRoles()) { _log.error("User does not sufficient roles to unassign policy at vpool"); throw APIException.forbidden.onlySystemAdminsCanAssignVpoolPolicies(policy.getFilePolicyName()); } else { _permissionsHelper.getObjectById(res, VirtualPool.class); } break; case project: if (!userHasTenantAdminRoles()) { _log.error("User does not sufficient roles to unassign policy at project"); throw APIException.forbidden.onlyTenantAdminsCanAssignProjectPolicies(policy.getFilePolicyName()); } else { _permissionsHelper.getObjectById(res, Project.class); } break; case file_system: if (!userHasTenantAdminRoles()) { _log.error("User does not sufficient roles to unassign policy at file system"); throw APIException.forbidden.onlyTenantAdminsCanAssignFileSystemPolicies(policy.getFilePolicyName()); } break; default: _log.error("Not a valid policy apply level: " + applyLevel); } } private boolean canUserAssignPolicyAtGivenLevel(FilePolicy policy) { // user should have system admin role to assign policy to vpool if (policy.getApplyAt() != null) { switch (policy.getApplyAt()) { case "vpool": if (!userHasSystemAdminRoles()) { _log.error("User does not sufficient roles to assign policy at vpool"); throw APIException.forbidden.onlySystemAdminsCanAssignVpoolPolicies(policy.getFilePolicyName()); } break; case "project": if (!userHasTenantAdminRoles()) { _log.error("User does not sufficient roles to assign policy at project"); throw APIException.forbidden.onlyTenantAdminsCanAssignProjectPolicies(policy.getFilePolicyName()); } // Verify the user has an access to given projects break; case "file_system": if (!userHasTenantAdminRoles()) { _log.error("User does not sufficient roles to assign policy at file system"); throw APIException.forbidden.onlyTenantAdminsCanAssignFileSystemPolicies(policy.getFilePolicyName()); } break; default: return false; } return true; } return false; } private boolean canAccessFilePolicy(FilePolicy filePolicy) { StringSet tenants = filePolicy.getTenantOrg(); if (tenants == null || tenants.isEmpty()) { return true; } if (isSystemAdmin()) { return true; } StorageOSUser user = getUserFromContext(); String userTenantId = user.getTenantId(); return tenants.contains(userTenantId); } private boolean doesResHasPolicyOfSameType(StringSet existingPolicies, FilePolicy filePolicy) { if (existingPolicies != null && !existingPolicies.isEmpty()) { List<URI> existingFilePolicyURIs = new ArrayList<URI>(); for (String existingPolicy : existingPolicies) { existingFilePolicyURIs.add(URI.create(existingPolicy)); } Iterator<FilePolicy> iterator = _dbClient.queryIterativeObjects(FilePolicy.class, existingFilePolicyURIs, true); if (filePolicy.getFilePolicyType().equals(FilePolicyType.file_replication.name())) { while (iterator.hasNext()) { FilePolicy fp = iterator.next(); if (FilePolicy.FilePolicyType.file_replication.name().equals(fp.getFilePolicyType())) { return true; } } } else if (filePolicy.getFilePolicyType().equals(FilePolicyType.file_snapshot.name())) { while (iterator.hasNext()) { FilePolicy fp = iterator.next(); if (FilePolicy.FilePolicyType.file_snapshot.name().equals(fp.getFilePolicyType())) { return true; } } } } return false; } private List<URI> getAssociatedStorageSystemsByVPool(VirtualPool vpool) { Set<URI> storageSystemURISet = new HashSet<URI>(); StringSet storagePoolURISet = null; if (vpool.getUseMatchedPools()) { storagePoolURISet = vpool.getMatchedStoragePools(); } else { storagePoolURISet = vpool.getAssignedStoragePools(); } if (storagePoolURISet != null && !storagePoolURISet.isEmpty()) { for (Iterator<String> iterator = storagePoolURISet.iterator(); iterator.hasNext();) { URI storagePoolURI = URI.create(iterator.next()); StoragePool spool = _dbClient.queryObject(StoragePool.class, storagePoolURI); if (spool != null && !spool.getInactive()) { storageSystemURISet.add(spool .getStorageDevice()); } } } return new ArrayList<URI>(storageSystemURISet); } private TaskResourceRep createAssignFilePolicyTask(FilePolicy filepolicy, String taskId) { filepolicy.setOpStatus(new OpStatusMap()); Operation op = new Operation(); op.setResourceType(ResourceOperationTypeEnum.ASSIGN_FILE_POLICY); filepolicy.getOpStatus().createTaskStatus(taskId, op); _dbClient.updateObject(filepolicy); return toTask(filepolicy, taskId); } private StringSet getSourceVArraySet(VirtualPool vpool, FilePolicy filePolicy) { StringSet vpoolVArraySet = vpool.getVirtualArrays(); if (filePolicy.getFileReplicationType() != null && filePolicy.getFileReplicationType().equalsIgnoreCase(FileReplicationType.REMOTE.name())) { Set<String> topologyVArraySet = new HashSet<String>(); List<FileReplicationTopology> dbTopologies = queryDBReplicationTopologies(filePolicy); if (dbTopologies != null && !dbTopologies.isEmpty()) { for (Iterator<FileReplicationTopology> iterator = dbTopologies.iterator(); iterator.hasNext();) { FileReplicationTopology fileReplicationTopology = iterator.next(); topologyVArraySet.add(fileReplicationTopology.getSourceVArray().toString()); } } vpoolVArraySet.retainAll(topologyVArraySet); } return vpoolVArraySet; } }