Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2877: Improved groups/slaves treeview of Hive Administrator

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