using Coscine.Api.Project.ParameterObjects; using Coscine.ApiCommons; using Coscine.Database.DataModel; using Coscine.Database.Models; using Coscine.Database.ReturnObjects; using Coscine.Database.Util; using Coscine.Logging; using Coscine.Metadata; using Coscine.ResourceTypes; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; namespace Coscine.Api.Project.Controllers { /// <summary> /// /// This controller represents the actions which can be taken with a project object. /// </summary> [Authorize] public class ProjectQuotaController : Controller { private readonly Authenticator _authenticator; private readonly ProjectModel _projectModel; private readonly ProjectRoleModel _projectRoleModel; private readonly ProjectQuotaModel _projectQuotaModel; private readonly ResourceTypeModel _resourceTypeModel; private readonly ResourceModel _resourceModel; private readonly CoscineLogger _coscineLogger; private readonly RdfStoreConnector _rdfStoreConnector; /// <summary> /// ProjectQuotaController constructor /// </summary> /// <param name="logger">Logger</param> public ProjectQuotaController(ILogger<ProjectQuotaController> logger) { _authenticator = new Authenticator(this, Program.Configuration); _projectModel = new ProjectModel(); _projectRoleModel = new ProjectRoleModel(); _resourceTypeModel = new ResourceTypeModel(); _resourceModel = new ResourceModel(); _projectQuotaModel = new ProjectQuotaModel(); _coscineLogger = new CoscineLogger(logger); _rdfStoreConnector = new RdfStoreConnector(Program.Configuration.GetString("coscine/local/virtuoso/additional/url")); } /// <summary> /// Retrieves all project quotas in GiB grouped by resource type for the selected project. /// </summary> /// <param name="id">Id of the project</param> /// <returns>List of project quotas per resource type</returns> [HttpGet("[controller]/{id}/-/all")] public ActionResult<IEnumerable<ProjectQuotaReturnObject>> Quotas(Guid id) { var user = _authenticator.GetUser(); var project = _projectModel.GetById(id); if (project == null) { return base.NotFound($"Could not find project with id: {id}"); } // Rights Matrix (https://git.rwth-aachen.de/coscine/docs/private/internal-wiki/-/blob/master/coscine/Definition%20of%20rights%20Matrix.md) // - Project: Create Subprojects | Project: View Settings (project, user, quota) ---> conflicts if (!_projectModel.HasAccess(user, project, UserRoles.Owner, UserRoles.Member)) { return Unauthorized("The user is not authorized to perform a get on the selected project!"); } var enabledResources = ResourceTypeFactory.Instance.GetResourceTypes(); var resourceTypes = _resourceTypeModel.GetAllWhere(r => enabledResources.Any(e => r.SpecificType.Equals(e))); return Ok(resourceTypes.Select(x => Helpers.CreateProjectQuotaReturnObject(x, id))); } /// <summary> /// Retrieves the project quota in GiB of a resource type for the selected project together with all individual resources of this resource type. /// </summary> /// <param name="id">Id of the project</param> /// <param name="resourceTypeId">Id of the resource type</param> /// <returns>The project quota for the resource type together with all individual resources of this resource type.</returns> [HttpGet("[controller]/{id}/{resourceTypeId}")] public ActionResult<ProjectQuotaExtendedReturnObject> Quota(Guid id, Guid resourceTypeId) { var user = _authenticator.GetUser(); var project = _projectModel.GetById(id); if (project == null) { return NotFound($"Could not find project with id: {id}"); } // Rights Matrix (https://git.rwth-aachen.de/coscine/docs/private/internal-wiki/-/blob/master/coscine/Definition%20of%20rights%20Matrix.md) // - Project: View Settings (project, user, quota) if (!_projectModel.HasAccess(user, project, UserRoles.Owner)) { return Unauthorized("The user is not authorized to perform a get on the selected project!"); } var resourceType = _resourceTypeModel.GetById(resourceTypeId); var enabledResources = ResourceTypeFactory.Instance.GetResourceTypes(); if (!enabledResources.Contains(resourceType.SpecificType)) { return base.NotFound($"Could not find resourceType with id: {resourceTypeId}"); } return Ok(Helpers.CreateProjectQuotaExtendedReturnObject(resourceType, id)); } /// <summary> /// Updates the reserved project quota of a resource type for a selected poject. Quota value in GiB. /// </summary> /// <param name="id">Id of the project</param> /// <param name="resourceTypeId">Id of the resource type</param> /// <param name="updateProjectQuotaObject">Object containing the update values.</param> /// <returns>NoContent (204).</returns> [HttpPost("[controller]/{id}/{resourceTypeId}")] public IActionResult UpdateQuota(Guid id, Guid resourceTypeId, [FromBody] UpdateProjectQuotaObject updateProjectQuotaObject) { var user = _authenticator.GetUser(); var project = _projectModel.GetById(id); if (project == null) { return NotFound($"Could not find project with id: {id}"); } // Rights Matrix (https://git.rwth-aachen.de/coscine/docs/private/internal-wiki/-/blob/master/coscine/Definition%20of%20rights%20Matrix.md) // - Project: Change Settings (project, user, quota) if (!_projectModel.HasAccess(user, project, UserRoles.Owner)) { return Unauthorized("The user is not authorized to perform a get on the selected project!"); } var resourceType = _resourceTypeModel.GetById(resourceTypeId); var enabledResources = ResourceTypeFactory.Instance.GetResourceTypes(); if (!enabledResources.Contains(resourceType.SpecificType)) { return NotFound($"Could not find resourceType with id: {resourceTypeId}"); } var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resourceType.Type, resourceType.SpecificType); if (resourceTypeDefinition == null) { return BadRequest($"No provider for: \"{resourceType.DisplayName}\"."); } if (!resourceTypeDefinition.GetResourceTypeInformation().Result.IsQuotaAdjustable) { return BadRequest($"Cannot adjust quota for {resourceType.DisplayName}."); } if (updateProjectQuotaObject.AllocatedGiB < 0) { return BadRequest($"Allocated {updateProjectQuotaObject.AllocatedGiB}. Cannot be less than 0 GB."); } var projectQuotaForCurrent = _projectQuotaModel.GetWhere(x => x.ProjectId == id && x.ResourceTypeId == resourceTypeId); var totalReserved = Helpers.CalculateTotalReservedQuota(resourceType, id); if (Helpers.ConvertCapacityUnits(totalReserved, QuotaUnit.GibiBYTE) > updateProjectQuotaObject.AllocatedGiB) { return BadRequest($"Cannot set quota ({updateProjectQuotaObject.AllocatedGiB} GB) below the total ({Helpers.ConvertCapacityUnits(totalReserved, QuotaUnit.GibiBYTE)} GB) that are currently reserved."); } if (updateProjectQuotaObject.AllocatedGiB > projectQuotaForCurrent.MaxQuota) { return BadRequest($"Cannot set quota to {updateProjectQuotaObject.AllocatedGiB} GB. It would exceed the maximum of {projectQuotaForCurrent.MaxQuota} GB for {resourceType.SpecificType} resources."); } var defaultQuotas = _rdfStoreConnector.GetQuotaDefault(user.Id.ToString()); var defaultQuota = defaultQuotas.FirstOrDefault(q => q.ResourceType == resourceType.DisplayName); if (projectQuotaForCurrent == null) { var projectQuota = new ProjectQuota { MaxQuota = (defaultQuota?.DefaultMaxQuota) ?? 0, Quota = (defaultQuota?.DefaultQuota) ?? 0, ProjectId = project.Id, ResourceTypeId = resourceType.Id }; _projectQuotaModel.Insert(projectQuota); } else { projectQuotaForCurrent.Quota = updateProjectQuotaObject.AllocatedGiB; _projectQuotaModel.Update(projectQuotaForCurrent); } if (Request.Query != null && Request.Query["noanalyticslog"] != "true") { LogAnalyticsOwnerProjectQuotaChange(project, user); } return NoContent(); } private void LogAnalyticsOwnerProjectQuotaChange(Database.DataModel.Project project, User user) { var enabledResources = ResourceTypeFactory.Instance.GetResourceTypes(); var resourceTypes = _resourceTypeModel.GetAllWhere(r => enabledResources.Any(e => r.SpecificType.Equals(e))); var objects = resourceTypes.Select(x => Helpers.CreateProjectQuotaReturnObject(x, project.Id)); _coscineLogger.AnalyticsLog( new AnalyticsLogObject { Type = "Action", Operation = "Owner Project Quota Change", RoleId = _projectRoleModel.GetGetUserRoleForProject(project.Id, user.Id).ToString(), ProjectId = project.Id.ToString(), QuotaSize = objects.Select(x => $"{x.Name}: {x.Allocated.Value}/{x.Maximum.Value}").ToList() }); } } }