Skip to content

Commit 437134c

Browse files
author
Jakub Konvicka
committed
Merge branch 'develop' into 'master'
push to master See merge request ADAS-Private/HEAppE/heappe-core!324
2 parents a1eda6c + d5f6fb7 commit 437134c

File tree

179 files changed

+25016
-1425
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

179 files changed

+25016
-1425
lines changed

BusinessLogicTier/BusinessLogicTier.csproj

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@
55
<RootNamespace>HEAppE.BusinessLogicTier</RootNamespace>
66
</PropertyGroup>
77
<ItemGroup>
8-
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0"/>
9-
<PackageReference Include="log4net" Version="3.0.3"/>
10-
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0"/>
11-
<PackageReference Include="RestSharp" Version="112.1.0"/>
12-
<PackageReference Include="SSH.NET" Version="2024.2.0"/>
13-
<PackageReference Include="SshNet.Security.Cryptography" Version="1.3.0"/>
8+
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" />
9+
<PackageReference Include="log4net" Version="3.0.3" />
10+
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
11+
<PackageReference Include="RestSharp" Version="112.1.0" />
12+
<PackageReference Include="SSH.NET" Version="2025.0.0" />
13+
<PackageReference Include="SshNet.Security.Cryptography" Version="1.3.0" />
1414
</ItemGroup>
1515
<ItemGroup>
16-
<ProjectReference Include="..\Exceptions\Exceptions.csproj"/>
17-
<ProjectReference Include="..\CertificateGenerator\CertificateGenerator.csproj"/>
18-
<ProjectReference Include="..\DataAccessTier\DataAccessTier.csproj"/>
19-
<ProjectReference Include="..\DomainObjects\DomainObjects.csproj"/>
20-
<ProjectReference Include="..\ExternalAuthentication\ExternalAuthentication.csproj"/>
21-
<ProjectReference Include="..\FileTransferFramework\FileTransferFramework.csproj"/>
22-
<ProjectReference Include="..\HpcConnectionFramework\HpcConnectionFramework.csproj"/>
23-
<ProjectReference Include="..\OpenStackAPI\OpenStackAPI.csproj"/>
16+
<ProjectReference Include="..\Exceptions\Exceptions.csproj" />
17+
<ProjectReference Include="..\CertificateGenerator\CertificateGenerator.csproj" />
18+
<ProjectReference Include="..\DataAccessTier\DataAccessTier.csproj" />
19+
<ProjectReference Include="..\DomainObjects\DomainObjects.csproj" />
20+
<ProjectReference Include="..\ExternalAuthentication\ExternalAuthentication.csproj" />
21+
<ProjectReference Include="..\FileTransferFramework\FileTransferFramework.csproj" />
22+
<ProjectReference Include="..\HpcConnectionFramework\HpcConnectionFramework.csproj" />
23+
<ProjectReference Include="..\OpenStackAPI\OpenStackAPI.csproj" />
2424
</ItemGroup>
2525
</Project>

BusinessLogicTier/Configuration/BusinessLogicConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ public sealed class BusinessLogicConfiguration
1212
/// </summary>
1313
public static bool SharedAccountsPoolMode { get; set; }
1414

15+
/// <summary>
16+
/// Automatic Cluster Account Initialization
17+
/// </summary>
18+
public static bool AutomaticClusterAccountInitialization { get; set; } = true;
19+
1520
/// <summary>
1621
/// Limit of generated file transfer key per job
1722
/// </summary>

BusinessLogicTier/Logic/ClusterInformation/ClusterInformationLogic.cs

Lines changed: 143 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Reflection;
5+
using HEAppE.BusinessLogicTier.Configuration;
6+
using HEAppE.BusinessLogicTier.Factory;
47
using HEAppE.DataAccessTier.UnitOfWork;
58
using HEAppE.DomainObjects.ClusterInformation;
69
using HEAppE.DomainObjects.JobManagement;
@@ -51,27 +54,51 @@ public IEnumerable<Cluster> ListAvailableClusters()
5154

