mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-30 07:40:13 +01:00
455 lines
16 KiB
C#
455 lines
16 KiB
C#
|
/* Copyright (c) Citrix Systems Inc.
|
|||
|
* All rights reserved.
|
|||
|
*
|
|||
|
* Redistribution and use in source and binary forms,
|
|||
|
* with or without modification, are permitted provided
|
|||
|
* that the following conditions are met:
|
|||
|
*
|
|||
|
* * Redistributions of source code must retain the above
|
|||
|
* copyright notice, this list of conditions and the
|
|||
|
* following disclaimer.
|
|||
|
* * Redistributions in binary form must reproduce the above
|
|||
|
* copyright notice, this list of conditions and the
|
|||
|
* following disclaimer in the documentation and/or other
|
|||
|
* materials provided with the distribution.
|
|||
|
*
|
|||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|||
|
* SUCH DAMAGE.
|
|||
|
*/
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.ComponentModel;
|
|||
|
using System.Drawing;
|
|||
|
using System.Data;
|
|||
|
using System.Text;
|
|||
|
using System.Windows.Forms;
|
|||
|
|
|||
|
using XenAdmin;
|
|||
|
using XenAdmin.Network;
|
|||
|
using XenAdmin.Core;
|
|||
|
using XenAPI;
|
|||
|
|
|||
|
|
|||
|
namespace XenAdmin.Controls
|
|||
|
{
|
|||
|
public partial class PoolHostPicker : CustomTreeView
|
|||
|
{
|
|||
|
public EventHandler<SelectedItemEventArgs> SelectedItemChanged;
|
|||
|
public bool SupressErrors = false;
|
|||
|
|
|||
|
public PoolHostPicker()
|
|||
|
{
|
|||
|
InitializeComponent();
|
|||
|
CollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged);
|
|||
|
ShowCheckboxes = false;
|
|||
|
ShowDescription = true;
|
|||
|
ShowImages = true;
|
|||
|
|
|||
|
buildList();
|
|||
|
|
|||
|
ConnectionsManager.XenConnections.CollectionChanged += CollectionChanged;
|
|||
|
}
|
|||
|
|
|||
|
public override int ItemHeight { get { return 18; } }
|
|||
|
|
|||
|
private CollectionChangeEventHandler CollectionChangedWithInvoke;
|
|||
|
void CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|||
|
{
|
|||
|
Program.BeginInvoke(this, buildList);
|
|||
|
}
|
|||
|
|
|||
|
void PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|||
|
{
|
|||
|
if(e.PropertyName == "name_label" || e.PropertyName == "metrics" || e.PropertyName == "enabled" || e.PropertyName == "live" || e.PropertyName == "patches")
|
|||
|
Program.Invoke(this, buildList);
|
|||
|
}
|
|||
|
|
|||
|
public void buildList()
|
|||
|
{
|
|||
|
Program.AssertOnEventThread();
|
|||
|
|
|||
|
Host selectedhost = null;
|
|||
|
IXenConnection selectedconnection = null;
|
|||
|
if (SelectedItem != null)
|
|||
|
{
|
|||
|
if (SelectedItem is HostItem)
|
|||
|
{
|
|||
|
selectedhost = (SelectedItem as HostItem).TheHost;
|
|||
|
}
|
|||
|
else if (SelectedItem is PoolItem)
|
|||
|
{
|
|||
|
selectedconnection = (SelectedItem as PoolItem).Connection;
|
|||
|
}
|
|||
|
}
|
|||
|
BeginUpdate();
|
|||
|
try
|
|||
|
{
|
|||
|
ClearAllNodes();
|
|||
|
foreach (IXenConnection xc in ConnectionsManager.XenConnectionsCopy)
|
|||
|
{
|
|||
|
if (Helpers.GetPool(xc) != null)
|
|||
|
{
|
|||
|
PoolItem item = new PoolItem(xc);
|
|||
|
if (SupressErrors)
|
|||
|
{
|
|||
|
item.Enabled = false;
|
|||
|
item.Description = "";
|
|||
|
}
|
|||
|
AddNode(item);
|
|||
|
foreach (Host host in xc.Cache.Hosts)
|
|||
|
{
|
|||
|
HostItem item2 = new HostItem(host);
|
|||
|
if (SupressErrors)
|
|||
|
{
|
|||
|
item2.Enabled = host.IsLive;
|
|||
|
item2.Description = "";
|
|||
|
}
|
|||
|
AddChildNode(item, item2);
|
|||
|
host.PropertyChanged -= PropertyChanged;
|
|||
|
host.PropertyChanged += PropertyChanged;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
else if (xc.IsConnected)
|
|||
|
{
|
|||
|
Host host = Helpers.GetMaster(xc);
|
|||
|
if (host != null)
|
|||
|
{
|
|||
|
HostItem item = new HostItem(host);
|
|||
|
if (SupressErrors)
|
|||
|
{
|
|||
|
item.Enabled = host.IsLive;
|
|||
|
item.Description = "";
|
|||
|
}
|
|||
|
AddNode(item);
|
|||
|
host.PropertyChanged -= PropertyChanged;
|
|||
|
host.PropertyChanged += PropertyChanged;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
PoolItem item = new PoolItem(xc);
|
|||
|
if (SupressErrors)
|
|||
|
{
|
|||
|
item.Enabled = false;
|
|||
|
item.Description = "";
|
|||
|
}
|
|||
|
AddNode(item);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
PoolItem item = new PoolItem(xc);
|
|||
|
if (SupressErrors)
|
|||
|
{
|
|||
|
item.Enabled = false;
|
|||
|
item.Description = "";
|
|||
|
}
|
|||
|
AddNode(item);
|
|||
|
}
|
|||
|
|
|||
|
Pool pool = Helpers.GetPoolOfOne(xc);
|
|||
|
if (pool != null)
|
|||
|
{
|
|||
|
pool.PropertyChanged -= PropertyChanged;
|
|||
|
pool.PropertyChanged += PropertyChanged;
|
|||
|
}
|
|||
|
|
|||
|
xc.ConnectionStateChanged -= xc_ConnectionStateChanged;
|
|||
|
xc.ConnectionStateChanged += xc_ConnectionStateChanged;
|
|||
|
xc.CachePopulated -= xc_CachePopulated;
|
|||
|
xc.CachePopulated += xc_CachePopulated;
|
|||
|
xc.Cache.RegisterCollectionChanged<Host>(CollectionChangedWithInvoke);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
EndUpdate();
|
|||
|
if (selectedhost != null)
|
|||
|
SelectHost(selectedhost);
|
|||
|
else if (selectedconnection != null)
|
|||
|
SelectConnection(selectedconnection);
|
|||
|
else if (SelectedItemChanged != null)
|
|||
|
SelectedItemChanged(null, new SelectedItemEventArgs(false));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void xc_CachePopulated(object sender, EventArgs e)
|
|||
|
{
|
|||
|
Program.Invoke(this, buildList);
|
|||
|
}
|
|||
|
|
|||
|
void xc_ConnectionStateChanged(object sender, EventArgs e)
|
|||
|
{
|
|||
|
Program.Invoke(this, buildList);
|
|||
|
}
|
|||
|
|
|||
|
private CustomTreeNode lastSelected;
|
|||
|
public bool AllowPoolSelect = true;
|
|||
|
|
|||
|
protected override void OnSelectedIndexChanged(EventArgs e)
|
|||
|
{
|
|||
|
if (SelectedItem is CustomTreeNode)
|
|||
|
{
|
|||
|
CustomTreeNode item = SelectedItem as CustomTreeNode;
|
|||
|
if (!item.Enabled)
|
|||
|
{
|
|||
|
SelectedItem = lastSelected;
|
|||
|
}
|
|||
|
if (!AllowPoolSelect && item is PoolItem)
|
|||
|
{
|
|||
|
SelectedItem = lastSelected;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
lastSelected = SelectedItem as CustomTreeNode;
|
|||
|
|
|||
|
base.OnSelectedIndexChanged(e);
|
|||
|
if(SelectedItemChanged != null)
|
|||
|
SelectedItemChanged(null,new SelectedItemEventArgs((SelectedItem is PoolItem || SelectedItem is HostItem) && (SelectedItem as CustomTreeNode).Enabled));
|
|||
|
}
|
|||
|
|
|||
|
private bool SelectNextEnabledNode(CustomTreeNode currentNode, bool searchForward)
|
|||
|
{
|
|||
|
CustomTreeNode nextEnabledNode = GetNextEnabledNode(currentNode, searchForward);
|
|||
|
if (nextEnabledNode != null)
|
|||
|
{
|
|||
|
SelectedItem = nextEnabledNode;
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
protected override void OnKeyDown(KeyEventArgs e)
|
|||
|
{
|
|||
|
var node = SelectedItem as CustomTreeNode;
|
|||
|
if (node != null)
|
|||
|
{
|
|||
|
switch (e.KeyCode)
|
|||
|
{
|
|||
|
case Keys.Down:
|
|||
|
{
|
|||
|
e.Handled = SelectNextEnabledNode(node, true);
|
|||
|
break;
|
|||
|
}
|
|||
|
case Keys.Up:
|
|||
|
{
|
|||
|
e.Handled = SelectNextEnabledNode(node, false);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
base.OnKeyDown(e);
|
|||
|
}
|
|||
|
|
|||
|
public IXenConnection ChosenConnection
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (SelectedItem == null || SelectedItem is HostItem || !(SelectedItem as CustomTreeNode).Enabled)
|
|||
|
return null;
|
|||
|
return (SelectedItem as PoolItem).Connection;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Host ChosenHost
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (SelectedItem == null || SelectedItem is PoolItem || !(SelectedItem as CustomTreeNode).Enabled)
|
|||
|
return null;
|
|||
|
return (SelectedItem as HostItem).TheHost;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void SelectHost(Host host)
|
|||
|
{
|
|||
|
if (host == null)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
foreach (CustomTreeNode item in Items)
|
|||
|
{
|
|||
|
if (TryToSelectHost(item, host))
|
|||
|
return;
|
|||
|
|
|||
|
if (item is PoolItem)
|
|||
|
{
|
|||
|
foreach (CustomTreeNode childItem in item.ChildNodes)
|
|||
|
{
|
|||
|
if (TryToSelectHost(childItem, host))
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
OnSelectedIndexChanged(null);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Tries to select the node if it is a host item. If it is a host item, but is disabled, selects its parent instead.
|
|||
|
/// </summary>
|
|||
|
/// <param name="item"></param>
|
|||
|
/// <param name="host"></param>
|
|||
|
/// <returns>True if successful</returns>
|
|||
|
private bool TryToSelectHost(CustomTreeNode item, Host host)
|
|||
|
{
|
|||
|
if (item is HostItem)
|
|||
|
{
|
|||
|
HostItem hostitem = item as HostItem;
|
|||
|
if (hostitem.TheHost.opaque_ref == host.opaque_ref)
|
|||
|
{
|
|||
|
if (hostitem.Enabled)
|
|||
|
{
|
|||
|
SelectedItem = hostitem;
|
|||
|
return true;
|
|||
|
}
|
|||
|
else if (hostitem.ParentNode is PoolItem)
|
|||
|
{
|
|||
|
SelectConnection(host.Connection);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public void SelectConnection(IXenConnection xenConnection)
|
|||
|
{
|
|||
|
foreach (CustomTreeNode item in Items)
|
|||
|
{
|
|||
|
if (item is PoolItem)
|
|||
|
{
|
|||
|
PoolItem poolitem = item as PoolItem;
|
|||
|
if (poolitem.Connection.Equals(xenConnection))
|
|||
|
{
|
|||
|
SelectedItem = poolitem;
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
OnSelectedIndexChanged(null);
|
|||
|
}
|
|||
|
|
|||
|
internal void SelectFirstThing()
|
|||
|
{
|
|||
|
for (int i = 0; i < Items.Count; i++)
|
|||
|
{
|
|||
|
if (Items[i] is CustomTreeNode && (Items[i] as CustomTreeNode).Enabled)
|
|||
|
{
|
|||
|
if (!AllowPoolSelect && Items[i] is PoolItem)
|
|||
|
continue;
|
|||
|
SelectedIndex = i;
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class PoolItem : CustomTreeNode
|
|||
|
{
|
|||
|
public IXenConnection Connection;
|
|||
|
public PoolItem(IXenConnection xc)
|
|||
|
{
|
|||
|
Connection = xc;
|
|||
|
Update();
|
|||
|
}
|
|||
|
|
|||
|
public void Update()
|
|||
|
{
|
|||
|
this.Image = Images.GetImage16For(Connection);
|
|||
|
this.Text = Helpers.GetName(Connection);
|
|||
|
this.Enabled = Connection.IsConnected && (Helpers.GetPool(Connection) == null || Helpers.HasFullyConnectedSharedStorage(Connection));
|
|||
|
if (Enabled)
|
|||
|
this.Description = "";
|
|||
|
else if (!Connection.IsConnected)
|
|||
|
this.Description = Messages.DISCONNECTED;
|
|||
|
else
|
|||
|
this.Description = Messages.POOL_HAS_NO_SHARED_STORAGE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
protected override int SameLevelSortOrder(CustomTreeNode other)
|
|||
|
{
|
|||
|
if (Enabled && !other.Enabled)
|
|||
|
return -1;
|
|||
|
else if (!Enabled && other.Enabled)
|
|||
|
return 1;
|
|||
|
else
|
|||
|
return base.SameLevelSortOrder(other);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class HostItem : CustomTreeNode
|
|||
|
{
|
|||
|
public Host TheHost;
|
|||
|
|
|||
|
public HostItem(Host host)
|
|||
|
{
|
|||
|
TheHost = host;
|
|||
|
Update();
|
|||
|
}
|
|||
|
|
|||
|
public void Update()
|
|||
|
{
|
|||
|
this.Image = Images.GetImage16For(TheHost);
|
|||
|
this.Text = Helpers.GetName(TheHost);
|
|||
|
this.Enabled = TheHost.IsLive && CanCreateVMsWithAffinityTo(TheHost);
|
|||
|
if (Enabled)
|
|||
|
this.Description = "";
|
|||
|
else if (!TheHost.IsLive)
|
|||
|
this.Description = Messages.HOST_NOT_LIVE;
|
|||
|
else
|
|||
|
this.Description = Messages.HOST_SEES_NO_STORAGE;
|
|||
|
}
|
|||
|
|
|||
|
protected override int SameLevelSortOrder(CustomTreeNode other)
|
|||
|
{
|
|||
|
if (Enabled && !other.Enabled)
|
|||
|
return -1;
|
|||
|
else if (!Enabled && other.Enabled)
|
|||
|
return 1;
|
|||
|
else if (Enabled && other.Enabled && other is HostItem)
|
|||
|
return TheHost.CompareTo(((HostItem)other).TheHost);
|
|||
|
else
|
|||
|
return base.SameLevelSortOrder(other);
|
|||
|
}
|
|||
|
|
|||
|
private static bool CanCreateVMsWithAffinityTo(Host TheHost)
|
|||
|
{
|
|||
|
if (Helpers.HasFullyConnectedSharedStorage(TheHost.Connection))
|
|||
|
return true;
|
|||
|
else
|
|||
|
{
|
|||
|
foreach (SR sr in TheHost.Connection.Cache.SRs)
|
|||
|
{
|
|||
|
if (sr.CanBeSeenFrom(TheHost) && sr.CanCreateVmOn())
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class SelectedItemEventArgs : EventArgs
|
|||
|
{
|
|||
|
public bool SomethingSelected;
|
|||
|
|
|||
|
public SelectedItemEventArgs(bool notnull)
|
|||
|
{
|
|||
|
SomethingSelected = notnull;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|