source: stable/HeuristicLab.Clients.Hive/3.3/HiveAdminClient.cs @ 17067

Last change on this file since 17067 was 17067, checked in by mkommend, 2 years ago

#2839: Merged 16622, 16878 into stable.

File size: 22.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Threading;
26using HeuristicLab.Clients.Access;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29
30namespace HeuristicLab.Clients.Hive {
31  [Item("Hive Administrator", "Hive Administrator")]
32  public sealed class HiveAdminClient : IContent {
33    private static HiveAdminClient instance;
34    public static HiveAdminClient Instance {
35      get {
36        if (instance == null) instance = new HiveAdminClient();
37        return instance;
38      }
39    }
40
41    #region Properties
42    private IItemList<Resource> resources;
43    public IItemList<Resource> Resources {
44      get { return resources; }
45    }
46
47    private IItemList<Downtime> downtimes;
48    public IItemList<Downtime> Downtimes {
49      get { return downtimes; }
50    }
51
52    private Guid downtimeForResourceId;
53    public Guid DowntimeForResourceId {
54      get { return downtimeForResourceId; }
55      set {
56        downtimeForResourceId = value;
57        if (downtimes != null) {
58          downtimes.Clear();
59        }
60      }
61    }
62
63    private IItemList<Project> projects;
64    public IItemList<Project> Projects {
65      get { return projects; }
66    }
67
68    private IItemList<AssignedProjectResource> projectResourceAssignments;
69    public IItemList<AssignedProjectResource> ProjectResourceAssignments {
70      get { return projectResourceAssignments; }
71    }
72
73    private Dictionary<Guid, HiveItemCollection<RefreshableJob>> jobs;
74    public Dictionary<Guid, HiveItemCollection<RefreshableJob>> Jobs {
75      get { return jobs; }
76      set {
77        if (value != jobs)
78          jobs = value;
79      }
80    }
81
82    private Dictionary<Guid, List<LightweightTask>> tasks;
83    public Dictionary<Guid, List<LightweightTask>> Tasks {
84      get { return tasks; }
85    }
86
87    private Dictionary<Guid, HashSet<Guid>> projectAncestors;
88    public Dictionary<Guid, HashSet<Guid>> ProjectAncestors {
89      get { return projectAncestors; }
90    }
91
92    private Dictionary<Guid, HashSet<Guid>> projectDescendants;
93    public Dictionary<Guid, HashSet<Guid>> ProjectDescendants {
94      get { return projectDescendants; }
95    }
96
97    private Dictionary<Guid, HashSet<Guid>> resourceAncestors;
98    public Dictionary<Guid, HashSet<Guid>> ResourceAncestors {
99      get { return resourceAncestors; }
100    }
101
102    private Dictionary<Guid, HashSet<Guid>> resourceDescendants;
103    public Dictionary<Guid, HashSet<Guid>> ResourceDescendants {
104      get { return resourceDescendants; }
105    }
106
107    private Dictionary<Guid, string> projectNames;
108    public Dictionary<Guid, string> ProjectNames {
109      get { return projectNames; }
110    }
111
112    private HashSet<Project> disabledParentProjects;
113    public HashSet<Project> DisabledParentProjects {
114      get { return disabledParentProjects; }
115    }
116
117    private Dictionary<Guid, string> resourceNames;
118    public Dictionary<Guid, string> ResourceNames {
119      get { return resourceNames; }
120    }
121
122    private HashSet<Resource> disabledParentResources;
123    public HashSet<Resource> DisabledParentResources {
124      get { return disabledParentResources; }
125    }
126    #endregion
127
128    #region Events
129    public event EventHandler Refreshing;
130    private void OnRefreshing() {
131      EventHandler handler = Refreshing;
132      if (handler != null) handler(this, EventArgs.Empty);
133    }
134    public event EventHandler Refreshed;
135    private void OnRefreshed() {
136      var handler = Refreshed;
137      if (handler != null) handler(this, EventArgs.Empty);
138    }
139    #endregion
140
141    private HiveAdminClient() { }
142
143    #region Refresh
144    public void Refresh() {
145      OnRefreshing();
146
147      try {
148        resources = new ItemList<Resource>();
149        projects = new ItemList<Project>();
150        projectResourceAssignments = new ItemList<AssignedProjectResource>();
151        jobs = new Dictionary<Guid, HiveItemCollection<RefreshableJob>>();
152        tasks = new Dictionary<Guid, List<LightweightTask>>();
153        projectNames = new Dictionary<Guid, string>();
154        resourceNames = new Dictionary<Guid, string>();
155
156        projectAncestors = new Dictionary<Guid, HashSet<Guid>>();
157        projectDescendants = new Dictionary<Guid, HashSet<Guid>>();
158        resourceAncestors = new Dictionary<Guid, HashSet<Guid>>();
159        resourceDescendants = new Dictionary<Guid, HashSet<Guid>>();
160
161        HiveServiceLocator.Instance.CallHiveService(service => {
162          service.GetSlaveGroupsForAdministration().ForEach(g => resources.Add(g));
163          service.GetSlavesForAdministration().ForEach(s => resources.Add(s));
164          service.GetProjectsForAdministration().ForEach(p => projects.Add(p));
165          var projectIds = projects.Select(p => p.Id).ToList();
166          if (projectIds.Any()) {
167            service.GetAssignedResourcesForProjectsAdministration(projectIds)
168                   .ForEach(a => projectResourceAssignments.Add(a));
169
170            var unsortedJobs = service.GetJobsByProjectIds(projectIds)
171                                      .OrderBy(x => x.DateCreated).ToList();
172
173            projectNames = service.GetProjectNames();
174            resourceNames = service.GetResourceNames();
175          }
176        });
177
178        UpdateResourceGenealogy();
179        UpdateProjectGenealogy();
180        RefreshDisabledParentProjects();
181        RefreshDisabledParentResources();
182      } catch {
183        throw;
184      } finally {
185        OnRefreshed();
186      }
187    }
188
189    private void UpdateResourceGenealogy() {
190      resourceAncestors.Clear();
191      resourceDescendants.Clear();
192
193      // fetch resource ancestor set
194      HiveServiceLocator.Instance.CallHiveService(service => {
195        var ra = service.GetResourceGenealogy();
196        ra.Keys.ToList().ForEach(k => resourceAncestors.Add(k, new HashSet<Guid>()));
197        resourceAncestors.Keys.ToList().ForEach(k => resourceAncestors[k].UnionWith(ra[k]));
198      });
199
200      // build resource descendant set
201      resourceAncestors.Keys.ToList().ForEach(k => resourceDescendants.Add(k, new HashSet<Guid>()));
202      foreach (var ra in resourceAncestors) {
203        foreach (var ancestor in ra.Value) {
204          resourceDescendants[ancestor].Add(ra.Key);
205        }
206      }
207    }
208
209    private void UpdateProjectGenealogy() {
210      projectAncestors.Clear();
211      projectDescendants.Clear();
212
213      // fetch project ancestor list
214      HiveServiceLocator.Instance.CallHiveService(service => {
215        var pa = service.GetProjectGenealogy();
216        pa.Keys.ToList().ForEach(k => projectAncestors.Add(k, new HashSet<Guid>()));
217        projectAncestors.Keys.ToList().ForEach(k => projectAncestors[k].UnionWith(pa[k]));
218      });
219
220      // build project descendant list
221      projectAncestors.Keys.ToList().ForEach(k => projectDescendants.Add(k, new HashSet<Guid>()));
222      foreach (var pa in projectAncestors) {
223        foreach (var ancestor in pa.Value) {
224          projectDescendants[ancestor].Add(pa.Key);
225        }
226      }
227    }
228
229    private void RefreshDisabledParentProjects() {
230      disabledParentProjects = new HashSet<Project>();
231
232      foreach (var pid in projects
233        .Where(x => x.ParentProjectId.HasValue)
234        .SelectMany(x => projectAncestors[x.Id]).Distinct()
235        .Where(x => !projects.Select(y => y.Id).Contains(x))) {
236        var p = new Project();
237        p.Id = pid;
238        p.ParentProjectId = projectAncestors[pid].FirstOrDefault();
239        p.Name = projectNames[pid];
240        disabledParentProjects.Add(p);
241      }
242    }
243
244    private void RefreshDisabledParentResources() {
245      disabledParentResources = new HashSet<Resource>();
246
247      foreach (var rid in resources
248        .Where(x => x.ParentResourceId.HasValue)
249        .SelectMany(x => resourceAncestors[x.Id]).Distinct()
250        .Where(x => !resources.Select(y => y.Id).Contains(x))) {
251        var r = new SlaveGroup();
252        r.Id = rid;
253        r.ParentResourceId = resourceAncestors[rid].FirstOrDefault();
254        r.Name = resourceNames[rid];
255        disabledParentResources.Add(r);
256      }
257    }
258
259    public void RefreshJobs(Guid projectId) {
260      HiveServiceLocator.Instance.CallHiveService(service => {
261        var projectIds = new HashSet<Guid>(service.GetProjectsForAdministration().Select(x => x.Id));
262        if (projectIds.Contains(projectId)) {
263          jobs.Add(projectId, new HiveItemCollection<RefreshableJob>());
264
265          var unsortedJobs = service.GetJobsByProjectId(projectId)
266                                    .OrderBy(x => x.DateCreated).ToList();
267
268          unsortedJobs.Where(j => j.State == JobState.DeletionPending).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
269          unsortedJobs.Where(j => j.State == JobState.StatisticsPending).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
270          unsortedJobs.Where(j => j.State == JobState.Online).ToList().ForEach(j => jobs[j.ProjectId].Add(new RefreshableJob(j)));
271
272          foreach (var job in jobs.SelectMany(x => x.Value))
273            LoadLightweightJob(job);
274        }
275      });
276    }
277
278    public void LoadLightweightJob(RefreshableJob refreshableJob) {
279      var job = refreshableJob.Job;
280      var lightweightTasks = HiveServiceLocator.Instance.CallHiveService(s => s.GetLightweightJobTasksWithoutStateLog(job.Id));
281
282      if (tasks.ContainsKey(job.Id)) {
283        tasks[job.Id].Clear();
284        tasks[job.Id].AddRange(lightweightTasks);
285      } else {
286        tasks.Add(job.Id, new List<LightweightTask>(lightweightTasks));
287      }
288
289      if (lightweightTasks != null && lightweightTasks.Count > 0 && lightweightTasks.All(x => x.Id != Guid.Empty)) {
290        if (lightweightTasks.All(x =>
291          x.State == TaskState.Finished
292          || x.State == TaskState.Aborted
293          || x.State == TaskState.Failed)) {
294          refreshableJob.ExecutionState = ExecutionState.Stopped;
295          refreshableJob.RefreshAutomatically = false;
296        } else if (
297          lightweightTasks
298            .Where(x => x.ParentTaskId != null)
299            .All(x =>
300              x.State != TaskState.Waiting
301              || x.State != TaskState.Transferring
302              || x.State != TaskState.Calculating)
303          && lightweightTasks
304             .Where(x => x.ParentTaskId != null)
305             .Any(x => x.State == TaskState.Paused)) {
306          refreshableJob.ExecutionState = ExecutionState.Paused;
307          refreshableJob.RefreshAutomatically = false;
308        } else if (lightweightTasks.Any(x => x.State == TaskState.Calculating
309                                  || x.State == TaskState.Transferring
310                                  || x.State == TaskState.Waiting)) {
311          refreshableJob.ExecutionState = ExecutionState.Started;
312        }
313
314        refreshableJob.ExecutionTime = TimeSpan.FromMilliseconds(lightweightTasks.Sum(x => x.ExecutionTime.TotalMilliseconds));
315      }
316    }
317
318    public void SortJobs() {
319      for (int i = 0; i < jobs.Count; i++) {
320        var projectId = jobs.Keys.ElementAt(i);
321        var unsortedJobs = jobs.Values.ElementAt(i);
322
323        var sortedJobs = new HiveItemCollection<RefreshableJob>();
324        sortedJobs.AddRange(unsortedJobs.Where(j => j.Job.State == JobState.DeletionPending));
325        sortedJobs.AddRange(unsortedJobs.Where(j => j.Job.State == JobState.StatisticsPending));
326        sortedJobs.AddRange(unsortedJobs.Where(j => j.Job.State == JobState.Online));
327
328        jobs[projectId] = sortedJobs;
329      }
330    }
331
332    #endregion
333
334    #region Refresh downtime calendar
335    public void RefreshCalendar() {
336      if (downtimeForResourceId != null && downtimeForResourceId != Guid.Empty) {
337        OnRefreshing();
338
339        try {
340          downtimes = new ItemList<Downtime>();
341
342          HiveServiceLocator.Instance.CallHiveService(service => {
343            service.GetDowntimesForResource(downtimeForResourceId).ForEach(d => downtimes.Add(d));
344          });
345        } catch {
346          throw;
347        } finally {
348          OnRefreshed();
349        }
350      }
351    }
352    #endregion
353
354    #region Store
355    public static void Store(IHiveItem item, CancellationToken cancellationToken) {
356      if (item.Id == Guid.Empty) {
357        if (item is SlaveGroup) {
358          item.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddSlaveGroup((SlaveGroup)item));
359        }
360        if (item is Slave) {
361          item.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddSlave((Slave)item));
362        }
363        if (item is Downtime) {
364          item.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddDowntime((Downtime)item));
365        }
366        if (item is Project) {
367          item.Id = HiveServiceLocator.Instance.CallHiveService(s => s.AddProject((Project)item));
368        }
369      } else {
370        if (item is SlaveGroup) {
371          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateSlaveGroup((SlaveGroup)item));
372        }
373        if (item is Slave) {
374          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateSlave((Slave)item));
375        }
376        if (item is Downtime) {
377          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateDowntime((Downtime)item));
378        }
379        if (item is Project) {
380          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateProject((Project)item));
381        }
382      }
383    }
384    #endregion
385
386    #region Delete
387    public static void Delete(IHiveItem item) {
388      if (item is SlaveGroup) {
389        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteSlaveGroup(item.Id));
390      } else if (item is Slave) {
391        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteSlave(item.Id));
392      } else if (item is Downtime) {
393        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteDowntime(item.Id));
394      } else if (item is Project) {
395        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteProject(item.Id));
396      }
397    }
398
399    public static void RemoveJobs(List<Guid> jobIds) {
400      HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateJobStates(jobIds, JobState.StatisticsPending));
401    }
402    #endregion
403
404    #region Job Handling
405
406    public static void ResumeJob(RefreshableJob refreshableJob) {
407      HiveServiceLocator.Instance.CallHiveService(service => {
408        var tasks = service.GetLightweightJobTasksWithoutStateLog(refreshableJob.Id);
409        foreach (var task in tasks) {
410          if (task.State == TaskState.Paused) {
411            service.RestartTask(task.Id);
412          }
413        }
414      });
415      refreshableJob.ExecutionState = ExecutionState.Started;
416    }
417
418    public static void PauseJob(RefreshableJob refreshableJob) {
419      HiveServiceLocator.Instance.CallHiveService(service => {
420        var tasks = service.GetLightweightJobTasksWithoutStateLog(refreshableJob.Id);
421        foreach (var task in tasks) {
422          if (task.State != TaskState.Finished && task.State != TaskState.Aborted && task.State != TaskState.Failed)
423            service.PauseTask(task.Id);
424        }
425      });
426      refreshableJob.ExecutionState = ExecutionState.Paused;
427    }
428
429    public static void StopJob(RefreshableJob refreshableJob) {
430      HiveServiceLocator.Instance.CallHiveService(service => {
431        var tasks = service.GetLightweightJobTasksWithoutStateLog(refreshableJob.Id);
432        foreach (var task in tasks) {
433          if (task.State != TaskState.Finished && task.State != TaskState.Aborted && task.State != TaskState.Failed)
434            service.StopTask(task.Id);
435        }
436      });
437      refreshableJob.ExecutionState = ExecutionState.Stopped;
438    }
439
440    public static void RemoveJob(RefreshableJob refreshableJob) {
441      HiveServiceLocator.Instance.CallHiveService((service) => {
442        service.UpdateJobState(refreshableJob.Id, JobState.StatisticsPending);
443      });
444    }
445    #endregion
446
447    public void ResetDowntime() {
448      downtimeForResourceId = Guid.Empty;
449      if (downtimes != null) {
450        downtimes.Clear();
451      }
452    }
453
454    #region Helper
455    public IEnumerable<Project> GetAvailableProjectAncestors(Guid id) {
456      if (projectAncestors.ContainsKey(id)) return projects.Where(x => projectAncestors[id].Contains(x.Id));
457      else return Enumerable.Empty<Project>();
458    }
459
460    public IEnumerable<Project> GetAvailableProjectDescendants(Guid id) {
461      if (projectDescendants.ContainsKey(id)) return projects.Where(x => projectDescendants[id].Contains(x.Id));
462      else return Enumerable.Empty<Project>();
463    }
464
465    public IEnumerable<Resource> GetAvailableResourceAncestors(Guid id) {
466      if (resourceAncestors.ContainsKey(id)) return resources.Where(x => resourceAncestors[id].Contains(x.Id));
467      else return Enumerable.Empty<Resource>();
468    }
469
470    public IEnumerable<Resource> GetAvailableResourceDescendants(Guid id) {
471      if (resourceDescendants.ContainsKey(id)) return resources.Where(x => resourceDescendants[id].Contains(x.Id));
472      else return Enumerable.Empty<Resource>();
473    }
474
475    public IEnumerable<Resource> GetDisabledResourceAncestors(IEnumerable<Resource> availableResources) {
476      var missingParentIds = availableResources
477        .Where(x => x.ParentResourceId.HasValue)
478        .SelectMany(x => resourceAncestors[x.Id]).Distinct()
479        .Where(x => !availableResources.Select(y => y.Id).Contains(x));
480
481      return resources.OfType<SlaveGroup>().Union(disabledParentResources).Where(x => missingParentIds.Contains(x.Id));
482    }
483
484    public bool CheckAccessToAdminAreaGranted() {
485      if (projects != null) {
486        return projects.Count > 0;
487      } else {
488        bool accessGranted = false;
489        HiveServiceLocator.Instance.CallHiveService(s => {
490          accessGranted = s.CheckAccessToAdminAreaGranted();
491        });
492        return accessGranted;
493      }
494    }
495
496    public bool CheckOwnershipOfResource(Resource res, Guid userId) {
497      if (res == null || userId == Guid.Empty) return false;
498
499      if (res.OwnerUserId == userId) {
500        return true;
501      } else if (resourceAncestors.ContainsKey(res.Id)) {
502        return GetAvailableResourceAncestors(res.Id).Where(x => x.OwnerUserId == userId).Any();
503      }
504
505      return false;
506    }
507
508    public bool CheckOwnershipOfProject(Project pro, Guid userId) {
509      if (pro == null || userId == Guid.Empty) return false;
510
511      if (pro.OwnerUserId == userId) {
512        return true;
513      } else if (projectAncestors.ContainsKey(pro.Id)) {
514        return GetAvailableProjectAncestors(pro.Id).Where(x => x.OwnerUserId == userId).Any();
515      }
516
517      return false;
518    }
519
520    public bool CheckOwnershipOfParentProject(Project pro, Guid userId) {
521      if (pro == null || userId == Guid.Empty) return false;
522
523      if (projectAncestors.ContainsKey(pro.Id)) {
524        return GetAvailableProjectAncestors(pro.Id).Any(x => x.OwnerUserId == userId);
525      }
526
527      if (pro.ParentProjectId != null && pro.ParentProjectId != Guid.Empty) {
528        var parent = projects.FirstOrDefault(x => x.Id == pro.ParentProjectId.Value);
529        if (parent != null)
530          return parent.OwnerUserId == userId || GetAvailableProjectAncestors(parent.Id).Any(x => x.OwnerUserId == userId);
531      }
532
533      return false;
534    }
535
536    public bool CheckParentChange(Project child, Project parent) {
537      bool changePossible = true;
538
539      // change is not possible...
540      // ... if the moved project is null
541      // ... or the new parent is not stored yet
542      // ... or there is not parental change
543      if (child == null
544          || (parent != null && parent.Id == Guid.Empty)
545          || (parent != null && parent.Id == child.ParentProjectId)) {
546        changePossible = false;
547      } else if (parent == null && !IsAdmin()) {
548        // ... if parent is null, but user is no admin (only admins are allowed to create root projects)
549        changePossible = false;
550      } else if (parent != null && (!IsAdmin() && parent.OwnerUserId != UserInformation.Instance.User.Id && !CheckOwnershipOfParentProject(parent, UserInformation.Instance.User.Id))) {
551        // ... if the user is no admin nor owner of the new parent or grand..grandparents
552        changePossible = false;
553      } else if (parent != null && projectDescendants.ContainsKey(child.Id)) {
554        // ... if the new parent is among the moved project's descendants
555        changePossible = !GetAvailableProjectDescendants(child.Id).Where(x => x.Id == parent.Id).Any();
556      }
557
558      return changePossible;
559    }
560
561    public bool CheckParentChange(Resource child, Resource parent) {
562      bool changePossible = true;
563
564      // change is not possisble...
565      // ... if the child resource is null
566      // ... or the child resource equals the parent
567      // ... or the new parent is not stored yet
568      // ... or the new parent is a slave
569      // ... or there is not parental change
570      if (child == null
571        || child == parent
572        || (parent != null && parent.Id == Guid.Empty)
573        || (parent != null && parent is Slave)
574        || (parent != null && parent.Id == child.ParentResourceId)) {
575        changePossible = false;
576      } else if (parent != null && resourceDescendants.ContainsKey(child.Id)) {
577        // ... or if the new parent is among the moved resource's descendants
578        changePossible = !GetAvailableResourceDescendants(child.Id).Where(x => x.Id == parent.Id).Any();
579      }
580
581      return changePossible;
582    }
583
584    public IEnumerable<Resource> GetAssignedResourcesForJob(Guid jobId) {
585      var assignedJobResource = HiveServiceLocator.Instance.CallHiveService(service => service.GetAssignedResourcesForJob(jobId));
586      return Resources.Where(x => assignedJobResource.Select(y => y.ResourceId).Contains(x.Id));
587    }
588
589    private bool IsAdmin() {
590      return HiveRoles.CheckAdminUserPermissions();
591    }
592    #endregion
593  }
594}
Note: See TracBrowser for help on using the repository browser.