Prevent crash and improve behaviour when the user tries to configure/disable HA in a pool that has lost the HA statefile.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
This commit is contained in:
Konstantina Chremmou 2019-07-18 00:35:11 +01:00 committed by Mihaela Stoica
parent dd81a5d14c
commit 8f2794b89d
6 changed files with 101 additions and 67 deletions

View File

@ -38,6 +38,8 @@ using XenAdmin.Core;
using XenAdmin.Dialogs;
using XenAdmin.Wizards;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
namespace XenAdmin.Commands
@ -47,6 +49,8 @@ namespace XenAdmin.Commands
/// </summary>
internal class HACommand : Command
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Initializes a new instance of this Command. The parameter-less constructor is required if
/// this Command is to be attached to a ToolStrip menu item or button. It should not be used in any other scenario.
@ -88,8 +92,27 @@ namespace XenAdmin.Commands
}
else if (pool.ha_enabled)
{
// Show VM restart priority editor
MainWindowCommandInterface.ShowPerConnectionWizard(connection, new EditVmHaPrioritiesDialog(pool));
if (pool.ha_statefiles.Any(sf => pool.Connection.Resolve(new XenRef<VDI>(sf)) != null))
{
// Show VM restart priority editor
MainWindowCommandInterface.ShowPerConnectionWizard(connection, new EditVmHaPrioritiesDialog(pool));
}
else
{
log.ErrorFormat("Cannot resolve HA statefile VDI (pool {0} has {1} statefiles).",
pool.Name(), pool.ha_statefiles.Length);
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
string.Format(Messages.HA_CONFIGURE_NO_STATEFILE, Helpers.GetName(pool).Ellipsise(30)),
Messages.CONFIGURE_HA),
"HADisable",
ThreeButtonDialog.ButtonOK))
{
dlg.ShowDialog(Program.MainWindow);
}
}
}
else
{
@ -105,26 +128,24 @@ namespace XenAdmin.Commands
protected override bool CanExecuteCore(SelectedItemCollection selection)
{
if (selection.Count == 1)
{
Pool poolAncestor = selection[0].PoolAncestor;
bool inPool = poolAncestor != null;
if (selection.Count != 1)
return false;
if (inPool )
{
Host master = Helpers.GetMaster(poolAncestor.Connection);
Pool poolAncestor = selection[0].PoolAncestor;
if (poolAncestor == null || poolAncestor.Locked)
return false;
if (master == null || HelpersGUI.FindActiveHaAction(poolAncestor.Connection) != null || poolAncestor.Locked)
{
return false;
}
else
{
return true;
}
}
}
return false;
if (poolAncestor.Connection == null || !poolAncestor.Connection.IsConnected)
return false;
if (HelpersGUI.FindActiveHaAction(poolAncestor.Connection) != null)
return false;
Host master = Helpers.GetMaster(poolAncestor.Connection);
if (master == null)
return false;
return true;
}
protected override string GetCantExecuteReasonCore(IXenObject item)

View File

@ -59,16 +59,13 @@ namespace XenAdmin.Dialogs
/// </summary>
private readonly long originalNtol;
/// <summary>
///
/// </summary>
/// <param name="pool">May not be null. HA must be turned off on the pool.</param>
public EditVmHaPrioritiesDialog(Pool pool)
{
if (pool == null)
throw new ArgumentNullException("pool");
if (!pool.ha_enabled)
throw new ArgumentException("You may only show the EditVmHaPrioritiesDialog for pools that already have HA turned on");
throw new ArgumentException("Can only configure HA for pools that already have HA turned on");
this.pool = pool;
InitializeComponent();
@ -78,23 +75,6 @@ namespace XenAdmin.Dialogs
pool.PropertyChanged += pool_PropertyChanged;
originalNtol = pool.ha_host_failures_to_tolerate;
if (pool.ha_statefiles.Length != 1)
{
log.ErrorFormat("Cannot show dialog: pool {0} has {1} statefiles, but this dialog can only handle exactly 1. Closing dialog.",
pool.Name(), pool.ha_statefiles.Length);
this.Close();
return;
}
XenRef<VDI> vdiRef = new XenRef<VDI>(pool.ha_statefiles[0]);
VDI vdi = pool.Connection.Resolve(vdiRef);
if (vdi == null)
{
log.Error("Could not resolve HA statefile reference. Closing dialog.");
this.Close();
return;
}
pictureBoxWarningIcon.Image = SystemIcons.Warning.ToBitmap();
Rebuild();
}

View File

@ -33,8 +33,10 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using XenAdmin.Actions;
using XenAdmin.Commands;
using XenAdmin.Controls;
using XenAdmin.Core;
using XenAdmin.Dialogs;
@ -629,6 +631,21 @@ namespace XenAdmin.TabPages
return;
}
if (pool.ha_statefiles.All(sf => pool.Connection.Resolve(new XenRef<VDI>(sf)) == null)) //empty gives true, which is correct
{
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
string.Format(Messages.HA_DISABLE_NO_STATEFILE, Helpers.GetName(pool).Ellipsise(30)),
Messages.DISABLE_HA),
"HADisable",
ThreeButtonDialog.ButtonOK))
{
dlg.ShowDialog(this);
return;
}
}
// Confirm the user wants to disable HA
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(
@ -649,35 +666,12 @@ namespace XenAdmin.TabPages
action.RunAsync();
}
/// <summary>
/// Shows the appropriate dialog (HA wizard if HA is disabled for the pool, or VM restart priority editor otherwise).
/// </summary>
/// <param name="pool">Must not be null.</param>
internal static void EditHA(Pool pool)
{
// Do nothing if there is an HA action in progress
if (HelpersGUI.FindActiveHaAction(pool.Connection) != null)
{
log.Debug("Not opening HA dialog: an HA action is in progress");
return;
}
if (!pool.Connection.IsConnected)
{
log.Debug("Not opening HA dialog: the connection to the pool is now closed");
return;
}
if (pool.ha_enabled)
{
// Show VM restart priority editor
Program.MainWindow.ShowPerConnectionWizard(pool.Connection, new EditVmHaPrioritiesDialog(pool));
}
else
{
// Show wizard to enable HA
Program.MainWindow.ShowPerConnectionWizard(pool.Connection, new HAWizard(pool));
}
var cmd = new HACommand(Program.MainWindow, pool);
if (cmd.CanExecute())
cmd.Execute();
}
private void SetNetworkHBInitDelay()

