mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-23 20:36:33 +01:00
341 lines
15 KiB
C#
341 lines
15 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.Collections.Generic;
|
|
using System.Linq;
|
|
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 RunCore(SelectedItemCollection selection)
|
|
{
|
|
var reasons = new Dictionary<IXenObject, string>();
|
|
foreach (Host host in _hosts)
|
|
{
|
|
PoolJoinRules.Reason reason = PoolJoinRules.CanJoinPool(host.Connection, _pool.Connection, true, true, _hosts.Count);
|
|
if (reason != PoolJoinRules.Reason.Allowed)
|
|
reasons[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)
|
|
{
|
|
var msg = _hosts.Count > 1
|
|
? string.Format(Messages.MAINWINDOW_CONFIRM_MOVE_TO_POOL_MULTIPLE, _pool.Name().Ellipsise(500))
|
|
: string.Format(Messages.MAINWINDOW_CONFIRM_MOVE_TO_POOL, _hosts[0].Name().Ellipsise(500), _pool.Name().Ellipsise(500));
|
|
|
|
using (var dialog = new WarningDialog(msg, ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo)
|
|
{WindowTitle = Messages.POOLCREATE_ADDING})
|
|
{
|
|
if (dialog.ShowDialog(Parent ?? Program.MainWindow) == DialogResult.No)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_pool == null || _pool.Connection == null || !_pool.Connection.IsConnected)
|
|
{
|
|
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 ErrorDialog(message))
|
|
dlg.ShowDialog(Parent);
|
|
|
|
return;
|
|
}
|
|
|
|
// Check supp packs and warn
|
|
List<string> badSuppPacks = PoolJoinRules.HomogeneousSuppPacksDiffering(_hosts, _pool);
|
|
|
|
if (!Program.RunInAutomatedTestMode && badSuppPacks.Count > 0)
|
|
{
|
|
string msg = string.Format(badSuppPacks.Count == 1 ? Messages.ADD_HOST_TO_POOL_SUPP_PACK : Messages.ADD_HOST_TO_POOL_SUPP_PACKS,
|
|
string.Join("\n", badSuppPacks));
|
|
|
|
using (var dlg = new WarningDialog(msg,
|
|
new ThreeButtonDialog.TBDButton(Messages.PROCEED, DialogResult.OK, selected: false),
|
|
new ThreeButtonDialog.TBDButton(Messages.CANCEL, DialogResult.Cancel, selected: true))
|
|
{HelpNameSetter = "PoolJoinSuppPacks"})
|
|
{
|
|
if (dlg.ShowDialog(Program.MainWindow) == DialogResult.Cancel)
|
|
return;
|
|
}
|
|
}
|
|
|
|
Host coordinator = Helpers.GetCoordinator(_pool);
|
|
|
|
// 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 (!Program.RunInAutomatedTestMode)
|
|
{
|
|
var hosts1 = _hosts.FindAll(host => PoolJoinRules.FreeHostPaidCoordinator(host, coordinator, false));
|
|
if (hosts1.Count > 0)
|
|
{
|
|
string msg = string.Format(hosts1.Count == 1
|
|
? Messages.ADD_HOST_TO_POOL_LICENSE_MESSAGE
|
|
: Messages.ADD_HOST_TO_POOL_LICENSE_MESSAGE_MULTIPLE,
|
|
string.Join("\n", hosts1.Select(h => h.Name())));
|
|
|
|
using (var dlg = new WarningDialog(msg, ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo)
|
|
{HelpNameSetter = "PoolJoinRelicensing"})
|
|
{
|
|
if (dlg.ShowDialog(Program.MainWindow) == DialogResult.No)
|
|
return;
|
|
}
|
|
}
|
|
|
|
var hosts2 = _hosts.FindAll(host => !PoolJoinRules.CompatibleCPUs(host, coordinator));
|
|
if (hosts2.Count > 0)
|
|
{
|
|
string msg = string.Format(hosts2.Count == 1
|
|
? Messages.ADD_HOST_TO_POOL_CPU_MASKING_MESSAGE
|
|
: Messages.ADD_HOST_TO_POOL_CPU_MASKING_MESSAGE_MULTIPLE,
|
|
string.Join("\n", hosts2.Select(h => h.Name())), BrandManager.ProductBrand);
|
|
|
|
using (var dlg = new WarningDialog(msg, ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo)
|
|
{HelpNameSetter = "PoolJoinCpuMasking"})
|
|
{
|
|
if (dlg.ShowDialog(Program.MainWindow) == DialogResult.No)
|
|
return;
|
|
}
|
|
}
|
|
|
|
var hosts3 = _hosts.FindAll(host => !PoolJoinRules.CompatibleAdConfig(host, coordinator, false));
|
|
if (hosts3.Count > 0)
|
|
{
|
|
string msg = string.Format(hosts3.Count == 1
|
|
? Messages.ADD_HOST_TO_POOL_AD_MESSAGE
|
|
: Messages.ADD_HOST_TO_POOL_AD_MESSAGE_MULTIPLE,
|
|
string.Join("\n", hosts3.Select(h => h.Name())));
|
|
|
|
using (var dlg = new WarningDialog(msg, ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo)
|
|
{HelpNameSetter = "PoolJoinAdConfiguring"})
|
|
{
|
|
if (dlg.ShowDialog(Program.MainWindow) == DialogResult.No)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!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;
|
|
var action = new AddHostToPoolAction(_pool, host, GetAdPrompt,
|
|
(licenseFailures, exceptionMessage) =>
|
|
{
|
|
if (licenseFailures.Count > 0)
|
|
{
|
|
Program.Invoke(Program.MainWindow, () =>
|
|
{
|
|
using (var dlg = new CommandErrorDialog(Messages.LICENSE_ERROR_TITLE, exceptionMessage,
|
|
licenseFailures.ToDictionary<LicenseFailure, IXenObject, string>(f => f.Host, f => f.AlertText)))
|
|
{
|
|
dlg.ShowDialog(Program.MainWindow);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
action.Completed += s => XenAdminConfigManager.Provider.ShowObject(opaque_ref);
|
|
actions.Add(action);
|
|
|
|
// hide connection. If the action fails, re-show it.
|
|
XenAdminConfigManager.Provider.HideObject(opaque_ref);
|
|
}
|
|
|
|
RunMultipleActions(actions, string.Format(Messages.ADDING_SERVERS_TO_POOL, _pool.Name()), Messages.POOLCREATE_ADDING, Messages.POOLCREATE_ADDED, true);
|
|
}
|
|
|
|
protected override bool CanRunCore(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(host))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static PoolAbstractAction.AdUserAndPassword GetAdPrompt(Host poolCoordinator)
|
|
{
|
|
AdPasswordPrompt adPrompt = new AdPasswordPrompt(true, poolCoordinator.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 NoIconDialog(msg,
|
|
ThreeButtonDialog.ButtonYes,
|
|
new ThreeButtonDialog.TBDButton(Messages.NO_BUTTON_CAPTION, DialogResult.No, selected: true))
|
|
{WindowTitle = Messages.HIGH_AVAILABILITY})
|
|
{
|
|
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 WarningDialog(msg, ThreeButtonDialog.ButtonYes,
|
|
new ThreeButtonDialog.TBDButton(Messages.NO_BUTTON_CAPTION, DialogResult.No, selected: true)
|
|
){WindowTitle = Messages.HIGH_AVAILABILITY})
|
|
{
|
|
if (dlg.ShowDialog(Program.MainWindow) == DialogResult.No)
|
|
{
|
|
cancel = true;
|
|
}
|
|
}
|
|
|
|
});
|
|
return cancel;
|
|
}
|
|
|
|
}
|
|
}
|