[7281] | 1 | using System;
|
---|
[7670] | 2 | using System.Collections.Generic;
|
---|
[7317] | 3 | using System.Diagnostics;
|
---|
[7670] | 4 | using System.Threading.Tasks;
|
---|
[7317] | 5 | using System.Timers;
|
---|
[7281] | 6 | using System.Windows.Forms;
|
---|
[7317] | 7 | using HeuristicLab.Clients.Hive.CloudManager.Model;
|
---|
[7389] | 8 | using HeuristicLab.Common;
|
---|
[7317] | 9 | using HeuristicLab.Core;
|
---|
| 10 | using HeuristicLab.Core.Views;
|
---|
| 11 | using HeuristicLab.MainForm;
|
---|
[7670] | 12 | using HeuristicLab.MainForm.WindowsForms;
|
---|
| 13 | using HeuristicLab.PluginInfrastructure;
|
---|
[7281] | 14 |
|
---|
| 15 | namespace HeuristicLab.Clients.Hive.CloudManager.Views {
|
---|
[7317] | 16 | [View("Cloud Resources View")]
|
---|
| 17 | [Content(typeof(IItemList<Subscription>), IsDefaultView = true)]
|
---|
| 18 | public partial class CloudResourcesView : ItemView, IDisposable {
|
---|
[7670] | 19 | ProgressView progressView;
|
---|
[7317] | 20 | private System.Timers.Timer timer = new System.Timers.Timer();
|
---|
| 21 | private int updateInterval = 15000;
|
---|
| 22 | private DateTime dueTime;
|
---|
[7387] | 23 |
|
---|
[7324] | 24 | private const int subscriptionImageIndex = 0;
|
---|
| 25 | private const int serviceImageIndex = 1;
|
---|
[7387] | 26 | private const int deploymentImageIndex = 2;
|
---|
[7317] | 27 |
|
---|
| 28 | public new IItemList<Subscription> Content {
|
---|
| 29 | get { return (IItemList<Subscription>)base.Content; }
|
---|
| 30 | set { base.Content = value; }
|
---|
| 31 | }
|
---|
| 32 |
|
---|
[7387] | 33 | private IItemList<HostedService> hostedServices;
|
---|
| 34 | public IItemList<HostedService> HostedServices {
|
---|
| 35 | get { return hostedServices; }
|
---|
| 36 | set { hostedServices = value; }
|
---|
| 37 | }
|
---|
| 38 |
|
---|
[7281] | 39 | public CloudResourcesView() {
|
---|
| 40 | InitializeComponent();
|
---|
[7317] | 41 |
|
---|
[7387] | 42 | treeCloudResources.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.NewFolder);
|
---|
[7324] | 43 | treeCloudResources.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.NetworkCenterLarge);
|
---|
| 44 | treeCloudResources.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.MonitorLarge);
|
---|
| 45 |
|
---|
[7387] | 46 | HostedServices = CloudManagerClient.Instance.HostedServices;
|
---|
[7324] | 47 | Content = CloudManagerClient.Instance.Subscriptions;
|
---|
[7387] | 48 |
|
---|
| 49 |
|
---|
[7317] | 50 | CloudManagerClient.Instance.Refreshing += new EventHandler(Instance_Refreshing);
|
---|
| 51 | CloudManagerClient.Instance.Refreshed += new EventHandler(Instance_Refreshed);
|
---|
| 52 |
|
---|
| 53 | timer.AutoReset = true;
|
---|
| 54 | timer.Interval = 1000;
|
---|
| 55 | timer.Elapsed += PerformUpdate;
|
---|
| 56 | timer.Start();
|
---|
| 57 | dueTime = DateTime.Now.AddMilliseconds(updateInterval);
|
---|
[7668] | 58 |
|
---|
[7670] | 59 | //IProgress progress = new Progress();
|
---|
| 60 | //progress.Status = "Restore Subscriptions";
|
---|
| 61 | //progressView = new ProgressView(this, progress);
|
---|
| 62 | //CloudManagerClient.Instance.RestoreSubscriptionsFromUserConfig();
|
---|
| 63 | //progressView.Finish();
|
---|
[7281] | 64 | }
|
---|
[7317] | 65 |
|
---|
[7324] | 66 | #region Register Content Events
|
---|
| 67 | protected override void DeregisterContentEvents() {
|
---|
| 68 | Content.ItemsAdded -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Subscription>>(Content_ItemsAdded);
|
---|
| 69 | Content.ItemsRemoved -= new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Subscription>>(Content_ItemsRemoved);
|
---|
| 70 | base.DeregisterContentEvents();
|
---|
| 71 | }
|
---|
| 72 | protected override void RegisterContentEvents() {
|
---|
| 73 | base.RegisterContentEvents();
|
---|
| 74 | Content.ItemsAdded += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Subscription>>(Content_ItemsAdded);
|
---|
| 75 | Content.ItemsRemoved += new Collections.CollectionItemsChangedEventHandler<Collections.IndexedItem<Subscription>>(Content_ItemsRemoved);
|
---|
| 76 | }
|
---|
| 77 | #endregion
|
---|
| 78 |
|
---|
[7387] | 79 | /// <summary>
|
---|
| 80 | /// Clean up any resources being used.
|
---|
| 81 | /// </summary>
|
---|
| 82 | /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
---|
| 83 | protected override void Dispose(bool disposing) {
|
---|
| 84 | if (disposing && (components != null)) {
|
---|
| 85 | components.Dispose();
|
---|
| 86 | CloudManagerClient.Instance.Refreshing -= new EventHandler(Instance_Refreshing);
|
---|
| 87 | CloudManagerClient.Instance.Refreshed -= new EventHandler(Instance_Refreshed);
|
---|
| 88 | timer.Stop();
|
---|
| 89 | timer.Dispose();
|
---|
| 90 | timer = null;
|
---|
| 91 | }
|
---|
[7655] | 92 | CloudManagerClient.Instance.PersistSubscriptionsToUserConfig();
|
---|
[7387] | 93 | base.Dispose(disposing);
|
---|
| 94 | }
|
---|
| 95 |
|
---|
[7670] | 96 | private void SetProgressView(IProgress progress) {
|
---|
| 97 | if (InvokeRequired) {
|
---|
| 98 | Invoke(new Action<IProgress>(SetProgressView), progress);
|
---|
| 99 | } else {
|
---|
| 100 | if (progressView == null) {
|
---|
| 101 | progressView = new ProgressView(this, progress);
|
---|
| 102 | } else {
|
---|
| 103 | progressView.Progress = progress;
|
---|
| 104 | }
|
---|
| 105 | }
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | private void FinishProgressView() {
|
---|
| 109 | if (InvokeRequired) {
|
---|
| 110 | Invoke(new Action(FinishProgressView));
|
---|
| 111 | } else {
|
---|
| 112 | if (progressView != null) {
|
---|
| 113 | progressView.Finish();
|
---|
| 114 | progressView = null;
|
---|
| 115 | SetEnabledStateOfControls();
|
---|
| 116 | }
|
---|
| 117 | }
|
---|
| 118 | }
|
---|
| 119 |
|
---|
| 120 | public void Restore() {
|
---|
| 121 | var task = System.Threading.Tasks.Task.Factory.StartNew(RestoreAsync);
|
---|
| 122 | task.ContinueWith((t) => {
|
---|
| 123 | FinishProgressView();
|
---|
| 124 | ErrorHandling.ShowErrorDialog(this, "An error occured while restoring Azure Subscriptions.", t.Exception);
|
---|
| 125 | }, TaskContinuationOptions.OnlyOnFaulted);
|
---|
| 126 | FinishProgressView();
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | private void RestoreAsync() {
|
---|
| 130 | IProgress progress = new Progress();
|
---|
| 131 | progress.Status = "Restoring Azure Subscriptions";
|
---|
| 132 | SetProgressView(progress);
|
---|
| 133 | List<Subscription> result = CloudManagerClient.Instance.RestoreSubscriptionsFromUserConfig();
|
---|
| 134 | FinishProgressView();
|
---|
| 135 | foreach (Subscription s in result) this.BeginInvoke((MethodInvoker)delegate() { CloudManagerClient.Instance.Add(s); });
|
---|
| 136 | }
|
---|
| 137 |
|
---|
[7324] | 138 | protected override void SetEnabledStateOfControls() {
|
---|
| 139 | base.SetEnabledStateOfControls();
|
---|
| 140 | if (Content == null || Content.Count == 0) {
|
---|
[7670] | 141 | if (this.InvokeRequired) {
|
---|
| 142 | this.BeginInvoke((MethodInvoker)delegate() { SetBtnControlsEnabled(false); });
|
---|
| 143 | } else {
|
---|
| 144 | SetBtnControlsEnabled(false);
|
---|
| 145 | }
|
---|
[7324] | 146 | } else {
|
---|
[7670] | 147 | if (this.InvokeRequired) {
|
---|
| 148 | this.BeginInvoke((MethodInvoker)delegate() { SetBtnControlsEnabled(true); });
|
---|
| 149 | } else {
|
---|
| 150 | SetBtnControlsEnabled(true);
|
---|
| 151 | }
|
---|
[7324] | 152 | }
|
---|
| 153 | }
|
---|
| 154 |
|
---|
[7670] | 155 | private void SetBtnControlsEnabled(bool enabled) {
|
---|
| 156 | btnAddSlaveService.Enabled = enabled;
|
---|
| 157 | btnDelete.Enabled = enabled;
|
---|
| 158 | btnSave.Enabled = enabled;
|
---|
| 159 | }
|
---|
| 160 |
|
---|
[7324] | 161 | protected override void OnContentChanged() {
|
---|
| 162 | base.OnContentChanged();
|
---|
| 163 | SetEnabledStateOfControls();
|
---|
| 164 | if (Content == null) {
|
---|
[7403] | 165 | viewHost.Content = null;
|
---|
[7324] | 166 | treeCloudResources.Nodes.Clear();
|
---|
[7433] | 167 | } else if (treeCloudResources.Nodes.Count == 0) {
|
---|
[7387] | 168 | treeCloudResources.BeginUpdate();
|
---|
[7433] | 169 | //treeCloudResources.Nodes.Clear();
|
---|
[7324] | 170 | foreach (Subscription sub in Content) {
|
---|
[7433] | 171 | TreeNode nodeSub = GetTreeNode(sub, sub.SubscriptionName, subscriptionImageIndex);
|
---|
[7387] | 172 | foreach (HostedService s in HostedServices) {
|
---|
[7403] | 173 | if (s.Subscription.SubscriptionID == sub.SubscriptionID) {
|
---|
[7433] | 174 | TreeNode nodeServ = GetTreeNode(s, s.HostedServiceProperties.Label, serviceImageIndex);
|
---|
[7387] | 175 | foreach (Deployment d in s.Deployments) {
|
---|
[7433] | 176 | TreeNode nodeDepl = GetTreeNode(d, d.Label + " (" + d.DeploymentSlot + ")", deploymentImageIndex);
|
---|
[7387] | 177 | nodeServ.Nodes.Add(nodeDepl);
|
---|
| 178 | }
|
---|
| 179 | nodeSub.Nodes.Add(nodeServ);
|
---|
| 180 | }
|
---|
| 181 | }
|
---|
| 182 | treeCloudResources.Nodes.Add(nodeSub);
|
---|
[7324] | 183 | }
|
---|
[7387] | 184 | treeCloudResources.EndUpdate();
|
---|
[7433] | 185 | } else {
|
---|
| 186 | treeCloudResources.BeginUpdate();
|
---|
| 187 | TreeNodeCollection tncSubs = treeCloudResources.Nodes;
|
---|
| 188 |
|
---|
| 189 | // Delete treenodes that have no corresponding data in Content and HosteServices
|
---|
| 190 | foreach (TreeNode tnSub in tncSubs) {
|
---|
| 191 | Subscription s = (Subscription)tnSub.Tag;
|
---|
| 192 | int idx = Content.IndexOf(s);
|
---|
| 193 | if (idx == -1) {
|
---|
| 194 | tncSubs.Remove(tnSub);
|
---|
| 195 | } else {
|
---|
| 196 | TreeNodeCollection tncHS = tnSub.Nodes;
|
---|
| 197 | foreach (TreeNode tnHS in tncHS) {
|
---|
| 198 | HostedService hs = (HostedService)tnHS.Tag;
|
---|
| 199 | int idxHS = HostedServices.IndexOf(hs);
|
---|
| 200 | if (idxHS == -1) {
|
---|
| 201 | tncHS.Remove(tnHS);
|
---|
| 202 | } else {
|
---|
| 203 | TreeNodeCollection tncDep = tnHS.Nodes;
|
---|
| 204 | foreach (TreeNode tnDep in tncDep) {
|
---|
| 205 | Deployment dep = (Deployment)tnDep.Tag;
|
---|
| 206 | HostedService hsDep = HostedServices[idxHS];
|
---|
| 207 | int idxDep = hsDep.Deployments.IndexOf(dep);
|
---|
| 208 | if (idxDep == -1) {
|
---|
| 209 | tncDep.Remove(tnDep);
|
---|
| 210 | }
|
---|
| 211 | }
|
---|
| 212 | }
|
---|
| 213 | }
|
---|
| 214 | }
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | // Add missing treenodes
|
---|
| 218 | foreach (Subscription sub in Content) {
|
---|
| 219 | bool foundSub = false;
|
---|
| 220 | TreeNode foundSubNode = null;
|
---|
| 221 | foreach (TreeNode tnSub in treeCloudResources.Nodes) {
|
---|
| 222 | if (((Subscription)tnSub.Tag).Equals(sub)) {
|
---|
| 223 | foundSub = true;
|
---|
| 224 | foundSubNode = tnSub;
|
---|
| 225 | }
|
---|
| 226 | }
|
---|
| 227 | TreeNode nodeSub;
|
---|
| 228 | if (!foundSub) {
|
---|
| 229 | nodeSub = GetTreeNode(sub, sub.SubscriptionName, subscriptionImageIndex);
|
---|
| 230 | treeCloudResources.Nodes.Add(nodeSub);
|
---|
| 231 | } else {
|
---|
| 232 | nodeSub = foundSubNode;
|
---|
| 233 | }
|
---|
| 234 | foreach (HostedService s in HostedServices) {
|
---|
| 235 | if (s.Subscription.SubscriptionID == sub.SubscriptionID) {
|
---|
| 236 | bool foundHS = false;
|
---|
| 237 | TreeNode foundHSNode = null;
|
---|
| 238 | foreach (TreeNode tnHS in nodeSub.Nodes) {
|
---|
| 239 | if (((HostedService)tnHS.Tag).Equals(s)) {
|
---|
| 240 | foundHS = true;
|
---|
| 241 | foundHSNode = tnHS;
|
---|
| 242 | }
|
---|
| 243 | }
|
---|
| 244 | TreeNode nodeServ;
|
---|
| 245 | if (!foundHS) {
|
---|
| 246 | nodeServ = GetTreeNode(s, s.HostedServiceProperties.Label, serviceImageIndex);
|
---|
| 247 | nodeSub.Nodes.Add(nodeServ);
|
---|
| 248 | } else {
|
---|
| 249 | nodeServ = foundHSNode;
|
---|
| 250 | }
|
---|
| 251 | foreach (Deployment d in s.Deployments) {
|
---|
| 252 | bool foundDep = false;
|
---|
| 253 | TreeNode foundDepNode;
|
---|
| 254 | foreach (TreeNode tnDep in nodeServ.Nodes) {
|
---|
| 255 | if (((Deployment)tnDep.Tag).Equals(d)) {
|
---|
| 256 | foundDep = true;
|
---|
| 257 | foundDepNode = tnDep;
|
---|
| 258 | }
|
---|
| 259 | }
|
---|
| 260 | TreeNode nodeDepl;
|
---|
| 261 | if (!foundDep) {
|
---|
| 262 | nodeDepl = GetTreeNode(d, d.Label + " (" + d.DeploymentSlot + ")", deploymentImageIndex);
|
---|
| 263 | nodeServ.Nodes.Add(nodeDepl);
|
---|
| 264 | } else {
|
---|
| 265 | // nothing to do
|
---|
| 266 | }
|
---|
| 267 | }
|
---|
| 268 | }
|
---|
| 269 | }
|
---|
| 270 | }
|
---|
| 271 | // ----------------------------
|
---|
| 272 | treeCloudResources.EndUpdate();
|
---|
[7324] | 273 | }
|
---|
| 274 | }
|
---|
| 275 |
|
---|
[7433] | 276 | private TreeNode GetTreeNode(Object tag, string text, int imageIndex) {
|
---|
| 277 | TreeNode node = new TreeNode();
|
---|
| 278 | node.Tag = tag;
|
---|
| 279 | node.Text = text;
|
---|
| 280 | node.ImageIndex = imageIndex;
|
---|
| 281 | node.SelectedImageIndex = node.ImageIndex;
|
---|
| 282 | return node;
|
---|
| 283 | }
|
---|
| 284 |
|
---|
[7324] | 285 | private void Instance_Refreshing(object sender, EventArgs e) {
|
---|
[7317] | 286 | Debug.WriteLine("Instance_Refreshing");
|
---|
[7387] | 287 | //if (treeCloudResources.InvokeRequired) {
|
---|
| 288 | // treeCloudResources.Invoke((MethodInvoker)delegate { OnContentChanged(); });
|
---|
| 289 | //} else {
|
---|
| 290 | // OnContentChanged();
|
---|
| 291 | //}
|
---|
[7317] | 292 | }
|
---|
| 293 |
|
---|
[7324] | 294 | private void Instance_Refreshed(object sender, EventArgs e) {
|
---|
[7317] | 295 | Debug.WriteLine("Instance_Refreshed");
|
---|
[7387] | 296 | if (treeCloudResources.InvokeRequired) {
|
---|
| 297 | treeCloudResources.Invoke((MethodInvoker)delegate { OnContentChanged(); });
|
---|
| 298 | } else {
|
---|
| 299 | OnContentChanged();
|
---|
| 300 | }
|
---|
[7317] | 301 | }
|
---|
| 302 |
|
---|
[7324] | 303 | private void Content_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Subscription>> e) {
|
---|
| 304 | OnContentChanged();
|
---|
| 305 | }
|
---|
| 306 |
|
---|
| 307 | private void Content_ItemsAdded(object sender, Collections.CollectionItemsChangedEventArgs<Collections.IndexedItem<Subscription>> e) {
|
---|
| 308 | OnContentChanged();
|
---|
| 309 | }
|
---|
| 310 |
|
---|
[7317] | 311 | private void PerformUpdate(object sender, ElapsedEventArgs e) {
|
---|
| 312 | TimeSpan timeToRefresh = dueTime - DateTime.Now;
|
---|
| 313 |
|
---|
| 314 | if (timeToRefresh.Seconds >= 0) {
|
---|
| 315 | this.Invoke((MethodInvoker)delegate { lblRefresh.Text = "Done. " + timeToRefresh.Seconds + "s to next refresh."; });
|
---|
| 316 | } else {
|
---|
| 317 | timer.Stop();
|
---|
| 318 | this.Invoke((MethodInvoker)delegate { lblRefresh.Text = "Refreshing data ..."; });
|
---|
| 319 |
|
---|
[7387] | 320 | CloudManagerClient.Instance.Refresh();
|
---|
[7317] | 321 |
|
---|
| 322 | dueTime = DateTime.Now.AddMilliseconds(updateInterval);
|
---|
[7387] | 323 | if (timer != null) {
|
---|
| 324 | timer.Start();
|
---|
| 325 | }
|
---|
[7317] | 326 | }
|
---|
| 327 | }
|
---|
[7324] | 328 |
|
---|
| 329 | private void btnAddSubscription_Click(object sender, EventArgs e) {
|
---|
| 330 | using (var form = new AddSubscriptionDialog()) {
|
---|
| 331 | form.ShowDialog();
|
---|
| 332 | if (!form.ErrorOccured) {
|
---|
| 333 | if (form.Subscription != null) {
|
---|
| 334 | Subscription sub = form.Subscription;
|
---|
| 335 | CloudManagerClient.Instance.Add(sub);
|
---|
| 336 | }
|
---|
| 337 | }
|
---|
| 338 | }
|
---|
| 339 | }
|
---|
[7339] | 340 |
|
---|
| 341 | private void btnAddSlaveService_Click(object sender, EventArgs e) {
|
---|
[7389] | 342 | // When refreshing subscriptions in the dialog,
|
---|
| 343 | // discoverservices and safetoconfig properties are always set to false
|
---|
| 344 | // -> work on deep copy
|
---|
| 345 | Cloner cloner = new Cloner();
|
---|
| 346 | IItemList<Subscription> subscriptions = cloner.Clone(Content);
|
---|
| 347 | using (var form = new AddAzureServiceDialog(subscriptions)) {
|
---|
[7339] | 348 | form.ShowDialog();
|
---|
| 349 |
|
---|
| 350 | }
|
---|
| 351 | }
|
---|
[7389] | 352 |
|
---|
| 353 | private void treeCloudResources_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) {
|
---|
[7403] | 354 | viewHost.Content = (IContent)e.Node.Tag;
|
---|
[7389] | 355 | }
|
---|
[7441] | 356 |
|
---|
| 357 | private void btnSave_Click(object sender, EventArgs e) {
|
---|
| 358 | if (treeCloudResources.SelectedNode != null) {
|
---|
| 359 | TreeNode tn = treeCloudResources.SelectedNode;
|
---|
| 360 | TreeNode tnParent = tn.Parent;
|
---|
| 361 | Object obj = tn.Tag;
|
---|
| 362 | Object objParent = tnParent.Tag;
|
---|
| 363 | if (obj is Subscription) {
|
---|
| 364 | // CloudManagerClient.Instance.Save((Subscription)obj);
|
---|
| 365 | } else if (obj is HostedService) {
|
---|
| 366 | // nothing to do so far
|
---|
| 367 | } else if (obj is Deployment && objParent is HostedService) {
|
---|
| 368 | HostedService hs = (HostedService)objParent;
|
---|
| 369 | Deployment dep = (Deployment)obj;
|
---|
| 370 | //call async
|
---|
| 371 | CloudManagerClient.Instance.ChangeIntances(dep, hs);
|
---|
| 372 | }
|
---|
| 373 | }
|
---|
| 374 | }
|
---|
| 375 |
|
---|
| 376 | private void btnDelete_Click(object sender, EventArgs e) {
|
---|
| 377 | if (treeCloudResources.SelectedNode != null) {
|
---|
| 378 | TreeNode tn = treeCloudResources.SelectedNode;
|
---|
| 379 | TreeNode tnParent = tn.Parent;
|
---|
| 380 | Object obj = tn.Tag;
|
---|
| 381 | Object objParent = null;
|
---|
| 382 | if (tnParent != null) {
|
---|
| 383 | objParent = tnParent.Tag;
|
---|
| 384 | }
|
---|
| 385 | viewHost.Content = null;
|
---|
| 386 | if (obj is Subscription) {
|
---|
| 387 | CloudManagerClient.Instance.Delete((Subscription)obj);
|
---|
| 388 | } else if (obj is HostedService) {
|
---|
[7608] | 389 | HostedService hs = (HostedService)obj;
|
---|
| 390 | using (var form = new DeleteHostedServiceView(hs)) {
|
---|
| 391 | form.ShowDialog();
|
---|
| 392 | }
|
---|
[7441] | 393 | } else if (obj is Deployment && objParent is HostedService) {
|
---|
| 394 | HostedService hs = (HostedService)objParent;
|
---|
| 395 | Deployment dep = (Deployment)obj;
|
---|
[7598] | 396 | using (var form = new DeleteDeploymentView(dep, hs)) {
|
---|
| 397 | form.ShowDialog();
|
---|
| 398 | }
|
---|
[7441] | 399 | }
|
---|
| 400 | }
|
---|
| 401 | }
|
---|
[7281] | 402 | }
|
---|
| 403 | }
|
---|