diff --git a/BusinessLogicTier/Logic/ClusterInformation/ClusterInformationLogic.cs b/BusinessLogicTier/Logic/ClusterInformation/ClusterInformationLogic.cs index 6777690..88dab2e 100644 --- a/BusinessLogicTier/Logic/ClusterInformation/ClusterInformationLogic.cs +++ b/BusinessLogicTier/Logic/ClusterInformation/ClusterInformationLogic.cs @@ -52,8 +52,8 @@ public ClusterNodeUsage GetCurrentClusterNodeUsage(long clusterNodeId, AdaptorUs } var clusterProjectIds = nodeType.Cluster.ClusterProjects.Select(x => x.ProjectId).ToList(); - var availableProjectIds = loggedUser.Groups.Where(g => clusterProjectIds.Contains(g.ProjectId.Value)).Select(x => x.ProjectId.Value).ToList(); - if (availableProjectIds.Count == 0) + var availableProjectIds = loggedUser.Groups.Where(g => clusterProjectIds.Contains(g.ProjectId.Value)).Select(x => x.ProjectId.Value).Distinct().ToList(); + if (availableProjectIds.Count() == 0) { throw new InvalidRequestException($"User {loggedUser} has no access to ClusterNodeId {clusterNodeId}."); } diff --git a/BusinessLogicTier/Logic/JobReporting/Converts/JobReportingLogicConverts.cs b/BusinessLogicTier/Logic/JobReporting/Converts/JobReportingLogicConverts.cs index 0cb3399..0bec5c3 100644 --- a/BusinessLogicTier/Logic/JobReporting/Converts/JobReportingLogicConverts.cs +++ b/BusinessLogicTier/Logic/JobReporting/Converts/JobReportingLogicConverts.cs @@ -1,5 +1,7 @@ using HEAppE.DomainObjects.JobManagement.JobInformation; +using HEAppE.DomainObjects.JobReporting.Enums; using System; +using System.Linq; namespace HEAppE.BusinessLogicTier.Logic.JobReporting.Converts { @@ -20,8 +22,13 @@ internal static class JobReportingLogicConverts { double walltimeInSeconds = task.AllocatedTime ?? 0; int ncpus = task.AllocatedCores ?? task.Specification.MaxCores ?? 0; - - return Math.Round((walltimeInSeconds * ncpus) / 3600, 3); + double nNodes = Math.Ceiling((double)ncpus / task.Specification.ClusterNodeType.CoresPerNode); + return task.Project.UsageType switch + { + UsageType.NodeHours => Math.Round(walltimeInSeconds * nNodes / 3600.0, 3), + UsageType.CoreHours => Math.Round(walltimeInSeconds * ncpus / 3600.0, 3), + _ => null, + }; } else { diff --git a/BusinessLogicTier/Logic/JobReporting/JobReportingLogic.cs b/BusinessLogicTier/Logic/JobReporting/JobReportingLogic.cs index ad4f53b..ee16f88 100644 --- a/BusinessLogicTier/Logic/JobReporting/JobReportingLogic.cs +++ b/BusinessLogicTier/Logic/JobReporting/JobReportingLogic.cs @@ -50,7 +50,7 @@ public IEnumerable UserGroupListReport() { AdaptorUserGroup = adaptorUserGroup, Project = GetProjectReport(adaptorUserGroup.Project, DateTime.MinValue, DateTime.UtcNow), - UsageType = DomainObjects.JobReporting.Enums.UsageType.CoreHours + UsageType = adaptorUserGroup.Project.UsageType }).ToList(); return userGroupReports; } diff --git a/BusinessLogicTier/Logic/Management/ManagementLogic.cs b/BusinessLogicTier/Logic/Management/ManagementLogic.cs index 39409ce..a27feaf 100644 --- a/BusinessLogicTier/Logic/Management/ManagementLogic.cs +++ b/BusinessLogicTier/Logic/Management/ManagementLogic.cs @@ -1,5 +1,6 @@ using HEAppE.BusinessLogicTier.Logic.Management.Exceptions; using HEAppE.CertificateGenerator; +using HEAppE.CertificateGenerator.Configuration; using HEAppE.DataAccessTier.UnitOfWork; using HEAppE.DomainObjects.ClusterInformation; using HEAppE.DomainObjects.JobManagement; @@ -243,6 +244,7 @@ private ClusterAuthenticationCredentials CreateClusterAuthenticationCredentials( PrivateKeyFile = keyPath, PrivateKeyPassword = passphrase, AuthenticationType = ClusterAuthenticationCredentialsAuthType.PrivateKey, + CipherType = CipherGeneratorConfiguration.Type, PublicKeyFingerprint = publicKeyFingerprint, ClusterProjectCredentials = new List(), IsGenerated = true @@ -290,6 +292,7 @@ public SecureShellKey RecreateSecureShellKey(string username, string publicKey) credentials.PrivateKeyPassword = passphrase; credentials.PublicKeyFingerprint = secureShellKey.PublicKeyFingerprint; + credentials.CipherType = secureShellKey.CipherType; _unitOfWork.ClusterAuthenticationCredentialsRepository.Update(credentials); } @@ -320,7 +323,8 @@ public string RemoveSecureShellKey(string publicKey) foreach (var credentials in clusterAuthenticationCredentials) { File.Delete(credentials.PrivateKeyFile); - _unitOfWork.ClusterAuthenticationCredentialsRepository.Delete(credentials); + credentials.IsDeleted = true; + _unitOfWork.ClusterAuthenticationCredentialsRepository.Update(credentials); } _unitOfWork.Save(); return "SecureShellKey revoked"; diff --git a/DataAccessTier/DataAccessTier.csproj b/DataAccessTier/DataAccessTier.csproj index 77dcd93..a917d32 100644 --- a/DataAccessTier/DataAccessTier.csproj +++ b/DataAccessTier/DataAccessTier.csproj @@ -14,22 +14,23 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/DataAccessTier/MiddlewareContext.cs b/DataAccessTier/MiddlewareContext.cs index 9397d91..20eb013 100644 --- a/DataAccessTier/MiddlewareContext.cs +++ b/DataAccessTier/MiddlewareContext.cs @@ -212,7 +212,9 @@ private void EnsureDatabaseSeeded() Username = cc.Username, Password = cc.Password, PrivateKeyFile = cc.PrivateKeyFile, - PrivateKeyPassword = cc.PrivateKeyPassword + PrivateKeyPassword = cc.PrivateKeyPassword, + CipherType = cc.CipherType, + IsDeleted = cc.IsDeleted })); InsertOrUpdateSeedData(MiddlewareContextSettings.FileTransferMethods); @@ -245,8 +247,16 @@ private void EnsureDatabaseSeeded() entries.ToList().ForEach(e => e.State = EntityState.Detached); //Update Authentication type - ClusterProjects.ToList().ForEach(cp => cp.ClusterProjectCredentials - .ForEach(cpc => cpc.ClusterAuthenticationCredentials.AuthenticationType = GetCredentialsAuthenticationType(cpc.ClusterAuthenticationCredentials, cp.Cluster))); + ClusterAuthenticationCredentials.ToList().ForEach(clusterAuthenticationCredential => + { + var clusters = clusterAuthenticationCredential.ClusterProjectCredentials + .Select(x => x.ClusterProject.Cluster) + .ToList(); + if (clusters.Count() >= 1) + { + clusterAuthenticationCredential.AuthenticationType = GetCredentialsAuthenticationType(clusterAuthenticationCredential, clusters.First()); + } + }); SaveChanges(); _log.Info("Seed data into the database completed."); @@ -256,9 +266,29 @@ private void ValidateSeed() { _log.Info("Seed validation has started."); ValidateCommandTemplateToProjectReference(MiddlewareContextSettings.CommandTemplates, MiddlewareContextSettings.ClusterProjects); + ValidateClusterAuthenticationCredentialsClusterReference(MiddlewareContextSettings.ClusterAuthenticationCredentials); _log.Info("Seed validation completed."); } + /// + /// Validate ClusterAuthenticationCredentials to used clusters same proxy connection + /// + /// + /// + private void ValidateClusterAuthenticationCredentialsClusterReference(List clusterAuthenticationCredentials) + { + foreach (var clusterAuthenticationCredential in clusterAuthenticationCredentials) + { + var clusters = clusterAuthenticationCredential.ClusterProjectCredentials.Select(x => x.ClusterProject.Cluster).ToList(); + if (clusters.Count() >= 1 && clusters.Any(c => c.ProxyConnection != clusters.First().ProxyConnection)) + { + string message = $"ClusterAuthenticationCredential with id {clusterAuthenticationCredential.Id} has ClusterProjectCredentials with different ProxyConnection."; + _log.Error(message); + throw new ApplicationException(message); + } + } + } + /// /// Validate CommandTemplate to Projectcross reference to ClusterProject mapping /// diff --git a/DataAccessTier/Migrations/20230630110823_SshKeyGeneration.Designer.cs b/DataAccessTier/Migrations/20230718105847_SshKeyGeneration.Designer.cs similarity index 99% rename from DataAccessTier/Migrations/20230630110823_SshKeyGeneration.Designer.cs rename to DataAccessTier/Migrations/20230718105847_SshKeyGeneration.Designer.cs index 4ccfc6a..d340622 100644 --- a/DataAccessTier/Migrations/20230630110823_SshKeyGeneration.Designer.cs +++ b/DataAccessTier/Migrations/20230718105847_SshKeyGeneration.Designer.cs @@ -11,7 +11,7 @@ namespace HEAppE.DataAccessTier.Migrations { [DbContext(typeof(MiddlewareContext))] - [Migration("20230630110823_SshKeyGeneration")] + [Migration("20230718105847_SshKeyGeneration")] partial class SshKeyGeneration { /// @@ -95,6 +95,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CipherType") .HasColumnType("int"); + b.Property("IsDeleted") + .HasColumnType("bit"); + b.Property("IsGenerated") .HasColumnType("bit"); @@ -779,6 +782,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("StartDate") .HasColumnType("datetime2"); + b.Property("UsageType") + .HasColumnType("int"); + b.HasKey("Id"); b.HasIndex("AccountingString") diff --git a/DataAccessTier/Migrations/20230630110823_SshKeyGeneration.cs b/DataAccessTier/Migrations/20230718105847_SshKeyGeneration.cs similarity index 86% rename from DataAccessTier/Migrations/20230630110823_SshKeyGeneration.cs rename to DataAccessTier/Migrations/20230718105847_SshKeyGeneration.cs index 8e6a815..14677ee 100644 --- a/DataAccessTier/Migrations/20230630110823_SshKeyGeneration.cs +++ b/DataAccessTier/Migrations/20230718105847_SshKeyGeneration.cs @@ -9,6 +9,13 @@ public partial class SshKeyGeneration : Migration /// protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.AddColumn( + name: "UsageType", + table: "Project", + type: "int", + nullable: false, + defaultValue: 1); + migrationBuilder.AlterColumn( name: "PreparationScript", table: "CommandTemplate", @@ -57,6 +64,13 @@ protected override void Up(MigrationBuilder migrationBuilder) nullable: false, defaultValue: 0); + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "ClusterAuthenticationCredentials", + type: "bit", + nullable: false, + defaultValue: false); + migrationBuilder.AddColumn( name: "IsGenerated", table: "ClusterAuthenticationCredentials", @@ -75,10 +89,18 @@ protected override void Up(MigrationBuilder migrationBuilder) /// protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropColumn( + name: "UsageType", + table: "Project"); + migrationBuilder.DropColumn( name: "CipherType", table: "ClusterAuthenticationCredentials"); + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "ClusterAuthenticationCredentials"); + migrationBuilder.DropColumn( name: "IsGenerated", table: "ClusterAuthenticationCredentials"); diff --git a/DataAccessTier/Migrations/MiddlewareContextModelSnapshot.cs b/DataAccessTier/Migrations/MiddlewareContextModelSnapshot.cs index c201314..7e2c257 100644 --- a/DataAccessTier/Migrations/MiddlewareContextModelSnapshot.cs +++ b/DataAccessTier/Migrations/MiddlewareContextModelSnapshot.cs @@ -92,6 +92,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CipherType") .HasColumnType("int"); + b.Property("IsDeleted") + .HasColumnType("bit"); + b.Property("IsGenerated") .HasColumnType("bit"); @@ -776,6 +779,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("StartDate") .HasColumnType("datetime2"); + b.Property("UsageType") + .HasColumnType("int"); + b.HasKey("Id"); b.HasIndex("AccountingString") diff --git a/DataAccessTier/Repository/UserAndLimitationManagement/ClusterAuthenticationCredentialsRepository.cs b/DataAccessTier/Repository/UserAndLimitationManagement/ClusterAuthenticationCredentialsRepository.cs index fff8aa1..d9351d6 100644 --- a/DataAccessTier/Repository/UserAndLimitationManagement/ClusterAuthenticationCredentialsRepository.cs +++ b/DataAccessTier/Repository/UserAndLimitationManagement/ClusterAuthenticationCredentialsRepository.cs @@ -20,7 +20,7 @@ public IEnumerable GetAuthenticationCredential { var clusterProject = _context.ClusterProjects.FirstOrDefault(cp => cp.ClusterId == clusterId && cp.ProjectId == projectId); var clusterProjectCredentials = clusterProject?.ClusterProjectCredentials.FindAll(cpc => !cpc.IsServiceAccount); - var credentials = clusterProjectCredentials?.Select(c => c.ClusterAuthenticationCredentials); + var credentials = clusterProjectCredentials?.Select(c => c.ClusterAuthenticationCredentials).Where(x => !x.IsDeleted); return credentials?.ToList() ?? new List(); } @@ -28,13 +28,13 @@ public ClusterAuthenticationCredentials GetServiceAccountCredentials(long cluste { var clusterProject = _context.ClusterProjects.FirstOrDefault(cp => cp.ClusterId == clusterId && cp.ProjectId == projectId); var clusterProjectCredentials = clusterProject?.ClusterProjectCredentials.FindAll(cpc => cpc.IsServiceAccount); - var credentials = clusterProjectCredentials?.Select(c => c.ClusterAuthenticationCredentials); + var credentials = clusterProjectCredentials?.Select(c => c.ClusterAuthenticationCredentials).Where(x => !x.IsDeleted); return credentials?.FirstOrDefault(); } public IEnumerable GetAllGeneratedWithFingerprint(string fingerprint) { - var credentials = _context.ClusterAuthenticationCredentials.Where(x => x.IsGenerated && x.PublicKeyFingerprint == fingerprint); + var credentials = _context.ClusterAuthenticationCredentials.Where(x => x.IsGenerated && !x.IsDeleted && x.PublicKeyFingerprint == fingerprint); return credentials?.ToList() ?? new List(); } #endregion diff --git a/DomainObjects/ClusterInformation/ClusterAuthenticationCredentials.cs b/DomainObjects/ClusterInformation/ClusterAuthenticationCredentials.cs index ebba94f..7e74abc 100644 --- a/DomainObjects/ClusterInformation/ClusterAuthenticationCredentials.cs +++ b/DomainObjects/ClusterInformation/ClusterAuthenticationCredentials.cs @@ -27,7 +27,7 @@ public class ClusterAuthenticationCredentials : IdentifiableDbEntity public ClusterAuthenticationCredentialsAuthType AuthenticationType { get; set; } [Required] - public FileTransferCipherType CipherType { get; set; } = FileTransferCipherType.RSA4096; + public FileTransferCipherType CipherType { get; set; } = FileTransferCipherType.Unknown; [StringLength(200)] public string PublicKeyFingerprint { get; set; } @@ -35,6 +35,9 @@ public class ClusterAuthenticationCredentials : IdentifiableDbEntity [Required] public bool IsGenerated { get; set; } = false; + [Required] + public bool IsDeleted { get; set; } = false; + public virtual List ClusterProjectCredentials { get; set; } = new List(); #endregion #region Override Methods diff --git a/DomainObjects/JobManagement/Project.cs b/DomainObjects/JobManagement/Project.cs index 8fbe95b..c991f77 100644 --- a/DomainObjects/JobManagement/Project.cs +++ b/DomainObjects/JobManagement/Project.cs @@ -1,4 +1,5 @@ -using System; +using HEAppE.DomainObjects.JobReporting.Enums; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -33,6 +34,9 @@ public class Project : IdentifiableDbEntity [Required] public bool IsDeleted { get; set; } = false; + [Required] + public UsageType UsageType { get; set; } = UsageType.NodeHours; + public virtual List ClusterProjects { get; set; } = new List(); public virtual List CommandTemplates { get; set; } = new List(); diff --git a/DomainObjects/JobReporting/Enums/UsageType.cs b/DomainObjects/JobReporting/Enums/UsageType.cs index 1af2534..2b33a9a 100644 --- a/DomainObjects/JobReporting/Enums/UsageType.cs +++ b/DomainObjects/JobReporting/Enums/UsageType.cs @@ -2,7 +2,7 @@ { public enum UsageType { - CoreHours = 1, - NodeHours + NodeHours = 1, + CoreHours = 2 } } diff --git a/ExtModels/ExtModels.csproj b/ExtModels/ExtModels.csproj index a495edc..7fa51dd 100644 --- a/ExtModels/ExtModels.csproj +++ b/ExtModels/ExtModels.csproj @@ -7,7 +7,7 @@ - + diff --git a/ExternalAuthentication/ExternalAuthentication.csproj b/ExternalAuthentication/ExternalAuthentication.csproj index 189f3a5..d500eeb 100644 --- a/ExternalAuthentication/ExternalAuthentication.csproj +++ b/ExternalAuthentication/ExternalAuthentication.csproj @@ -9,7 +9,7 @@ - + diff --git a/RestApi/seed.example.localcomputing.njson b/RestApi/seed.example.localcomputing.njson index f06436b..3790ae0 100644 --- a/RestApi/seed.example.localcomputing.njson +++ b/RestApi/seed.example.localcomputing.njson @@ -92,14 +92,16 @@ "Username": "heappeclient", "Password": "pass", "PrivateKeyFile": "{PrivateKeyFile}", - "PrivateKeyPassword": "{PrivateKeyPassword}" + "PrivateKeyPassword": "{PrivateKeyPassword}", + "CipherType": 1 }, { "Id": 2, "Username": "heappeclient", "Password": "pass", "PrivateKeyFile": "{PrivateKeyFile}", - "PrivateKeyPassword": "{PrivateKeyPassword}" + "PrivateKeyPassword": "{PrivateKeyPassword}", + "CipherType": 1 } ], "Projects": [ diff --git a/RestApi/seed.example.njson b/RestApi/seed.example.njson index 79c0028..300a9ad 100644 --- a/RestApi/seed.example.njson +++ b/RestApi/seed.example.njson @@ -92,14 +92,16 @@ "Username": "{UserName}", "Password": null, "PrivateKeyFile": "{PrivateKeyFile}", - "PrivateKeyPassword": "{PrivateKeyPassword}" + "PrivateKeyPassword": "{PrivateKeyPassword}", + "CipherType": 1 }, { "Id": 2, "Username": "{UserName}", "Password": null, "PrivateKeyFile": "{PrivateKeyFile}", - "PrivateKeyPassword": "{PrivateKeyPassword}" + "PrivateKeyPassword": "{PrivateKeyPassword}", + "CipherType": 1 } ], "Projects": [ diff --git a/RestApiModels/RestApiModels.csproj b/RestApiModels/RestApiModels.csproj index 25899d6..4cf93cc 100644 --- a/RestApiModels/RestApiModels.csproj +++ b/RestApiModels/RestApiModels.csproj @@ -7,7 +7,7 @@ - +