xenadmin/XenAdmin/Commands/AddHostToPoolCommand.cs

305 lines
14 KiB
C#
Raw Normal View History

/* 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.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using XenAdmin.Actions;
using XenAdmin.Core;
using XenAdmin.Dialogs;
using XenAPI;
namespace XenAdmin.Commands
{
/// <summary>
/// A Command for adding a host to a pool. This Command does not use the current selection for specifying the host and pool.
/// The host and pool are set in the constructor for this class.
/// </summary>
internal class AddHostToPoolCommand : Command
{
private readonly List<Host> _hosts;
private readonly Pool _pool;
private readonly bool _confirm;
/// <summary>
/// Initializes a new instance of the <see cref="AddHostToPoolCommand"/> class.
/// </summary>
/// <param name="mainWindow">The main window interface. It can be found at Program.MainWindow.CommandInterface.</param>
/// <param name="hosts">The hosts which are to be added to the pool.</param>
/// <param name="pool">The pool the host should be added to.</param>
/// <param name="confirm">if set to <c>true</c> a confirmation dialog is shown.</param>
public AddHostToPoolCommand(IMainWindow mainWindow, IEnumerable<Host> hosts, Pool pool, bool confirm)
: base(mainWindow)
{
Util.ThrowIfParameterNull(hosts, "hosts");
Util.ThrowIfParameterNull(pool, "pool");
_hosts = new List<Host>(hosts);
_confirm = confirm;
_pool = pool;
}
protected override void ExecuteCore(SelectedItemCollection selection)
{
Dictionary<SelectedItem, string> reasons = new Dictionary<SelectedItem, string>();
foreach (Host host in _hosts)
{
PoolJoinRules.Reason reason = PoolJoinRules.CanJoinPool(host.Connection, _pool.Connection, true, true, true);
if (reason != PoolJoinRules.Reason.Allowed)
reasons[new SelectedItem(host)] = PoolJoinRules.ReasonMessage(reason);
}
if (reasons.Count > 0)
{
string title = Messages.ERROR_DIALOG_ADD_TO_POOL_TITLE;
string text = string.Format(Messages.ERROR_DIALOG_ADD_TO_POOL_TEXT, Helpers.GetName(_pool).Ellipsise(500));
new CommandErrorDialog(title, text, reasons).ShowDialog(Parent);
return;
}
if (_confirm && !ShowConfirmationDialog())
{
// Bail out if the user doesn't want to continue.
return;
}
if (!Helpers.IsConnected(_pool))
{
string message = _hosts.Count == 1
? string.Format(Messages.ADD_HOST_TO_POOL_DISCONNECTED_POOL,
Helpers.GetName(_hosts[0]).Ellipsise(500), Helpers.GetName(_pool).Ellipsise(500))
: string.Format(Messages.ADD_HOST_TO_POOL_DISCONNECTED_POOL_MULTIPLE,
Helpers.GetName(_pool).Ellipsise(500));
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Error, message, Messages.XENCENTER)))
{
dlg.ShowDialog(Parent);
}
return;
}
// Check supp packs and warn
List<string> badSuppPacks = PoolJoinRules.HomogeneousSuppPacksDiffering(_hosts, _pool);
if (!HelpersGUI.GetPermissionFor(badSuppPacks, sp => true,
Messages.ADD_HOST_TO_POOL_SUPP_PACK, Messages.ADD_HOST_TO_POOL_SUPP_PACKS, false, "PoolJoinSuppPacks"))
{
return;
}
// Are there any hosts which are forbidden from masking their CPUs for licensing reasons?
// If so, we need to show upsell.
Host master = Helpers.GetMaster(_pool);
if (null != _hosts.Find(host =>
!PoolJoinRules.CompatibleCPUs(host, master, false) &&
Helpers.FeatureForbidden(host, Host.RestrictCpuMasking) &&
!PoolJoinRules.FreeHostPaidMaster(host, master, false))) // in this case we can upgrade the license and then mask the CPU
{
using (var dlg = new UpsellDialog(HiddenFeatures.LinkLabelHidden ? Messages.UPSELL_BLURB_CPUMASKING : Messages.UPSELL_BLURB_CPUMASKING + Messages.UPSELL_BLURB_CPUMASKING_MORE,
InvisibleMessages.UPSELL_LEARNMOREURL_CPUMASKING))
dlg.ShowDialog(Parent);
return;
}
// Get permission for any fix-ups: 1) Licensing free hosts; 2) CPU masking 3) Ad configuration 4) CPU feature levelling (Dundee or higher only)
// (We already know that these things are fixable because we have been through CanJoinPool() above).
if (!HelpersGUI.GetPermissionFor(_hosts, host => PoolJoinRules.FreeHostPaidMaster(host, master, false),
Messages.ADD_HOST_TO_POOL_LICENSE_MESSAGE, Messages.ADD_HOST_TO_POOL_LICENSE_MESSAGE_MULTIPLE, true, "PoolJoinRelicensing")
||
!HelpersGUI.GetPermissionFor(_hosts, host => !PoolJoinRules.CompatibleCPUs(host, master, false),
Messages.ADD_HOST_TO_POOL_CPU_MASKING_MESSAGE, Messages.ADD_HOST_TO_POOL_CPU_MASKING_MESSAGE_MULTIPLE, true, "PoolJoinCpuMasking")
||
!HelpersGUI.GetPermissionFor(_hosts, host => !PoolJoinRules.CompatibleAdConfig(host, master, false),
Messages.ADD_HOST_TO_POOL_AD_MESSAGE, Messages.ADD_HOST_TO_POOL_AD_MESSAGE_MULTIPLE, true, "PoolJoinAdConfiguring")
||
!HelpersGUI.GetPermissionForCpuFeatureLevelling(_hosts, _pool))
{
return;
}
MainWindowCommandInterface.SelectObjectInTree(_pool);
List<AsyncAction> actions = new List<AsyncAction>();
foreach (Host host in _hosts)
{
string opaque_ref = host.opaque_ref;
AddHostToPoolAction action = new AddHostToPoolAction(_pool, host, GetAdPrompt, NtolDialog, ApplyLicenseEditionCommand.ShowLicensingFailureDialog);
action.Completed += s => Program.ShowObject(opaque_ref);
actions.Add(action);
// hide connection. If the action fails, re-show it.
Program.HideObject(opaque_ref);
}
RunMultipleActions(actions, string.Format(Messages.ADDING_SERVERS_TO_POOL, _pool.Name), Messages.POOLCREATE_ADDING, Messages.POOLCREATE_ADDED, true);
}
protected override bool CanExecuteCore(SelectedItemCollection selection)
{
if (_hosts.Count > 0)
{
foreach (Host host in _hosts)
{
// only allowed to add standalone hosts.
if (Helpers.GetPool(host.Connection) != null || host.RestrictPooling)
{
return false;
}
}
return true;
}
return false;
}
protected override string ConfirmationDialogText
{
get
{
if (_hosts.Count == 1)
{
return string.Format(Messages.MAINWINDOW_CONFIRM_MOVE_TO_POOL, _hosts[0].Name.Ellipsise(500), _pool.Name.Ellipsise(500));
}
else if (_hosts.Count > 1)
{
return string.Format(Messages.MAINWINDOW_CONFIRM_MOVE_TO_POOL_MULTIPLE, _pool.Name.Ellipsise(500));
}
return null;
}
}
protected override string ConfirmationDialogTitle
{
get
{
return Messages.POOLCREATE_ADDING;
}
}
public static PoolAbstractAction.AdUserAndPassword GetAdPrompt(Host poolMaster)
{
AdPasswordPrompt adPrompt = new AdPasswordPrompt(true, poolMaster.external_auth_service_name);
Program.Invoke(Program.MainWindow, delegate
{
if (adPrompt.ShowDialog(Program.MainWindow) == DialogResult.Cancel)
throw new CancelledException();
});
return new PoolAbstractAction.AdUserAndPassword(adPrompt.Username, adPrompt.Password);
}
public static bool EnableNtolDialog(Pool pool, Host host, long currentNtol, long max)
{
bool doit = false;
Program.Invoke(Program.MainWindow, delegate()
{
string poolName = Helpers.GetName(pool).Ellipsise(500);
string hostName = Helpers.GetName(host).Ellipsise(500);
string msg = string.Format(Messages.HA_HOST_ENABLE_NTOL_RAISE_QUERY, poolName, hostName, currentNtol, max);
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(null, msg, Messages.HIGH_AVAILABILITY),
ThreeButtonDialog.ButtonYes,
new ThreeButtonDialog.TBDButton(Messages.NO_BUTTON_CAPTION, DialogResult.No, ThreeButtonDialog.ButtonType.CANCEL, true)))
{
if (dlg.ShowDialog(Program.MainWindow) == DialogResult.Yes)
{
doit = true;
}
}
});
return doit;
}
public static bool NtolDialog(HostAbstractAction action, Pool pool, long currentNtol, long targetNtol)
{
bool cancel = false;
Program.Invoke(Program.MainWindow, delegate()
{
string poolName = Helpers.GetName(pool).Ellipsise(500);
string hostName = Helpers.GetName(action.Host).Ellipsise(500);
string msg;
if (targetNtol == 0)
{
string f;
if (action is EvacuateHostAction)
{
f = Messages.HA_HOST_DISABLE_NTOL_ZERO;
}
else if (action is RebootHostAction)
{
f = Messages.HA_HOST_REBOOT_NTOL_ZERO;
}
else
{
f = Messages.HA_HOST_SHUTDOWN_NTOL_ZERO;
}
msg = string.Format(f, poolName, hostName);
}
else
{
string f;
if (action is EvacuateHostAction)
{
f = Messages.HA_HOST_DISABLE_NTOL_DROP;
}
else if (action is RebootHostAction)
{
f = Messages.HA_HOST_REBOOT_NTOL_DROP;
}
else
{
f = Messages.HA_HOST_SHUTDOWN_NTOL_DROP;
}
msg = string.Format(f, poolName, currentNtol, hostName, targetNtol);
}
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Warning, msg, Messages.HIGH_AVAILABILITY),
ThreeButtonDialog.ButtonYes,
new ThreeButtonDialog.TBDButton(Messages.NO_BUTTON_CAPTION, DialogResult.No, ThreeButtonDialog.ButtonType.CANCEL, true)
))
{
if (dlg.ShowDialog(Program.MainWindow) == DialogResult.No)
{
cancel = true;
}
}
});
return cancel;
}
}
}