5255
public ClusterNodeUsage GetCurrentClusterNodeUsage(long clusterNodeId, AdaptorUser loggedUser, long projectId)
5356
{
54-
var nodeType = GetClusterNodeTypeById(clusterNodeId);
55-
var project = _unitOfWork.ProjectRepository.GetById(projectId);
56-
if (!nodeType.ClusterId.HasValue) throw new InvalidRequestException("ClusterNodeNoReferenceToCluster");
57+
var nodeType = GetClusterNodeTypeById(clusterNodeId)
58+
?? throw new RequestedObjectDoesNotExistException("ClusterNodeTypeNotFound", clusterNodeId);
59+
60+
var project = _unitOfWork.ProjectRepository.GetById(projectId)
61+
?? throw new RequestedObjectDoesNotExistException("ProjectNotFound", projectId);
62+
63+
var cluster = nodeType.Cluster
64+
?? throw new InvalidRequestException("ClusterNodeNoReferenceToCluster", clusterNodeId);
65+
66+
if (!nodeType.ClusterId.HasValue)
67+
throw new InvalidRequestException("ClusterNodeNoReferenceToClusterId", clusterNodeId);
5768

58-
if (project is null) throw new RequestedObjectDoesNotExistException("ProjectNotFound");
69+
if (loggedUser?.Groups == null || !loggedUser.Groups.Any())
70+
throw new InvalidRequestException("UserHasNoGroups", loggedUser);
71+
72+
var clusterProjectIds = cluster.ClusterProjects?
73+
.Where(x => x.ProjectId == projectId)
74+
.Select(y => y.ProjectId)
75+
.ToList() ?? new List<long>();
76+
77+
var availableProjectIds = loggedUser.Groups
78+
.Where(g => g.ProjectId.HasValue && clusterProjectIds.Contains(g.ProjectId.Value))
79+
.Select(g => g.ProjectId!.Value)
80+
.Distinct()
81+
.ToList();
5982

60-
var clusterProjectIds = nodeType.Cluster.ClusterProjects.Where(x => x.ProjectId == projectId)
61-
.Select(y => y.ProjectId);
62-
var availableProjectIds = loggedUser.Groups.Where(g => clusterProjectIds.Contains(g.ProjectId.Value))
63-
.Select(x => x.ProjectId.Value).Distinct().ToList();
6483
if (availableProjectIds.Count == 0)
6584
throw new InvalidRequestException("UserNoAccessToClusterNode", loggedUser, clusterNodeId);
66-
var serviceAccount =
67-
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetServiceAccountCredentials(
68-
nodeType.ClusterId.Value, projectId);
69-
return serviceAccount is null
70-
? throw new InvalidRequestException("ProjectNoReferenceToCluster", projectId, nodeType.ClusterId.Value)
71-
: SchedulerFactory.GetInstance(nodeType.Cluster.SchedulerType).CreateScheduler(nodeType.Cluster, project)
72-
.GetCurrentClusterNodeUsage(nodeType, serviceAccount);
85+
86+
var serviceAccount = _unitOfWork.ClusterAuthenticationCredentialsRepository
87+
?.GetServiceAccountCredentials(cluster.Id, projectId, requireIsInitialized: true, adaptorUserId: loggedUser.Id);
88+
89+
if (serviceAccount is null)
90+
throw new InvalidRequestException("ProjectNoReferenceToCluster", projectId, cluster.Id);
91+
92+
var schedulerFactory = SchedulerFactory.GetInstance(cluster.SchedulerType)
93+
?? throw new InvalidOperationException("SchedulerFactoryInstanceIsNull");
94+
95+
var scheduler = schedulerFactory.CreateScheduler(cluster, project, adaptorUserId:loggedUser.Id)
96+
?? throw new InvalidOperationException("SchedulerInitializationFailed");
97+
98+
return scheduler.GetCurrentClusterNodeUsage(nodeType, serviceAccount);
7399
}
74100

