Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2877_HiveImprovements/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs @ 15613

Last change on this file since 15613 was 15613, checked in by swagner, 6 years ago

#2877: Improved groups/slaves treeview of Hive Administrator

  • slaves and slave groups are sorted by name
  • slave state is indicated by different colors
  • number of contained slaves is shown for each group
  • CPU utilization is shown for each calculating slave
  • last heartbeat date is shown for each offline slave
File size: 21.7 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.Drawing;
25using System.Linq;
26using System.ServiceModel.Security;
27using System.Threading;
28using System.Threading.Tasks;
29using System.Windows.Forms;
30using HeuristicLab.Clients.Access;
31using HeuristicLab.Clients.Hive.Views;
32using HeuristicLab.Core;
33using HeuristicLab.Core.Views;
34using HeuristicLab.MainForm;
35using TS = System.Threading.Tasks;
36
37namespace HeuristicLab.Clients.Hive.Administrator.Views {
38  [View("Resources View")]
39  [Content(typeof(IItemList<Resource>), false)]
40  public partial class ResourcesView : ItemView, IDisposable {
41    public new IItemList<Resource> Content {
42      get { return (IItemList<Resource>)base.Content; }
43      set { base.Content = value; }
44    }
45
46    public const string UngroupedGroupName = "UNGROUPED";
47    private const int slaveImageIndex = 0;
48    private const int slaveGroupImageIndex = 1;
49    private readonly Color ownedResourceColor = Color.LightGreen;
50    private readonly Color calculatingColor = Color.DarkGreen;
51    private readonly Color offlineColor = Color.Red;
52    private TS.Task progressTask;
53    private bool stopProgressTask;
54    private bool currentlyAuthorized;
55
56
57    public ResourcesView() {
58      InitializeComponent();
59      treeSlaveGroup.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.MonitorLarge);
60      treeSlaveGroup.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.NetworkCenterLarge);
61
62      HiveAdminClient.Instance.Refreshing += new EventHandler(Instance_Refreshing);
63      HiveAdminClient.Instance.Refreshed += new EventHandler(Instance_Refreshed);
64
65      Access.AccessClient.Instance.Refreshing += new EventHandler(AccessClient_Refreshing);
66      Access.AccessClient.Instance.Refreshed += new EventHandler(AccessClient_Refreshed);
67    }
68
69    private void UpdateProgress() {
70      while (!stopProgressTask) {
71        int diff = (progressBar.Maximum - progressBar.Minimum) / 10;
72
73        if (progressBar.InvokeRequired) {
74          progressBar.Invoke(new Action(delegate() { progressBar.Value = (progressBar.Value + diff) % progressBar.Maximum; }));
75        } else {
76          progressBar.Value = (progressBar.Value + diff) % progressBar.Maximum;
77        }
78
79        //ok, this is not very clever...
80        Thread.Sleep(500);
81      }
82      if (progressBar.InvokeRequired) {
83        progressBar.Invoke(new Action(delegate() { progressBar.Value = progressBar.Minimum; }));
84      } else {
85        progressBar.Value = progressBar.Minimum;
86      }
87    }
88
89    void Instance_Refreshing(object sender, EventArgs e) {
90      stopProgressTask = false;
91      progressTask = new TS.Task(UpdateProgress);
92      progressTask.Start();
93      SetEnabledStateOfControls();
94    }
95
96    void Instance_Refreshed(object sender, EventArgs e) {
97      stopProgressTask = true;
98      SetEnabledStateOfControls();
99    }
100
101    void AccessClient_Refreshing(object sender, EventArgs e) {
102      stopProgressTask = false;
103      progressTask = new TS.Task(UpdateProgress);
104      progressTask.Start();
105      SetEnabledStateOfControls();
106      btnPermissionsSave.Enabled = false;
107    }
108
109    void AccessClient_Refreshed(object sender, EventArgs e) {
110      stopProgressTask = true;
111      SetEnabledStateOfControls();
112    }
113
114    #region Register Content Events
115    protected override void DeregisterContentEvents() {
116      Content.ItemsAdded -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsAdded);
117      Content.ItemsRemoved -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsRemoved);
118      base.DeregisterContentEvents();
119    }
120    protected override void RegisterContentEvents() {
121      base.RegisterContentEvents();
122      Content.ItemsAdded += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsAdded);
123      Content.ItemsRemoved += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Resource>>(Content_ItemsRemoved);
124    }
125    #endregion
126
127    protected override void OnContentChanged() {
128      base.OnContentChanged();
129      if (Content == null) {
130        slaveView.Content = null;
131        scheduleView.Content = null;
132        permissionView.Content = null;
133        permissionView.FetchSelectedUsers = null;
134        treeSlaveGroup.Nodes.Clear();
135      } else {
136        permissionView.Content = Access.AccessClient.Instance;
137        treeSlaveGroup.Nodes.Clear();
138
139        //rebuild
140        var ungroupedSlaves = new SlaveGroup {
141          Id = Guid.NewGuid(),
142          Name = UngroupedGroupName,
143          Description = "Slaves without a group"
144        };
145        var ungroupedSlavesCount = 0;
146        var ungroupedNode = CreateTreeNodeFromResource(ungroupedSlaves);
147
148        var parentlessResources = from r in Content
149                                  where r.ParentResourceId == null
150                                  orderby r.Name
151                                  select r;
152        foreach (Resource r in parentlessResources) {
153          if (r.GetType() == typeof(SlaveGroup)) {  // root node
154            var root = CreateTreeNodeFromResource(r);
155            treeSlaveGroup.Nodes.Add(root);
156            BuildSlaveGroupTree((SlaveGroup)r, root);
157          } else if (r.GetType() == typeof(Slave)) {  // ungrouped slaves
158            ungroupedNode.Nodes.Add(CreateTreeNodeFromResource(r));
159            ungroupedSlavesCount++;
160          }
161        }
162        if (ungroupedSlavesCount > 0)
163          ungroupedNode.Text += "   [" + ungroupedSlavesCount.ToString() + "]";
164        treeSlaveGroup.Nodes.Add(ungroupedNode);
165      }
166    }
167
168    private void BuildSlaveGroupTree(SlaveGroup group, TreeNode node) {
169      var childGroups = from r in Content.OfType<SlaveGroup>()
170                        where r.ParentResourceId == @group.Id
171                        orderby r.Name
172                        select r;
173      foreach (var g in childGroups) {
174        var childNode = CreateTreeNodeFromResource(g);
175        node.Nodes.Add(childNode);
176        BuildSlaveGroupTree(g, childNode);
177      }
178
179      var childSlaves = from r in Content.OfType<Slave>()
180                        where r.ParentResourceId == @group.Id
181                        orderby r.Name
182                        select r;
183      foreach (var s in childSlaves)
184        node.Nodes.Add(CreateTreeNodeFromResource(s));
185      var childSlavesCount = childSlaves.Count();
186      if (childSlavesCount > 0)
187        node.Text += "   [" + childSlavesCount.ToString() + "]";
188    }
189
190    private TreeNode CreateTreeNodeFromResource(Resource r) {
191      var node = new TreeNode();
192      node.Text = r.Name;
193
194      if (r is SlaveGroup) {
195        node.ImageIndex = slaveGroupImageIndex;
196      } else if (r is Slave) {
197        var s = r as Slave;
198        node.ImageIndex = slaveImageIndex;
199        if (s.SlaveState == SlaveState.Calculating) {
200          node.ForeColor = calculatingColor;
201          node.Text += "   [" + s.CpuUtilization.ToString("N2") + " %]";
202        } else if (s.SlaveState == SlaveState.Offline) {
203          node.ForeColor = offlineColor;
204          if (s.LastHeartbeat.HasValue)
205            node.Text += "   [" + s.LastHeartbeat?.ToString("g") + "]";
206        }
207      }
208      node.SelectedImageIndex = node.ImageIndex;
209
210      if (r.OwnerUserId == Access.UserInformation.Instance.User.Id) node.BackColor = ownedResourceColor;
211
212      node.Tag = r;
213      return node;
214    }
215
216    protected override void SetEnabledStateOfControls() {
217      base.SetEnabledStateOfControls();
218      if (Content == null) {
219        btnAddGroup.Enabled = false;
220        btnRemoveGroup.Enabled = false;
221        btnSave.Enabled = false;
222        btnPermissionsSave.Enabled = false;
223        permissionView.Enabled = false;
224        scheduleView.SetEnabledStateOfSchedule(false);
225        btnPermissionsSave.Enabled = false;
226        permissionView.Enabled = false;
227      } else {
228        btnAddGroup.Enabled = true;
229        btnRemoveGroup.Enabled = true;
230        btnSave.Enabled = true;
231        scheduleView.SetEnabledStateOfSchedule(IsAuthorized(slaveView.Content));
232        btnPermissionsSave.Enabled = permissionView.FetchSelectedUsers != null;
233        permissionView.Enabled = permissionView.FetchSelectedUsers != null;
234      }
235    }
236
237    private bool IsAuthorized(Resource resource) {
238      return resource != null
239          && resource.Name != UngroupedGroupName
240          && resource.Id != Guid.Empty
241          && UserInformation.Instance.UserExists
242          && (resource.OwnerUserId == UserInformation.Instance.User.Id || HiveRoles.CheckAdminUserPermissions());
243    }
244
245    private void treeSlaveGroup_AfterSelect(object sender, TreeViewEventArgs e) {
246      if (e.Action != TreeViewAction.Unknown) {
247        Resource selectedResource = ((Resource)e.Node.Tag);
248        currentlyAuthorized = IsAuthorized(selectedResource);
249        if (currentlyAuthorized) {
250          permissionView.FetchSelectedUsers = new Func<List<Guid>>(() => {
251            return HiveServiceLocator.Instance.CallHiveService<List<ResourcePermission>>(service => {
252              return service.GetResourcePermissions(selectedResource.Id);
253            }).Select(x => x.GrantedUserId).ToList();
254          });
255          if (!tabSlaveGroup.TabPages.Contains(tabPermissions)) tabSlaveGroup.TabPages.Add(tabPermissions);
256        } else {
257          permissionView.FetchSelectedUsers = null;
258          btnPermissionsSave.Enabled = false;
259          if (selectedResource.Id == Guid.Empty) {
260            if (!tabSlaveGroup.TabPages.Contains(tabPermissions)) tabSlaveGroup.TabPages.Add(tabPermissions);
261          } else tabSlaveGroup.TabPages.Remove(tabPermissions);
262        }
263
264        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
265          slaveView.Content.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
266        }
267
268        slaveView.Content = selectedResource;
269        HiveAdminClient.Instance.DowntimeForResourceId = selectedResource.Id;
270
271        if (selectedResource is SlaveGroup) {
272          slaveView.Content.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
273        }
274
275        if (tabSlaveGroup.SelectedIndex == 1) {
276          UpdateScheduleAsync();
277        } else if (tabSlaveGroup.SelectedIndex == 2) {
278          UpdatePermissionsAsync();
279        }
280      }
281    }
282
283    void SlaveViewContent_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
284      OnContentChanged();
285      if (e.PropertyName == "HbInterval") {
286        UpdateChildHbIntervall(slaveView.Content);
287      }
288    }
289
290    private void UpdateChildHbIntervall(Resource resource) {
291      foreach (Resource r in Content.Where(x => x.ParentResourceId == resource.Id)) {
292        r.HbInterval = resource.HbInterval;
293        if (r is SlaveGroup) {
294          UpdateChildHbIntervall(r);
295        }
296      }
297    }
298
299    private void btnAddGroup_Click(object sender, EventArgs e) {
300      SlaveGroup newGroup = new SlaveGroup();
301      newGroup.Name = "New Group";
302      newGroup.OwnerUserId = UserInformation.Instance.User.Id;
303      Content.Add(newGroup);
304    }
305
306    void Content_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Resource>> e) {
307      OnContentChanged();
308    }
309
310    void Content_ItemsAdded(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Resource>> e) {
311      OnContentChanged();
312    }
313
314    private void btnRemoveGroup_Click(object sender, EventArgs e) {
315      if (treeSlaveGroup.SelectedNode != null && treeSlaveGroup.SelectedNode.Tag != null) {
316        Resource res = (Resource)treeSlaveGroup.SelectedNode.Tag;
317
318        DialogResult diagRes = MessageBox.Show("Do you really want to delete " + res.Name + "?", "HeuristicLab Hive Administrator", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
319        if (diagRes == DialogResult.Yes) {
320          if (res is Slave) {
321            Content.Remove(res);
322            HiveAdminClient.Delete(res);
323          } else if (res is SlaveGroup) {
324            //only delete empty groups
325            if (Content.Where(s => s.ParentResourceId == res.Id).Count() < 1) {
326              Content.Remove(res);
327              HiveAdminClient.Delete(res);
328            } else {
329              MessageBox.Show("Only empty groups can be deleted.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
330            }
331          }
332        }
333      }
334    }
335
336    private void btnSave_Click(object sender, EventArgs e) {
337      foreach (Resource res in Content) {
338        if (res is SlaveGroup && res.Id == Guid.Empty) {
339          SlaveGroup slaveGroup = (SlaveGroup)res;
340          slaveGroup.Store();
341        } else if (res.Id != Guid.Empty && res.Modified) {
342          res.Store();
343        }
344      }
345    }
346
347    private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
348      if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false)) {
349        Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
350        TreeNode destNode = ((TreeView)sender).GetNodeAt(pt);
351        TreeNode newNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
352
353        if (destNode.TreeView == newNode.TreeView) {
354          if (destNode.Text == UngroupedGroupName || (destNode.Parent != null && destNode.Parent.Text == UngroupedGroupName)) {
355            MessageBox.Show(string.Format("You can't drag items to the group \"{0}\".{1}This group only contains slaves which haven't yet been assigned to a real group.",
356              UngroupedGroupName, Environment.NewLine), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information);
357            return;
358          }
359
360          SlaveGroup sgrp = null;
361          TreeNode parentNode = null;
362          if (destNode.Tag != null && destNode.Tag is SlaveGroup) {
363            sgrp = (SlaveGroup)destNode.Tag;
364            parentNode = destNode;
365          } else if (destNode.Parent != null && destNode.Parent.Tag is SlaveGroup) {
366            sgrp = (SlaveGroup)destNode.Parent.Tag;
367            parentNode = destNode.Parent;
368          }
369
370          if (newNode.Tag is SlaveGroup && CheckParentsEqualsMovedNode(parentNode, newNode)) {
371            return;
372          }
373
374          SlaveGroup parent = (SlaveGroup)parentNode.Tag;
375
376          if (parent.OwnerUserId != null && !IsAuthorized(parent)) {
377            MessageBox.Show(string.Format("You don't have the permissions to drag items to the group \"{0}\".", ((Resource)parentNode.Tag).Name),
378              "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
379            return;
380          }
381
382          if (sgrp != null && newNode.Tag != null) {
383            //save parent group to get an id
384            if (sgrp.Id == Guid.Empty) {
385              sgrp.Store();
386            }
387
388            if (newNode.Tag is Slave) {
389              Slave slave = (Slave)newNode.Tag;
390              if (slave.ParentResourceId == null || (slave.ParentResourceId != null && slave.ParentResourceId != sgrp.Id)) {
391                slave.ParentResourceId = sgrp.Id;
392                newNode.Remove();
393                parentNode.Nodes.Clear();
394                BuildSlaveGroupTree(sgrp, parentNode);
395              }
396            } else if (newNode.Tag is SlaveGroup) {
397              SlaveGroup slaveGroup = (SlaveGroup)newNode.Tag;
398              if (slaveGroup.ParentResourceId == null || (slaveGroup.ParentResourceId != null && slaveGroup.ParentResourceId != sgrp.Id)) {
399                slaveGroup.ParentResourceId = sgrp.Id;
400                newNode.Remove();
401                parentNode.Nodes.Clear();
402                BuildSlaveGroupTree(sgrp, parentNode);
403              }
404            }
405          }
406        }
407      }
408    }
409
410    private bool CheckParentsEqualsMovedNode(TreeNode dest, TreeNode movedNode) {
411      TreeNode tmp = dest;
412
413      while (tmp != null) {
414        if (tmp == movedNode) {
415          return true;
416        }
417        tmp = tmp.Parent;
418      }
419      return false;
420    }
421
422    private void treeSlaveGroup_ItemDrag(object sender, ItemDragEventArgs e) {
423      TreeNode sourceNode = (TreeNode)e.Item;
424      if (IsAuthorized((Resource)sourceNode.Tag))
425        DoDragDrop(sourceNode, DragDropEffects.All);
426    }
427
428    private void treeSlaveGroup_DragEnter(object sender, DragEventArgs e) {
429      e.Effect = DragDropEffects.Move;
430    }
431
432    private void treeSlaveGroup_DragOver(object sender, DragEventArgs e) {
433      e.Effect = DragDropEffects.Move;
434    }
435
436    private void treeSlaveGroup_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
437      e.Action = DragAction.Continue;
438    }
439
440    void ResetView() {
441      if (this.InvokeRequired) {
442        Invoke(new Action(ResetView));
443      } else {
444        treeSlaveGroup.Nodes.Clear();
445
446        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
447          slaveView.Content.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(SlaveViewContent_PropertyChanged);
448        }
449        slaveView.Content = null;
450        if (scheduleView.Content != null) {
451          scheduleView.Content.Clear();
452        }
453        HiveAdminClient.Instance.ResetDowntime();
454      }
455    }
456
457    private void UpdateResources() {
458      ResetView();
459
460      try {
461        if (!Access.UserInformation.Instance.UserExists) {
462          //do a refresh just in case that the user has changed his usr and pwd in between
463          Access.UserInformation.Instance.Refresh();
464        }
465        HiveAdminClient.Instance.Refresh();
466        Content = HiveAdminClient.Instance.Resources;
467      }
468      catch (MessageSecurityException) {
469        ShowMessageSecurityException();
470      }
471      catch (AnonymousUserException) {
472        ShowHiveInformationDialog();
473      }
474    }
475
476    private void ShowMessageSecurityException() {
477      if (this.InvokeRequired) {
478        Invoke(new Action(ShowMessageSecurityException));
479      } else {
480        MessageBox.Show("A Message Security error has occured. This normally means that your user name or password is wrong.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
481      }
482    }
483
484    private void ShowHiveInformationDialog() {
485      if (this.InvokeRequired) {
486        Invoke(new Action(ShowHiveInformationDialog));
487      } else {
488        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
489          dialog.ShowDialog(this);
490        }
491      }
492    }
493
494    private void UpdateResourcesAsync() {
495      TS.Task.Factory.StartNew(UpdateResources).ContinueWith((t) => {
496        DisplayError(t.Exception);
497      }, TaskContinuationOptions.OnlyOnFaulted);
498    }
499
500    private void UpdateSchedule() {
501      HiveAdminClient.Instance.RefreshCalendar();
502      scheduleView.Invoke(new Action(() => {
503        scheduleView.Content = HiveAdminClient.Instance.Downtimes;
504        SetEnabledStateOfControls();
505      }));
506    }
507
508    private void UpdateScheduleAsync() {
509      TS.Task.Factory.StartNew(UpdateSchedule).ContinueWith((t) => {
510        DisplayError(t.Exception);
511      }, TaskContinuationOptions.OnlyOnFaulted);
512    }
513
514    private void UpdatePermissions() {
515      if (permissionView.Content != null && permissionView.FetchSelectedUsers != null)
516        permissionView.Invoke(new Action(() => permissionView.ManualRefresh()));
517    }
518
519    private void UpdatePermissionsAsync() {
520      TS.Task.Factory.StartNew(UpdatePermissions).ContinueWith((t) => {
521        DisplayError(t.Exception);
522      }, TaskContinuationOptions.OnlyOnFaulted);
523    }
524
525
526    private void DisplayError(Exception ex) {
527      MessageBox.Show(string.Format("An error occured while updating: {0} {1}", Environment.NewLine, ex.Message), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
528    }
529
530    private void tabSlaveGroup_SelectedIndexChanged(object sender, EventArgs e) {
531      if (tabSlaveGroup.SelectedIndex == 1) {
532        UpdateScheduleAsync();
533      } else if (tabSlaveGroup.SelectedIndex == 2) {
534        UpdatePermissionsAsync();
535      }
536    }
537
538    private void btnRefresh_Click(object sender, EventArgs e) {
539      UpdateResourcesAsync();
540    }
541
542    private void ResourcesView_Load(object sender, EventArgs e) {
543      UpdateResourcesAsync();
544    }
545
546    private void btnPermissionsSave_Click(object sender, EventArgs e) {
547      SetEnabledStateOfControls();
548      HiveServiceLocator.Instance.CallHiveService(service => {
549        service.GrantResourcePermissions(((Resource)treeSlaveGroup.SelectedNode.Tag).Id, permissionView.GetAddedUsers().Select(x => x.Id).ToList());
550        service.RevokeResourcePermissions(((Resource)treeSlaveGroup.SelectedNode.Tag).Id, permissionView.GetDeletedUsers().Select(x => x.Id).ToList());
551      });
552      SetEnabledStateOfControls();
553    }
554  }
555}
Note: See TracBrowser for help on using the repository browser.