Skip to content
Snippets Groups Projects

Release: Sprint/2022 14 :robot:

Merged Petar Hristov requested to merge dev into master
6 files
+ 198
183
Compare changes
  • Side-by-side
  • Inline
Files
6
@@ -5,6 +5,7 @@ using Coscine.Api.Admin.ReturnObjects;
using Coscine.ApiCommons;
using Coscine.Database.DataModel;
using Coscine.Database.Models;
using Coscine.Database.ReturnObjects;
using Coscine.Logging;
using Coscine.Metadata;
using Coscine.ResourceTypes;
@@ -35,7 +36,6 @@ namespace Coscine.Api.Admin.Controllers
private readonly ProjectQuotaModel _projectQuotaModel;
private readonly ResourceTypeModel _resourceTypeModel;
private readonly Emitter _emitter;
private readonly float _oneGb = 1024 * 1024 * 1024;
private readonly CoscineLogger _coscineLogger;
/// <summary>
@@ -58,109 +58,18 @@ namespace Coscine.Api.Admin.Controllers
_coscineLogger = new CoscineLogger(logger);
}
/// <summary>
/// Check if the user has a specific role.
/// </summary>
/// <param name="userId">Id (GUID) if the user.</param>
/// <param name="role">The role of the user.</param>
/// <returns>True if user has the role and false if not.</returns>
private bool HasRole(string userId, string role)
{
var graph = _rdfStoreConnector.GetGraph(_graphUrl);
// Get the subject (blank node for the specific member)
var userTriples = graph.GetTriplesWithPredicateObject(graph.CreateUriNode(new Uri(_memberUrl)), graph.CreateUriNode(new Uri($"{_userUrlPrefix}{userId.ToUpper()}")));
// Extract the node
var userSubject = userTriples?.FirstOrDefault()?.Subject;
if (userSubject == null)
{
return false;
}
// Get the role based on the blank node and compare it to the requested role
var roleTriples = graph.GetTriplesWithSubjectPredicate(userSubject, graph.CreateUriNode(new Uri(_roleUrl)));
return (roleTriples?.FirstOrDefault()?.Object.ToString()) == _roleUrlPrefix + role;
}
/// <summary>
/// Sum up allocated quota for all resources of a given resource type within a project.
/// </summary>
/// <param name="resourceType">The used resource type.</param>
/// <param name="projectId">The used project.</param>
/// <returns>Allocated quota of the given resource type in the project.</returns>
private int CalculateAllocatedForAll(ResourceType resourceType, Guid projectId)
{
var resources = _resourceModel.GetAllWhere((resource) =>
(from projectResource in resource.ProjectResources
where projectResource.ProjectId == projectId
select projectResource).Any() &&
resource.TypeId == resourceType.Id);
var allocated = resources.Sum(resource =>
{
// Linked has no quota.
var rt = ResourceTypeFactory.Instance.GetResourceType(resource);
if (rt.GetResourceTypeInformation().Result.IsQuotaAvailable)
{
return rt.GetResourceQuotaAvailable(resource.Id.ToString(), _resourceModel.GetResourceTypeOptions(resource.Id)).Result;
}
else
{
return 0;
}
});
return (int)allocated;
}
/// <summary>
/// Sum up used quota for all resources of a given resource type within a project.
/// </summary>
/// <param name="resourceType">The used resource type.</param>
/// <param name="projectId">The used project.</param>
/// <returns>Used quota of the given resource type in the project.</returns>
private int CalculateUsedForAll(ResourceType resourceType, Guid projectId)
{
var resources = _resourceModel.GetAllWhere((resource) =>
(from projectResource in resource.ProjectResources
where projectResource.ProjectId == projectId
select projectResource).Any() &&
resource.TypeId == resourceType.Id);
var used = Math.Ceiling(
resources.Sum(
resource =>
{
// Linked has no quota.
var rt = ResourceTypeFactory.Instance.GetResourceType(resource);
if (rt.GetResourceTypeInformation().Result.IsQuotaAvailable)
{
return rt.GetResourceQuotaUsed(resource.Id.ToString(), _resourceModel.GetResourceTypeOptions(resource.Id)).Result / _oneGb;
}
else
{
return 0;
}
}
)
);
return (int)used;
}
/// <summary>
/// Find the project related to the projectString(GUID or slug)
/// </summary>
/// <param name="projectString">Either the id (GUID) of the project or the slug.</param>
/// <param name="projectString">The project id (GUID) or slug (from URL).</param>
/// <returns>JSON list of all quotas.</returns>
[HttpGet("[controller]/{projectString}")]
public ActionResult<ProjectObject> GetProject(string projectString)
public ActionResult<AdminProjectObject> GetProject(string projectString)
{
var user = _authenticator.GetUserId();
if (!HasRole(user, _adminRole))
{
return Unauthorized($@"User has not the role: ""{_adminRole}"".");
return Unauthorized($"User does not have the role \"{_adminRole}\".");
}
Project project;
@@ -181,46 +90,46 @@ namespace Coscine.Api.Admin.Controllers
var quotas = _projectQuotaModel.GetAllWhere(x => x.ProjectId == project.Id);
return new ProjectObject
return new AdminProjectObject
{
GUID = project.Id,
Name = project.ProjectName,
ShortName = project.DisplayName,
Quotas = quotas.Select(x => new ProjectQuotaObject
{
QuotaId = x.RelationId,
ResourceType = _resourceTypeModel.GetById(x.ResourceTypeId).SpecificType,
Quota = x.Quota,
MaxQuota = x.MaxQuota,
Used = CalculateUsedForAll(_resourceTypeModel.GetById(x.ResourceTypeId), project.Id),
Allocated = CalculateAllocatedForAll(_resourceTypeModel.GetById(x.ResourceTypeId), project.Id)
}).ToList(),
Id = project.Id,
ProjectName = project.ProjectName,
DisplayName = project.DisplayName,
Quotas = quotas.Select(x => CreateAdminQuotaReturnObject(x, project.Id)).ToList(),
};
}
/// <summary>
/// Update the project quota
/// Update a project maximum and allocated quota
/// </summary>
/// <param name="updateQuotaParameter">JSON object for updating quota.</param>
/// <param name="projectId">Id of the project</param>
/// <param name="resourceTypeId">Id of the resource type</param>
/// <param name="updateQuotaParameter">JSON object for updating the project maximum and allocated quota.</param>
/// <returns>NoContent (204) on success.</returns>
[HttpPost("[controller]/")]
public IActionResult UpdateQuota([FromBody] UpdateQuotaParameterObject updateQuotaParameter)
[HttpPut("[controller]/{projectId}/{resourceTypeId}")]
public IActionResult UpdateQuota(Guid projectId, Guid resourceTypeId, [FromBody] UpdateQuotaParameterObject updateQuotaParameter)
{
var user = _authenticator.GetUser();
if (!HasRole(user.Id.ToString(), _adminRole))
{
return Unauthorized($@"User has not the role: ""{_adminRole}"".");
return Unauthorized($"User does not have the role \"{_adminRole}\".");
}
var reservedQuotaGiB = CalculateAllocatedForAll(_resourceTypeModel.GetById(resourceTypeId), projectId);
if (reservedQuotaGiB > updateQuotaParameter.MaximumGiB)
{
return BadRequest($"Currently {reservedQuotaGiB} GB are reserved by resources in total. Can not set the new maximum quota lower than {reservedQuotaGiB}!");
}
var projectQuotaModel = new ProjectQuotaModel();
var projectQuota = projectQuotaModel.GetById(updateQuotaParameter.QuotaId);
var projectQuota = _projectQuotaModel.GetWhere(x => x.ProjectId == projectId && x.ResourceTypeId == resourceTypeId);
if (projectQuota == null)
{
return NotFound("Quota was not found.");
}
projectQuota.MaxQuota = (int)updateQuotaParameter.Quota;
projectQuota.MaxQuota = (int)updateQuotaParameter.MaximumGiB;
projectQuota.Quota = projectQuota.MaxQuota;
projectQuotaModel.Update(projectQuota);
@@ -239,15 +148,7 @@ namespace Coscine.Api.Admin.Controllers
try
{
var quotas = _projectQuotaModel.GetAllWhere(x => x.ProjectId == projectId);
var quotaObjects = quotas.Select(x => new ProjectQuotaObject
{
QuotaId = x.RelationId,
ResourceType = _resourceTypeModel.GetById(x.ResourceTypeId).DisplayName,
Quota = x.Quota,
MaxQuota = x.MaxQuota,
Used = x.Quota,
Allocated = CalculateAllocatedForAll(_resourceTypeModel.GetById(x.ResourceTypeId), projectId)
}).ToList();
var quotaObjects = quotas.Select(x => CreateAdminQuotaReturnObject(x, projectId)).ToList();
_coscineLogger.AnalyticsLog(
new AnalyticsLogObject
@@ -256,7 +157,7 @@ namespace Coscine.Api.Admin.Controllers
Operation = "Admin Project Quota Change",
UserId = user.Id.ToString(),
ProjectId = projectId.ToString(),
QuotaSize = quotaObjects.ConvertAll(x => $"{x.ResourceType}: {x.Allocated}/{x.Used}")
QuotaSize = quotaObjects.ConvertAll(x => $"{x.ResourceType}: {x.TotalReserved}/{x.TotalUsed}")
});
}
#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception.
@@ -265,5 +166,122 @@ namespace Coscine.Api.Admin.Controllers
{
}
}
/// <summary>
/// Check if the user has a specific role.
/// </summary>
/// <param name="userId">Id (GUID) if the user.</param>
/// <param name="role">The role of the user.</param>
/// <returns>True if user has the role and false if not.</returns>
private bool HasRole(string userId, string role)
{
var graph = _rdfStoreConnector.GetGraph(_graphUrl);
// Get the subject (blank node for the specific member)
var userTriples = graph.GetTriplesWithPredicateObject(graph.CreateUriNode(new Uri(_memberUrl)), graph.CreateUriNode(new Uri($"{_userUrlPrefix}{userId.ToUpper()}")));
// Extract the node
var userSubject = userTriples?.FirstOrDefault()?.Subject;
if (userSubject == null)
{
return false;
}
// Get the role based on the blank node and compare it to the requested role
var roleTriples = graph.GetTriplesWithSubjectPredicate(userSubject, graph.CreateUriNode(new Uri(_roleUrl)));
return (roleTriples?.FirstOrDefault()?.Object.ToString()) == _roleUrlPrefix + role;
}
/// <summary>
/// Sum up allocated quota for all resources of a given resource type within a project.
/// </summary>
/// <param name="resourceType">The used resource type.</param>
/// <param name="projectId">The used project.</param>
/// <returns>Allocated quota of the given resource type in the project.</returns>
private int CalculateAllocatedForAll(ResourceType resourceType, Guid projectId)
{
var resources = _resourceModel.GetAllWhere((resource) =>
(from projectResource in resource.ProjectResources
where projectResource.ProjectId == projectId
select projectResource).Any() &&
resource.TypeId == resourceType.Id);
var allocated = resources.Sum(resource =>
{
// Linked has no quota.
var rt = ResourceTypeFactory.Instance.GetResourceType(resource);
if (rt.GetResourceTypeInformation().Result.IsQuotaAvailable)
{
return rt.GetResourceQuotaAvailable(resource.Id.ToString(), _resourceModel.GetResourceTypeOptions(resource.Id)).Result;
}
else
{
return 0;
}
});
return (int)allocated;
}
/// <summary>
/// Sum up used quota for all resources of a given resource type within a project.
/// </summary>
/// <param name="resourceType">The used resource type.</param>
/// <param name="projectId">The used project.</param>
/// <returns>Used quota of the given resource type in the project.</returns>
private int CalculateUsedForAll(ResourceType resourceType, Guid projectId)
{
var resources = _resourceModel.GetAllWhere((resource) =>
(from projectResource in resource.ProjectResources
where projectResource.ProjectId == projectId
select projectResource).Any() &&
resource.TypeId == resourceType.Id);
var used = resources.Sum(resource =>
{
// Linked has no quota.
var rt = ResourceTypeFactory.Instance.GetResourceType(resource);
if (rt.GetResourceTypeInformation().Result.IsQuotaAvailable)
{
return rt.GetResourceQuotaUsed(resource.Id.ToString(), _resourceModel.GetResourceTypeOptions(resource.Id)).Result;
}
else
{
return 0;
}
}
);
return (int)used;
}
private AdminQuotaReturnObject CreateAdminQuotaReturnObject(ProjectQuota projectQuota, Guid projectId)
{
return new AdminQuotaReturnObject
{
RelationId = projectQuota.RelationId,
ResourceType = _resourceTypeModel.GetById(projectQuota.ResourceTypeId).SpecificType,
Allocated = new QuotaDimObject()
{
Value = projectQuota.Quota,
Unit = QuotaUnit.GibiBYTE,
},
Maximum = new QuotaDimObject()
{
Value = projectQuota.MaxQuota,
Unit = QuotaUnit.GibiBYTE,
},
TotalReserved = new QuotaDimObject()
{
Value = CalculateAllocatedForAll(_resourceTypeModel.GetById(projectQuota.ResourceTypeId), projectId),
Unit = QuotaUnit.GibiBYTE,
},
TotalUsed = new QuotaDimObject()
{
Value = CalculateUsedForAll(_resourceTypeModel.GetById(projectQuota.ResourceTypeId), projectId),
Unit = QuotaUnit.BYTE,
},
};
}
}
}
\ No newline at end of file
Loading