101+
75102
public IEnumerable<string> GetCommandTemplateParametersName(long commandTemplateId, long projectId,
76103
string userScriptPath, AdaptorUser loggedUser)
77104
{
@@ -97,37 +124,92 @@ public IEnumerable<string> GetCommandTemplateParametersName(long commandTemplate
97124
var cluster = commandTemplate.ClusterNodeType.Cluster;
98125
var serviceAccountCredentials =
99126
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetServiceAccountCredentials(cluster.Id,
100-
projectId);
127+
projectId, requireIsInitialized: true, adaptorUserId: loggedUser.Id);
101128
if (serviceAccountCredentials is null)
102129
throw new RequestedObjectDoesNotExistException("ServiceAccountCredentialsNotDefinedInCommandTemplate");
103130

104131
var commandTemplateParameters = new List<string> { scriptPath };
105132
commandTemplateParameters.AddRange(SchedulerFactory.GetInstance(cluster.SchedulerType)
106-
.CreateScheduler(cluster, project)
133+
.CreateScheduler(cluster, project, adaptorUserId: loggedUser.Id)
107134
.GetParametersFromGenericUserScript(cluster, serviceAccountCredentials, userScriptPath).ToList());
108135
return commandTemplateParameters;
109136
}
110137

111138
return commandTemplate.TemplateParameters.Select(s => s.Identifier)
112139
.ToList();
113140
}
141+
142+
private IEnumerable<ClusterAuthenticationCredentials> InitializeCredentials(long projectId, long clusterId, long? adaptorUserId)
143+
{
144+
var credentials =
145+
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetAuthenticationCredentialsForClusterAndProject(
146+
clusterId, projectId, false, null);
147+
var managementLogic = LogicFactory.GetLogicFactory().CreateManagementLogic(_unitOfWork);
148+
foreach (var credential in credentials)
149+
{
150+
var status = managementLogic.InitializeClusterScriptDirectory(
151+
projectId,
152+
true,
153+
adaptorUserId: adaptorUserId.HasValue ? adaptorUserId.Value : null,
154+
username: credential.Username
155+
);
156+
_log.Info($"Initialized credential {credential.Username} for project {projectId} with status: {status}");
157+
}
158+
return credentials;
159+
}
114160