View File

@ -194,6 +194,9 @@ namespace XenAdmin.Wizards.HAWizard_Pages
private void DeregisterEvents()
{
if (connection == null)
return;
// Remove listeners
connection.Cache.DeregisterCollectionChanged<VM>(VM_CollectionChangedWithInvoke);

View File

@ -7491,6 +7491,15 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to Configure HA.
/// </summary>
public static string CONFIGURE_HA {
get {
return ResourceManager.GetString("CONFIGURE_HA", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &amp;Configure HA....
/// </summary>
@ -17012,6 +17021,15 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to Cannot configure HA for pool &apos;{0}&apos; because an HA Statefile VDI could not be found in the pool..
/// </summary>
public static string HA_CONFIGURE_NO_STATEFILE {
get {
return ResourceManager.GetString("HA_CONFIGURE_NO_STATEFILE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Configure HA now....
/// </summary>
@ -17102,6 +17120,15 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to Cannot disable HA for pool &apos;{0}&apos; because an HA Statefile VDI could not be found in the pool..
/// </summary>
public static string HA_DISABLE_NO_STATEFILE {
get {
return ResourceManager.GetString("HA_DISABLE_NO_STATEFILE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to disable HA for pool &apos;{0}&apos;?.
/// </summary>

View File

@ -2706,6 +2706,9 @@ Do you want to assign it to the schedule '{2}' instead?</value>
<data name="COMPRESSING_FILES" xml:space="preserve">
<value>Compressing appliance files...</value>
</data>
<data name="CONFIGURE_HA" xml:space="preserve">
<value>Configure HA</value>
</data>
<data name="CONFIGURE_HA_ELLIPSIS" xml:space="preserve">
<value>&amp;Configure HA...</value>
</data>
@ -5976,6 +5979,9 @@ not currently live:
<data name="HA_CONFIGURE_NOW" xml:space="preserve">
<value>Configure HA now...</value>
</data>
<data name="HA_CONFIGURE_NO_STATEFILE" xml:space="preserve">
<value>Cannot configure HA for pool '{0}' because an HA Statefile VDI could not be found in the pool.</value>
</data>
<data name="HA_CONFIRM_FORCESHUTDOWN_VM" xml:space="preserve">
<value>The selected VM is currently protected by HA. Are you sure you want to force the shut down of this VM? This operation will cancel all running tasks for the VM and can result in data loss.</value>
</data>
@ -5997,6 +6003,9 @@ not currently live:
<data name="HA_CURRENT_CAPACITY" xml:space="preserve">
<value>Current failure capacity:</value>
</data>
<data name="HA_DISABLE_NO_STATEFILE" xml:space="preserve">
<value>Cannot disable HA for pool '{0}' because an HA Statefile VDI could not be found in the pool.</value>
</data>
<data name="HA_DISABLE_QUERY" xml:space="preserve">
<value>Are you sure you want to disable HA for pool '{0}'?</value>
</data>