mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-23 20:36:33 +01:00
359 lines
14 KiB
C#
359 lines
14 KiB
C#
/* Copyright (c) Cloud Software Group, Inc.
|
|
*
|
|
* 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.Linq;
|
|
using System.Windows.Forms;
|
|
using XenAdmin.Actions;
|
|
using XenAdmin.Actions.Wlb;
|
|
using XenAdmin.Controls;
|
|
using XenAdmin.Core;
|
|
using XenAdmin.Network;
|
|
using XenAPI;
|
|
|
|
|
|
namespace XenAdmin.Commands
|
|
{
|
|
/// <summary>
|
|
/// This is the base ToolStripMenuItem for StartVMOnHostToolStripMenuItem, ResumeVMOnHostToolStripMenuItem and MigrateVMToolStripMenuItem.
|
|
/// </summary>
|
|
internal abstract class VMOperationToolStripMenuItem : CommandToolStripMenuItem
|
|
{
|
|
private readonly vm_operations _operation;
|
|
private readonly bool _resumeAfter;
|
|
|
|
protected VMOperationToolStripMenuItem(Command command, bool inContextMenu, vm_operations operation)
|
|
: base(command, inContextMenu)
|
|
{
|
|
if (operation != vm_operations.start_on && operation != vm_operations.resume_on && operation != vm_operations.pool_migrate)
|
|
{
|
|
throw new ArgumentException("Invalid operation", "operation");
|
|
}
|
|
|
|
if (operation.Equals(vm_operations.resume_on))
|
|
_resumeAfter = true;
|
|
|
|
_operation = operation;
|
|
base.DropDownItems.Add(new ToolStripMenuItem());
|
|
}
|
|
|
|
protected override void OnDropDownClosed(EventArgs e)
|
|
{
|
|
base.OnDropDownClosed(e);
|
|
Stop();
|
|
}
|
|
|
|
|
|
protected override void OnDropDownOpening(EventArgs e)
|
|
{
|
|
base.DropDownItems.Clear();
|
|
|
|
// Work around bug in tool kit where disabled menu items show their dropdown menus
|
|
if (!Enabled)
|
|
{
|
|
ToolStripMenuItem emptyMenuItem = new ToolStripMenuItem(Messages.HOST_MENU_EMPTY);
|
|
emptyMenuItem.Font = Program.DefaultFont;
|
|
emptyMenuItem.Enabled = false;
|
|
base.DropDownItems.Add(emptyMenuItem);
|
|
return;
|
|
}
|
|
|
|
VisualMenuItemAlignData.ParentStrip = this;
|
|
var selection = Command.GetSelection();
|
|
IXenConnection connection = selection[0].Connection;
|
|
bool wlb = Helpers.WlbEnabled(connection);
|
|
|
|
if (wlb)
|
|
{
|
|
base.DropDownItems.Add(new VMOperationToolStripMenuSubItem(Messages.WLB_OPT_MENU_OPTIMAL_SERVER, Images.StaticImages._000_ServerWlb_h32bit_16));
|
|
}
|
|
else
|
|
{
|
|
base.DropDownItems.Add(new VMOperationToolStripMenuSubItem(Messages.HOME_SERVER_MENU_ITEM, Images.StaticImages._000_ServerHome_h32bit_16));
|
|
}
|
|
|
|
List<Host> hosts = new List<Host>(connection.Cache.Hosts);
|
|
hosts.Sort();
|
|
foreach (Host host in hosts)
|
|
{
|
|
VMOperationToolStripMenuSubItem item = new VMOperationToolStripMenuSubItem(String.Format(Messages.MAINWINDOW_CONTEXT_UPDATING, host.name_label.EscapeAmpersands()), Images.StaticImages._000_ServerDisconnected_h32bit_16);
|
|
item.Tag = host;
|
|
base.DropDownItems.Add(item);
|
|
}
|
|
|
|
// Adds the migrate wizard button, do this before the enable checks on the other items
|
|
AddAdditionalMenuItems(selection);
|
|
|
|
UpdateHostList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hook to add additional members to the menu item
|
|
/// Note: Called on main window thread by running code
|
|
/// </summary>
|
|
protected virtual void AddAdditionalMenuItems(SelectedItemCollection selection) { return; }
|
|
|
|
#region UpdateHostList
|
|
|
|
private ProduceConsumerQueue workerQueueWithoutWlb;
|
|
readonly object locker = new object();
|
|
private bool stopped;
|
|
|
|
private bool Stopped
|
|
{
|
|
set
|
|
{
|
|
lock (locker)
|
|
{
|
|
stopped = value;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
lock (locker)
|
|
{
|
|
return stopped;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Stop()
|
|
{
|
|
Stopped = true;
|
|
if (workerQueueWithoutWlb != null)
|
|
workerQueueWithoutWlb.CancelWorkers(false);
|
|
}
|
|
|
|
private void UpdateHostList()
|
|
{
|
|
Stopped = false;
|
|
|
|
var selection = Command.GetSelection();
|
|
var connection = selection[0].Connection;
|
|
|
|
if (Helpers.WlbEnabled(connection))
|
|
{
|
|
var vms = selection.AsXenObjects<VM>();
|
|
if (vms == null || vms.Count == 0)
|
|
return;
|
|
|
|
var retrieveVmRecommendationsAction = new WlbRetrieveVmRecommendationsAction(connection, vms);
|
|
retrieveVmRecommendationsAction.Completed += delegate
|
|
{
|
|
if (Stopped || retrieveVmRecommendationsAction.Cancelled || !retrieveVmRecommendationsAction.Succeeded)
|
|
return;
|
|
|
|
var recommendations = new WlbRecommendations(vms, retrieveVmRecommendationsAction.Recommendations);
|
|
|
|
Program.Invoke(Program.MainWindow, delegate
|
|
{
|
|
if (recommendations.IsError)
|
|
EnableAppropriateHostsNoWlb();
|
|
else
|
|
EnableAppropriateHostsWlb(recommendations);
|
|
});
|
|
};
|
|
retrieveVmRecommendationsAction.RunAsync();
|
|
}
|
|
else
|
|
{
|
|
EnableAppropriateHostsNoWlb();
|
|
}
|
|
}
|
|
|
|
private void EnableAppropriateHostsWlb(WlbRecommendations recommendations)
|
|
{
|
|
if (Stopped || DropDownItems.Count == 0)
|
|
return;
|
|
|
|
// set the first menu item to be the WLB optimal server menu item
|
|
var firstItem = DropDownItems[0] as VMOperationToolStripMenuSubItem;
|
|
if (firstItem == null)
|
|
return;
|
|
|
|
var selection = Command.GetSelection();
|
|
|
|
var firstItemCmd = new VMOperationWlbOptimalServerCommand(Command.MainWindowCommandInterface,
|
|
selection, _operation, recommendations);
|
|
|
|
firstItem.Command = firstItemCmd;
|
|
firstItem.Enabled = firstItemCmd.CanRun();
|
|
|
|
var hostMenuItems = new List<VMOperationToolStripMenuSubItem>();
|
|
foreach (var item in DropDownItems)
|
|
{
|
|
var hostMenuItem = item as VMOperationToolStripMenuSubItem;
|
|
if (hostMenuItem == null)
|
|
continue;
|
|
|
|
var host = hostMenuItem.Tag as Host;
|
|
if (host != null)
|
|
{
|
|
var cmd = new VMOperationWlbHostCommand(Command.MainWindowCommandInterface, selection, host,
|
|
_operation, recommendations.GetStarRating(host));
|
|
|
|
hostMenuItem.Command = cmd;
|
|
hostMenuItem.Enabled = cmd.CanRun();
|
|
|
|
hostMenuItems.Add(hostMenuItem);
|
|
}
|
|
}
|
|
|
|
// sort the hostMenuItems by star rating
|
|
hostMenuItems.Sort(new WlbHostStarCompare());
|
|
|
|
// refresh the drop-down-items from the menuItems.
|
|
foreach (VMOperationToolStripMenuSubItem menuItem in hostMenuItems)
|
|
{
|
|
DropDownItems.Insert(hostMenuItems.IndexOf(menuItem) + 1, menuItem);
|
|
}
|
|
}
|
|
|
|
private void EnableAppropriateHostsNoWlb()
|
|
{
|
|
if (Stopped || DropDownItems.Count == 0)
|
|
return;
|
|
|
|
var firstItem = DropDownItems[0] as VMOperationToolStripMenuSubItem;
|
|
if (firstItem == null)
|
|
return;
|
|
|
|
// API calls could happen in CanRun(), which take time to wait. So a Producer-Consumer-Queue with size 25 is used here to :
|
|
// 1. Make API calls for different menu items happen in parallel;
|
|
// 2. Limit the count of concurrent threads (now it's 25).
|
|
workerQueueWithoutWlb = new ProduceConsumerQueue(25);
|
|
|
|
var selection = Command.GetSelection();
|
|
var connection = selection[0].Connection;
|
|
var session = connection.DuplicateSession();
|
|
|
|
var affinityHost = connection.Resolve(((VM)selection[0].XenObject).affinity);
|
|
|
|
EnqueueHostMenuItem(this, session, affinityHost, firstItem, true);
|
|
|
|
var hostMenuItems = DropDownItems.OfType<VMOperationToolStripMenuSubItem>().ToList();
|
|
|
|
if (Stopped)
|
|
return;
|
|
|
|
foreach (VMOperationToolStripMenuSubItem item in hostMenuItems)
|
|
{
|
|
var host = item.Tag as Host;
|
|
if (host != null)
|
|
{
|
|
var tempItem = item;
|
|
EnqueueHostMenuItem(this, session, host, tempItem, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EnqueueHostMenuItem(VMOperationToolStripMenuItem menu, Session session, Host host, VMOperationToolStripMenuSubItem hostMenuItem, bool isHomeServer)
|
|
{
|
|
workerQueueWithoutWlb.EnqueueItem(() =>
|
|
{
|
|
var selection = menu.Command.GetSelection();
|
|
var cmd = isHomeServer
|
|
? new VMOperationHomeServerCommand(menu.Command.MainWindowCommandInterface, selection, menu._operation, session)
|
|
: new VMOperationHostCommand(menu.Command.MainWindowCommandInterface, selection, delegate { return host; }, host.Name().EscapeAmpersands(), menu._operation, session);
|
|
|
|
var oldMigrateCmdCanRun = cmd.CanRun();
|
|
if (Stopped)
|
|
return;
|
|
|
|
if (host == null || menu._operation == vm_operations.start_on || oldMigrateCmdCanRun)
|
|
{
|
|
Program.Invoke(Program.MainWindow, delegate
|
|
{
|
|
hostMenuItem.Command = cmd;
|
|
hostMenuItem.Enabled = oldMigrateCmdCanRun;
|
|
});
|
|
}
|
|
else
|
|
{
|
|
var cpmCmd = isHomeServer
|
|
? new CrossPoolMigrateToHomeCommand(menu.Command.MainWindowCommandInterface, selection, host)
|
|
: new CrossPoolMigrateCommand(menu.Command.MainWindowCommandInterface, selection, host, menu._resumeAfter);
|
|
|
|
var crossPoolMigrateCmdCanRun = cpmCmd.CanRun();
|
|
if (Stopped)
|
|
return;
|
|
|
|
Program.Invoke(Program.MainWindow, delegate
|
|
{
|
|
if (crossPoolMigrateCmdCanRun || !string.IsNullOrEmpty(cpmCmd.CantRunReason))
|
|
{
|
|
hostMenuItem.Command = cpmCmd;
|
|
hostMenuItem.Enabled = crossPoolMigrateCmdCanRun;
|
|
}
|
|
else
|
|
{
|
|
hostMenuItem.Command = cmd;
|
|
hostMenuItem.Enabled = false;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// This class is an implementation of the 'IComparer' interface
|
|
/// for sorting vm placement menuItem List when wlb is enabled
|
|
/// </summary>
|
|
private class WlbHostStarCompare : IComparer<VMOperationToolStripMenuSubItem>
|
|
{
|
|
public int Compare(VMOperationToolStripMenuSubItem x, VMOperationToolStripMenuSubItem y)
|
|
{
|
|
int result = 0;
|
|
|
|
// if x and y are enabled, compare their start rating
|
|
if (x.Enabled && y.Enabled)
|
|
result = y.StarRating.CompareTo(x.StarRating);
|
|
|
|
// if x and y are disabled, they are equal
|
|
else if (!x.Enabled && !y.Enabled)
|
|
result = 0;
|
|
|
|
// if x is disabled, y is greater
|
|
else if (!x.Enabled)
|
|
result = 1;
|
|
|
|
// if y is disabled, x is greater
|
|
else if (!y.Enabled)
|
|
result = -1;
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|