115-
public ClusterAuthenticationCredentials GetNextAvailableUserCredentials(long clusterId, long projectId)
161+
public ClusterAuthenticationCredentials GetNextAvailableUserCredentials(long clusterId, long projectId, bool requireIsInitialized, long? adaptorUserId)
116162
{
117163
var cluster = _unitOfWork.ClusterRepository.GetById(clusterId);
164+
if (cluster == null)
165+
throw new RequestedObjectDoesNotExistException("ClusterNotExists", clusterId);
118166

119-
if (cluster == null) throw new RequestedObjectDoesNotExistException("ClusterNotExists", clusterId);
167+
var project = _unitOfWork.ProjectRepository.GetById(projectId);
168+
if (project == null)
169+
throw new RequestedObjectDoesNotExistException("ProjectNotExists", projectId);
120170

171+
if (project.IsOneToOneMapping)
172+
{
173+
try
174+
{
175+
return GetNextAvailableUserCredentialsByAdaptorUser(clusterId, projectId, requireIsInitialized,
176+
adaptorUserId.Value);
177+
}
178+
catch (NotAllowedException ex)
179+
{
180+
if (BusinessLogicConfiguration.AutomaticClusterAccountInitialization)
181+
{
182+
_log.Info($"Automatic initialization of cluster accounts is enabled. Attempting to initialize accounts for project {projectId} on cluster {clusterId} for adaptor user {adaptorUserId}");
183+
var initializedCredentials = InitializeCredentials(projectId, clusterId, adaptorUserId);
184+
return GetNextAvailableUserCredentialsByAdaptorUser(clusterId, projectId, requireIsInitialized,
185+
adaptorUserId.Value);
186+
}
187+
}
188+
}
189+
121190
//return all non service account for specific cluster and project
122-
var credentials =
123-
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetAuthenticationCredentialsForClusterAndProject(
124-
clusterId, projectId);
191+
IEnumerable<ClusterAuthenticationCredentials> credentials = new List<ClusterAuthenticationCredentials>();
192+
try
193+
{
194+
credentials =
195+
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetAuthenticationCredentialsForClusterAndProject(
196+
clusterId, projectId, requireIsInitialized, null);
197+
}
198+
catch (NotAllowedException ex)
199+
{
200+
if (BusinessLogicConfiguration.AutomaticClusterAccountInitialization)
201+
{
202+
_log.Info($"Automatic initialization of cluster accounts is enabled. Attempting to initialize accounts for project {projectId} on cluster {clusterId}");
203+
credentials = InitializeCredentials(projectId, clusterId, null);
204+
}
205+
}
206+
125207
if (credentials == null || credentials?.Count() == 0)
126208
throw new RequestedObjectDoesNotExistException("ClusterProjectCombinationNotFound", clusterId, projectId);
209+
127210
var serviceCredentials =
128-
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetServiceAccountCredentials(clusterId, projectId)
129-
?? throw new RequestedObjectDoesNotExistException("ClusterProjectCombinationNoServiceAccount", clusterId,
130-
projectId);
211+
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetServiceAccountCredentials(clusterId, projectId, requireIsInitialized, null)
212+
?? throw new RequestedObjectDoesNotExistException("ClusterProjectCombinationNoServiceAccount", clusterId, projectId);
131213

132214
var firstCredentials = credentials.FirstOrDefault();
133215

@@ -152,6 +234,41 @@ public ClusterAuthenticationCredentials GetNextAvailableUserCredentials(long clu
152234
return creds;
153235
}
154236

237+
private ClusterAuthenticationCredentials GetNextAvailableUserCredentialsByAdaptorUser(long clusterId, long projectId, bool requireIsInitialized, long adaptorUserId)
238+
{
239+
//return all non service account for specific cluster and project
240+
var credentials =
241+
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetAuthenticationCredentialsForClusterAndProject(
242+
clusterId, projectId, requireIsInitialized, adaptorUserId);
243+
if (credentials == null || credentials?.Count() == 0)
244+
throw new RequestedObjectDoesNotExistException("ClusterProjectCombinationNotFound", clusterId, projectId);
245+
246+
var serviceCredentials =
247+
_unitOfWork.ClusterAuthenticationCredentialsRepository.GetServiceAccountCredentials(clusterId, projectId, requireIsInitialized, adaptorUserId)
248+
?? throw new RequestedObjectDoesNotExistException("ClusterAuthenticationCredentialsNoServiceAccount", clusterId, projectId, adaptorUserId);
249+
250+
var firstCredentials = credentials.FirstOrDefault();
251+
var lastUsedId = AdaptorUserProjectClusterUserCache.GetLastUserId(adaptorUserId, projectId, clusterId);
252+
if (lastUsedId is null)
253+
{
254+
// No user has been used from this cluster
255+
// return first usable account
256+
AdaptorUserProjectClusterUserCache.SetLastUserId(adaptorUserId, projectId, clusterId, serviceCredentials.Id, firstCredentials.Id);
257+
_log.DebugFormat("Using initial cluster account: {0}", firstCredentials.Username);
258+
return firstCredentials;
259+
}
260+
261+
// Return first user with Id higher than the last one
262+
var creds = (from account in credentials where account.Id > lastUsedId select account).FirstOrDefault();
263+
// No credentials with Id higher than last used found
264+
// use first usable account
265+
creds ??= firstCredentials;
266+
267+
AdaptorUserProjectClusterUserCache.SetLastUserId(adaptorUserId, projectId, clusterId, serviceCredentials.Id, creds.Id);
268+
_log.DebugFormat("Using cluster account: {0}", creds.Username);
269+
return creds;
270+
}
271+
155272
public ClusterNodeType GetClusterNodeTypeById(long clusterNodeTypeId)
156273
{
157274
var nodeType = _unitOfWork.ClusterNodeTypeRepository.GetById(clusterNodeTypeId);

BusinessLogicTier/Logic/ClusterInformation/ClusterUserCache.cs

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public static class ClusterUserCache
2020
{
2121
lock (lastUserId)
2222
{
23-
if (lastUserId == null || !lastUserId.ContainsKey(cluster.Id)) return null;
23+
if (lastUserId == null || !lastUserId.ContainsKey(cluster.Id))
24+
return null;
2425
return lastUserId[cluster.Id];
2526
}
2627
}
@@ -37,7 +38,7 @@ public static void SetLastUserId(Cluster cluster, ClusterAuthenticationCredentia
3738
{
3839
if (serviceAccount.Id != clusterUserId)
3940
{
40-
if (lastUserId == null) lastUserId = new Dictionary<long, long>();
41+
lastUserId ??= [];
4142

4243
if (!lastUserId.ContainsKey(cluster.Id))
4344
lastUserId.Add(cluster.Id, clusterUserId);
@@ -46,4 +47,49 @@ public static void SetLastUserId(Cluster cluster, ClusterAuthenticationCredentia
4647
}
4748
}
4849
}
49-
}
50+
}
51+
52+
public static class AdaptorUserProjectClusterUserCache
53+
{
54+
private static Dictionary<(long, long, long), long> lastUserId = [];
55+
56+
/// <summary>
57+
/// Returns id of last used user of given cluster.
58+
/// Returns zero if no user of given cluster has been used yet.
59+
/// </summary>
60+
/// <param name="cluster">Cluster</param>
61+
/// <returns>Last used user id</returns>
62+
public static long? GetLastUserId(long adaptorUserId, long projectId, long clusterId)
63+
{
64+
var key = (adaptorUserId, projectId, clusterId);
65+
lock (lastUserId)
66+
{
67+
if (lastUserId == null || !lastUserId.ContainsKey(key))
68+
return null;
69+
return lastUserId[key];
70+
}
71+
}
72+
73+
/// <summary>
74+
/// Sets last used user id for given cluster.
75+
/// </summary>
76+
/// <param name="cluster">Cluster</param>
77+
/// <param name="clusterUserId">Last used user id</param>
78+
public static void SetLastUserId(long adaptorUserId, long projectId, long clusterId,
79+
long serviceAccountId, long clusterUserId)
80+
{
81+
var key = (adaptorUserId, projectId, clusterId);
82+
lock (lastUserId)
83+
{
84+
if (serviceAccountId != clusterUserId)
85+
{
86+
lastUserId ??= [];
87+
88+
if (!lastUserId.ContainsKey(key))
89+
lastUserId.Add(key, clusterUserId);
90+
else
91+
lastUserId[key] = clusterUserId;
92+
}
93+
}
94+
}
95+
}

BusinessLogicTier/Logic/ClusterInformation/IClusterInformationLogic.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public interface IClusterInformationLogic
1313
IEnumerable<string> GetCommandTemplateParametersName(long commandTemplateId, long projectId, string userScriptPath,
1414
AdaptorUser loggedUser);
1515

16-
ClusterAuthenticationCredentials GetNextAvailableUserCredentials(long clusterId, long projectId);
16+
ClusterAuthenticationCredentials GetNextAvailableUserCredentials(long clusterId, long projectId, bool requireIsInitialized, long? adaptorUserId);
1717
ClusterNodeType GetClusterNodeTypeById(long clusterNodeTypeId);
1818
Cluster GetClusterById(long clusterId);
1919
IEnumerable<ClusterNodeType> ListClusterNodeTypes();

0 commit comments

Comments
 (0)