source: trunk/HeuristicLab.Clients.Hive/3.3/HiveAdminClient.cs @ 16202

Last change on this file since 16202 was 16202, checked in by jzenisek, 12 months ago

#2839: fixed several bugs:

  • renamed tab "Slaves" to "Resources"
  • fixed bugs in job manager > resource selector reported by fholzinger
  • adapted client-side handling of disabled resource ancestors
  • adapted parentship check in HiveAdminClient
  • adapted seting of enabled state of controls for project details, project resources
  • introduced "inactive" tag in ProjectsView
File size: 20.4 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.Threading;
24using HeuristicLab.Common;
25using HeuristicLab.Core;
26using System.Collections.Generic;
27using System.Linq;
28using HeuristicLab.Clients.Access;
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<Job>> jobs;
74    public Dictionary<Guid, HiveItemCollection<Job>> Jobs {
75      get { return jobs; }
76      set {
77        if (value != jobs)
78          jobs = value;
79        }
80    }
81
82    private Dictionary<Guid, HashSet<Guid>> projectAncestors;
83    public Dictionary<Guid, HashSet<Guid>> ProjectAncestors {
84      get { return projectAncestors; }
85    }
86
87    private Dictionary<Guid, HashSet<Guid>> projectDescendants;
88    public Dictionary<Guid, HashSet<Guid>> ProjectDescendants {
89      get { return projectDescendants; }
90    }
91
92    private Dictionary<Guid, HashSet<Guid>> resourceAncestors;
93    public Dictionary<Guid, HashSet<Guid>> ResourceAncestors {
94      get { return resourceAncestors; }
95    }
96
97    private Dictionary<Guid, HashSet<Guid>> resourceDescendants;
98    public Dictionary<Guid, HashSet<Guid>> ResourceDescendants {
99      get { return resourceDescendants; }
100    }
101
102    private Dictionary<Guid, string> projectNames;
103    public Dictionary<Guid, string> ProjectNames {
104      get { return projectNames; }
105    }
106
107    private HashSet<Project> disabledParentProjects;
108    public HashSet<Project> DisabledParentProjects {
109      get { return disabledParentProjects; }
110    }
111
112    private Dictionary<Guid, string> resourceNames;
113    public Dictionary<Guid, string> ResourceNames {
114      get { return resourceNames; }
115    }
116
117    private HashSet<Resource> disabledParentResources;
118    public HashSet<Resource> DisabledParentResources {
119      get { return disabledParentResources; }
120    }
121    #endregion
122
123    #region Events
124    public event EventHandler Refreshing;
125    private void OnRefreshing() {
126      EventHandler handler = Refreshing;
127      if (handler != null) handler(this, EventArgs.Empty);
128    }
129    public event EventHandler Refreshed;
130    private void OnRefreshed() {
131      var handler = Refreshed;
132      if (handler != null) handler(this, EventArgs.Empty);
133    }
134    #endregion
135
136    private HiveAdminClient() { }
137
138    #region Refresh
139    public void Refresh() {
140      OnRefreshing();
141
142      try {
143        resources = new ItemList<Resource>();
144        projects = new ItemList<Project>();
145        projectResourceAssignments = new ItemList<AssignedProjectResource>();
146        jobs = new Dictionary<Guid, HiveItemCollection<Job>>();
147        projectNames = new Dictionary<Guid, string>();
148        resourceNames = new Dictionary<Guid, string>();
149
150        projectAncestors = new Dictionary<Guid, HashSet<Guid>>();
151        projectDescendants = new Dictionary<Guid, HashSet<Guid>>();
152        resourceAncestors = new Dictionary<Guid, HashSet<Guid>>();
153        resourceDescendants = new Dictionary<Guid, HashSet<Guid>>();
154
155        HiveServiceLocator.Instance.CallHiveService(service => {
156          service.GetSlaveGroupsForAdministration().ForEach(g => resources.Add(g));
157          service.GetSlavesForAdministration().ForEach(s => resources.Add(s));
158          service.GetProjectsForAdministration().ForEach(p => projects.Add(p));
159          var projectIds = projects.Select(p => p.Id).ToList();
160          if (projectIds.Any()) {
161            service.GetAssignedResourcesForProjectsAdministration(projectIds)
162              .ForEach(a => projectResourceAssignments.Add(a));
163            projectIds.ForEach(p => jobs.Add(p, new HiveItemCollection<Job>()));
164            var unsortedJobs = service.GetJobsByProjectIds(projectIds)
165              .OrderBy(x => x.DateCreated).ToList();
166
167            unsortedJobs.Where(j => j.State == JobState.DeletionPending).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
168            unsortedJobs.Where(j => j.State == JobState.StatisticsPending).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
169            unsortedJobs.Where(j => j.State == JobState.Online).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
170
171            projectNames = service.GetProjectNames();
172            resourceNames = service.GetResourceNames();
173          }
174        });
175
176        UpdateResourceGenealogy();
177        UpdateProjectGenealogy();
178        RefreshDisabledParentProjects();
179        RefreshDisabledParentResources();
180      }
181      catch {
182        throw;
183      }
184      finally {
185        OnRefreshed();
186      }
187    }
188
189    //public void UpdateResourceGenealogy(IItemList<Resource> resources) {
190    //  resourceAncestors.Clear();
191    //  resourceDescendants.Clear();
192
193    //  foreach (var r in resources) {
194    //    resourceAncestors.Add(r.Id, new HashSet<Resource>());
195    //    resourceDescendants.Add(r.Id, new HashSet<Resource>());
196    //  }
197
198    //  foreach (var r in resources) {
199    //    var parentResourceId = r.ParentResourceId;
200    //    while (parentResourceId != null) {
201    //      var parent = resources.SingleOrDefault(x => x.Id == parentResourceId);
202    //      if (parent != null) {
203    //        resourceAncestors[r.Id].Add(parent);
204    //        resourceDescendants[parent.Id].Add(r);
205    //        parentResourceId = parent.ParentResourceId;
206    //      } else {
207    //        parentResourceId = null;
208    //      }
209    //    }
210    //  }
211    //}
212
213    //public void UpdateProjectGenealogy(IItemList<Project> projects) {
214    //  projectAncestors.Clear();
215    //  projectDescendants.Clear();
216
217    //  foreach (var p in projects) {
218    //    projectAncestors.Add(p.Id, new HashSet<Project>());
219    //    projectDescendants.Add(p.Id, new HashSet<Project>());
220    //  }
221
222    //  foreach (var p in projects) {
223    //    var parentProjectId = p.ParentProjectId;
224    //    while (parentProjectId != null) {
225    //      var parent = projects.SingleOrDefault(x => x.Id == parentProjectId);
226    //      if (parent != null) {
227    //        projectAncestors[p.Id].Add(parent);
228    //        projectDescendants[parent.Id].Add(p);
229    //        parentProjectId = parent.ParentProjectId;
230    //      } else {
231    //        parentProjectId = null;
232    //      }
233    //    }
234    //  }
235    //}
236
237    private void UpdateResourceGenealogy() {
238      resourceAncestors.Clear();
239      resourceDescendants.Clear();
240
241      // fetch resource ancestor set
242      HiveServiceLocator.Instance.CallHiveService(service => {
243        var ra = service.GetResourceGenealogy();
244        ra.Keys.ToList().ForEach(k => resourceAncestors.Add(k, new HashSet<Guid>()));
245        resourceAncestors.Keys.ToList().ForEach(k => resourceAncestors[k].UnionWith(ra[k]));
246      });
247
248      // build resource descendant set
249      resourceAncestors.Keys.ToList().ForEach(k => resourceDescendants.Add(k, new HashSet<Guid>()));
250      foreach (var ra in resourceAncestors) {
251        foreach (var ancestor in ra.Value) {
252          resourceDescendants[ancestor].Add(ra.Key);
253        }
254      }
255    }
256
257    private void UpdateProjectGenealogy() {
258      projectAncestors.Clear();
259      projectDescendants.Clear();
260
261      // fetch project ancestor list
262      HiveServiceLocator.Instance.CallHiveService(service => {
263        var pa = service.GetProjectGenealogy();
264        pa.Keys.ToList().ForEach(k => projectAncestors.Add(k, new HashSet<Guid>()));
265        projectAncestors.Keys.ToList().ForEach(k => projectAncestors[k].UnionWith(pa[k]));
266      });
267
268      // build project descendant list
269      projectAncestors.Keys.ToList().ForEach(k => projectDescendants.Add(k, new HashSet<Guid>()));
270      foreach (var pa in projectAncestors) {
271        foreach (var ancestor in pa.Value) {
272          projectDescendants[ancestor].Add(pa.Key);
273        }
274      }
275    }
276
277    private void RefreshDisabledParentProjects() {
278      disabledParentProjects = new HashSet<Project>();
279
280      foreach (var pid in projects
281        .Where(x => x.ParentProjectId.HasValue)
282        .SelectMany(x => projectAncestors[x.Id]).Distinct()
283        .Where(x => !projects.Select(y => y.Id).Contains(x))) {
284        var p = new Project();
285        p.Id = pid;
286        p.ParentProjectId = projectAncestors[pid].FirstOrDefault();
287        p.Name = projectNames[pid];
288        disabledParentProjects.Add(p);
289      }
290    }
291
292    private void RefreshDisabledParentResources() {
293      disabledParentResources = new HashSet<Resource>();
294
295      foreach (var rid in resources
296        .Where(x => x.ParentResourceId.HasValue)
297        .SelectMany(x => resourceAncestors[x.Id]).Distinct()
298        .Where(x => !resources.Select(y => y.Id).Contains(x))) {
299        var r = new SlaveGroup();
300        r.Id = rid;
301        r.ParentResourceId = resourceAncestors[rid].FirstOrDefault();
302        r.Name = resourceNames[rid];
303        disabledParentResources.Add(r);
304      }
305    }
306
307    public void RefreshJobs() {
308      var projectIds = new List<Guid>();
309      jobs = new Dictionary<Guid, HiveItemCollection<Job>>();
310
311      HiveServiceLocator.Instance.CallHiveService(service => {
312        service.GetProjectsForAdministration().ForEach(p => projectIds.Add(p.Id));
313        if(projectIds.Any()) {
314          projectIds.ForEach(p => jobs.Add(p, new HiveItemCollection<Job>()));
315          var unsortedJobs = service.GetJobsByProjectIds(projectIds)
316            .OrderBy(x => x.DateCreated).ToList();
317          unsortedJobs.Where(j => j.State == JobState.DeletionPending).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
318          unsortedJobs.Where(j => j.State == JobState.StatisticsPending).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
319          unsortedJobs.Where(j => j.State == JobState.Online).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
320        }
321      });
322    }
323
324    public void SortJobs() {
325      for(int i = 0; i < jobs.Count; i++) {
326        var projectId = jobs.Keys.ElementAt(i);
327        var unsortedJobs = jobs.Values.ElementAt(i);
328
329        var sortedJobs = new HiveItemCollection<Job>();
330        sortedJobs.AddRange(unsortedJobs.Where(j => j.State == JobState.DeletionPending));
331        sortedJobs.AddRange(unsortedJobs.Where(j => j.State == JobState.StatisticsPending));
332        sortedJobs.AddRange(unsortedJobs.Where(j => j.State == JobState.Online));
333
334        jobs[projectId] = sortedJobs;
335      }
336    }
337
338    #endregion
339
340    #region Refresh downtime calendar
341    public void RefreshCalendar() {
342      if (downtimeForResourceId != null && downtimeForResourceId != Guid.Empty) {
343        OnRefreshing();
344
345        try {
346          downtimes = new ItemList<Downtime>();
347
348          HiveServiceLocator.Instance.CallHiveService(service => {
349            service.GetDowntimesForResource(downtimeForResourceId).ForEach(d => downtimes.Add(d));
350          });
351        }
352        catch {
353          throw;
354        }
355        finally {
356          OnRefreshed();
357        }
358      }
359    }
360    #endregion
361
362    #region Store
363    public static void Store(IHiveItem item, CancellationToken cancellationToken) {
364      if (item.Id == Guid.Empty) {
365        if (item is SlaveGroup) {
366          item.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddSlaveGroup((SlaveGroup)item));
367        }
368        if (item is Slave) {
369          item.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddSlave((Slave)item));
370        }
371        if (item is Downtime) {
372          item.Id = HiveServiceLocator.Instance.CallHiveService((s) => s.AddDowntime((Downtime)item));
373        }
374        if (item is Project) {
375          item.Id = HiveServiceLocator.Instance.CallHiveService(s => s.AddProject((Project)item));
376        }
377      } else {
378        if (item is SlaveGroup) {
379          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateSlaveGroup((SlaveGroup)item));
380        }
381        if (item is Slave) {
382          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateSlave((Slave)item));
383        }
384        if (item is Downtime) {
385          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateDowntime((Downtime)item));
386        }
387        if (item is Project) {
388          HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateProject((Project)item));
389        }
390      }
391    }
392    #endregion
393
394    #region Delete
395    public static void Delete(IHiveItem item) {
396      if (item is SlaveGroup) {
397        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteSlaveGroup(item.Id));
398      } else if (item is Slave) {
399        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteSlave(item.Id));
400      } else if (item is Downtime) {
401        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteDowntime(item.Id));
402      } else if (item is Project) {
403        HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteProject(item.Id));
404      }
405    }
406
407    public static void DeleteJobs(List<Guid> jobIds) {
408
409      HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateJobStates(jobIds, JobState.StatisticsPending));
410    }
411    #endregion
412
413    public void ResetDowntime() {
414      downtimeForResourceId = Guid.Empty;
415      if (downtimes != null) {
416        downtimes.Clear();
417      }
418    }
419
420    #region Helper
421    public IEnumerable<Project> GetAvailableProjectAncestors(Guid id) {
422      if (projectAncestors.ContainsKey(id)) return projects.Where(x => projectAncestors[id].Contains(x.Id));
423      else return Enumerable.Empty<Project>();
424    }
425
426    public IEnumerable<Project> GetAvailableProjectDescendants(Guid id) {
427      if(projectDescendants.ContainsKey(id)) return projects.Where(x => projectDescendants[id].Contains(x.Id));
428      else return Enumerable.Empty<Project>();
429    }
430
431    public IEnumerable<Resource> GetAvailableResourceAncestors(Guid id) {
432      if (resourceAncestors.ContainsKey(id)) return resources.Where(x => resourceAncestors[id].Contains(x.Id));
433      else return Enumerable.Empty<Resource>();
434    }
435
436    public IEnumerable<Resource> GetAvailableResourceDescendants(Guid id) {
437      if (resourceDescendants.ContainsKey(id)) return resources.Where(x => resourceDescendants[id].Contains(x.Id));
438      else return Enumerable.Empty<Resource>();
439    }
440
441    public IEnumerable<Resource> GetDisabledResourceAncestors(IEnumerable<Resource> availableResources) {
442      var missingParentIds = availableResources
443        .Where(x => x.ParentResourceId.HasValue)
444        .SelectMany(x => resourceAncestors[x.Id]).Distinct()
445        .Where(x => !availableResources.Select(y => y.Id).Contains(x));
446
447      return resources.OfType<SlaveGroup>().Union(disabledParentResources).Where(x => missingParentIds.Contains(x.Id));
448    }
449
450    public bool CheckAccessToAdminAreaGranted() {
451      if(projects != null) {
452        return projects.Count > 0;
453      } else {
454        bool accessGranted = false;
455        HiveServiceLocator.Instance.CallHiveService(s => {
456          accessGranted = s.CheckAccessToAdminAreaGranted();
457        });
458        return accessGranted;
459      }
460    }
461
462    public bool CheckOwnershipOfResource(Resource res, Guid userId) {
463      if (res == null || userId == Guid.Empty) return false;
464
465      if (res.OwnerUserId == userId) {
466        return true;
467      } else if(resourceAncestors.ContainsKey(res.Id)) {
468        return GetAvailableResourceAncestors(res.Id).Where(x => x.OwnerUserId == userId).Any();
469      }
470
471      return false;
472    }
473
474    public bool CheckOwnershipOfProject(Project pro, Guid userId) {
475      if (pro == null || userId == Guid.Empty) return false;
476
477      if (pro.OwnerUserId == userId) {
478        return true;
479      } else if (projectAncestors.ContainsKey(pro.Id)) {
480        return GetAvailableProjectAncestors(pro.Id).Where(x => x.OwnerUserId == userId).Any();
481      }
482
483      return false;
484    }
485
486    public bool CheckOwnershipOfParentProject(Project pro, Guid userId) {
487      if (pro == null || userId == Guid.Empty) return false;
488
489      if(projectAncestors.ContainsKey(pro.Id)) {
490        return GetAvailableProjectAncestors(pro.Id).Any(x => x.OwnerUserId == userId);
491      }
492
493      if (pro.ParentProjectId != null && pro.ParentProjectId != Guid.Empty) {
494        var parent = projects.FirstOrDefault(x => x.Id == pro.ParentProjectId.Value);
495        if (parent != null)
496          return parent.OwnerUserId == userId || GetAvailableProjectAncestors(parent.Id).Any(x => x.OwnerUserId == userId);
497      }
498
499      return false;
500    }
501
502    public bool CheckParentChange(Project child, Project parent) {
503      bool changePossible = true;
504
505      // change is not possible...
506      // ... if the moved project is null
507      // ... or the new parent is not stored yet
508      // ... or there is not parental change
509      if (child == null
510          || (parent != null && parent.Id == Guid.Empty)
511          || (parent != null && parent.Id == child.ParentProjectId)) {
512        changePossible = false;
513      } else if (parent == null && !IsAdmin()) {
514        // ... if parent is null, but user is no admin (only admins are allowed to create root projects)
515        changePossible = false;
516      } else if (parent != null && (!IsAdmin() && parent.OwnerUserId != UserInformation.Instance.User.Id && !CheckOwnershipOfParentProject(parent, UserInformation.Instance.User.Id))) {
517        // ... if the user is no admin nor owner of the new parent or grand..grandparents
518        changePossible = false;
519      } else if(parent != null && projectDescendants.ContainsKey(child.Id)) {
520        // ... if the new parent is among the moved project's descendants
521        changePossible = !GetAvailableProjectDescendants(child.Id).Where(x => x.Id == parent.Id).Any();
522      }
523
524      return changePossible;
525    }
526
527    public bool CheckParentChange(Resource child, Resource parent) {
528      bool changePossible = true;
529
530      // change is not possisble...
531      // ... if the child resource is null
532      // ... or the child resource equals the parent
533      // ... or the new parent is not stored yet
534      // ... or the new parent is a slave
535      // ... or there is not parental change
536      if (child == null 
537        || child == parent
538        || (parent != null && parent.Id == Guid.Empty)
539        || (parent != null && parent is Slave)
540        || (parent != null && parent.Id == child.ParentResourceId)) {
541        changePossible = false;
542      } else if (parent != null && resourceDescendants.ContainsKey(child.Id)) {
543        // ... or if the new parent is among the moved resource's descendants
544        changePossible = !GetAvailableResourceDescendants(child.Id).Where(x => x.Id == parent.Id).Any();
545      }
546
547      return changePossible;
548    }
549
550    private bool IsAdmin() {
551      return HiveRoles.CheckAdminUserPermissions();
552    }
553    #endregion
554  }
555}
Note: See TracBrowser for help on using the repository browser.