CA-339305: XenCenter was hanging when update alerts were removed very fast, and other issues:

- Use a plain list to store update alerts and, where possible, fire a collection
  change event only after a bulk change and not every time a single object is added
  or removed.
- Fixed issue where dismissed updates were stored in the config of all connected
  pools, even of those where the update did not apply.
- Corrected RBAC checks for update dismissal.
- Fixed enabled state of dismiss buttons.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
This commit is contained in:
Konstantina Chremmou 2020-07-26 14:54:52 +01:00
parent f05b512ca4
commit e3896239df
16 changed files with 351 additions and 403 deletions

View File

@ -29,29 +29,23 @@
* SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using XenAdmin.Alerts;
using XenAdmin.Network;
using XenAdmin.Core;
using XenAPI;
namespace XenAdmin.Actions
{
public class DeleteAllAlertsAction : AsyncAction
{
private readonly IEnumerable<Alert> Alerts;
private readonly List<Alert> _alerts;
/// <param name="connection">May be null; this is expected for client-side alerts.</param>
/// <param name="alerts"></param>
public DeleteAllAlertsAction(IXenConnection connection, IEnumerable<Alert> alerts)
: base(connection,
GetActionTitle(connection, alerts.Count()),
Messages.ACTION_REMOVE_ALERTS_DESCRIPTION)
public DeleteAllAlertsAction(List<Alert> alerts, IXenConnection connection = null)
: base(connection, GetActionTitle(connection, alerts.Count),
Messages.ACTION_REMOVE_ALERTS_DESCRIPTION)
{
this.Alerts = alerts;
_alerts = new List<Alert>(alerts);
if (connection != null)
{
@ -73,58 +67,25 @@ namespace XenAdmin.Actions
protected override void Run()
{
int i = 0;
int max = Alerts.Count();
Exception e = null;
LogDescriptionChanges = false;
List<Alert> toBeDismissed = new List<Alert>();
try
{
var myList = new List<Alert>(Alerts);
foreach (Alert a in myList)
for (var i = 0; i < _alerts.Count; i++)
{
PercentComplete = (i * 100) / max;
i++;
Description = string.Format(Messages.ACTION_REMOVE_ALERTS_PROGRESS_DESCRIPTION, i, max);
Alert a1 = a;
BestEffort(ref e, delegate
{
try
{
if (a1 is XenServerPatchAlert || a1 is XenServerVersionAlert)
{
toBeDismissed.Add(a1);
}
else if(a1 is XenCenterUpdateAlert)
{
a1.Dismiss();
}
else
{
a1.DismissSingle(Session);
}
}
catch (Failure exn)
{
if (exn.ErrorDescription[0] != Failure.HANDLE_INVALID)
throw;
//remove alert from XenCenterAlerts; this will trigger the CollectionChanged event, on which we update the alert count
if (Alert.FindAlert(a1) != null)
Alert.RemoveAlert(a1);
}
});
Description = string.Format(Messages.ACTION_REMOVE_ALERTS_PROGRESS_DESCRIPTION, i, _alerts.Count);
_alerts[i].Dismiss();
PercentComplete = i * 100 / _alerts.Count;
}
Updates.DismissUpdates(toBeDismissed);
}
finally
{
LogDescriptionChanges = true;
}
Description = max == 1 ? Messages.ACTION_REMOVE_ALERTS_DONE_ONE : string.Format(Messages.ACTION_REMOVE_ALERTS_DONE, max);
Description = _alerts.Count == 1
? Messages.ACTION_REMOVE_ALERTS_DONE_ONE
: string.Format(Messages.ACTION_REMOVE_ALERTS_DONE, _alerts.Count);
}
}
}

View File

@ -32,6 +32,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using XenAdmin.Alerts;
using XenAdmin.Network;
using XenAdmin.Core;
using XenAPI;
@ -56,11 +57,11 @@ namespace XenAdmin.Actions
Dictionary<string, string> other_config = pool.other_config;
if (other_config.ContainsKey(Updates.IgnorePatchKey))
other_config.Remove(Updates.IgnorePatchKey);
if (other_config.ContainsKey(XenServerPatchAlert.IgnorePatchKey))
other_config.Remove(XenServerPatchAlert.IgnorePatchKey);
if (other_config.ContainsKey(Updates.LAST_SEEN_SERVER_VERSION_KEY))
other_config.Remove(Updates.LAST_SEEN_SERVER_VERSION_KEY);
if (other_config.ContainsKey(XenServerVersionAlert.LAST_SEEN_SERVER_VERSION_KEY))
other_config.Remove(XenServerVersionAlert.LAST_SEEN_SERVER_VERSION_KEY);
XenAPI.Pool.set_other_config(Connection.Session, pool.opaque_ref, other_config);

View File

@ -44,7 +44,7 @@ namespace XenAdmin.Alerts
{
public class MessageAlert : Alert
{
public XenAPI.Message Message;
public readonly Message Message;
public IXenObject XenObject;
private const int DEFAULT_PRIORITY = 0;
@ -438,14 +438,28 @@ namespace XenAdmin.Alerts
public override void Dismiss()
{
new DestroyMessageAction(Message.Connection, Message.opaque_ref).RunAsync();
base.Dismiss();
new DestroyMessageAction(Message).RunExternal(Connection.Session);
RemoveAlert(this);
}
public override void DismissSingle(Session s)
public override bool AllowedToDismiss()
{
Message.destroy(s, Message.opaque_ref);
base.Dismiss();
if (Dismissing)
return false;
// this shouldn't happen for this type of alert, but check for safety
if (Connection == null)
return true;
// if we are disconnected do not dismiss as the alert will disappear soon
if (Connection.Session == null)
return false;
if (Connection.Session.IsLocalSuperuser)
return true;
var allowedRoles = Role.ValidRoleList("Message.destroy", Connection);
return allowedRoles.Any(r => Connection.Session.Roles.Contains(r));
}
public static void RemoveAlert(Message m)

View File

@ -42,6 +42,8 @@ namespace XenAdmin.Alerts
{
public class XenServerPatchAlert : XenServerUpdateAlert
{
public const string IgnorePatchKey = "XenCenter.IgnorePatches";
public readonly XenServerPatch Patch;
public readonly XenServerVersion NewServerVersion;
@ -169,15 +171,32 @@ namespace XenAdmin.Alerts
Dictionary<string, string> other_config = pool.other_config;
if (other_config.ContainsKey(Updates.IgnorePatchKey))
if (other_config.ContainsKey(IgnorePatchKey))
{
List<string> current = new List<string>(other_config[Updates.IgnorePatchKey].Split(','));
List<string> current = new List<string>(other_config[IgnorePatchKey].Split(','));
if (current.Contains(Patch.Uuid, StringComparer.OrdinalIgnoreCase))
return true;
}
return false;
}
protected override void Dismiss(Dictionary<string, string> otherConfig)
{
if (otherConfig.ContainsKey(IgnorePatchKey))
{
var current = new List<string>(otherConfig[IgnorePatchKey].Split(','));
if (current.Contains(Patch.Uuid, StringComparer.OrdinalIgnoreCase))
return;
current.Add(Patch.Uuid);
otherConfig[IgnorePatchKey] = string.Join(",", current.ToArray());
}
else
{
otherConfig.Add(IgnorePatchKey, Patch.Uuid);
}
}
public override bool Equals(Alert other)
{
if (other is XenServerPatchAlert patchAlert)

View File

@ -147,6 +147,50 @@ namespace XenAdmin.Alerts
}
}
private bool AllowedToDismiss(IXenConnection connection)
{
if (connection == null)
return true;
// if we are disconnected do not dismiss as the alert will disappear soon
if (connection.Session == null)
return false;
// prevent dismissal if the alert is not relevant to the current connection
if (!Connections.Contains(connection) && DistinctHosts.All(h => h.Connection != connection))
return false;
if (connection.Session.IsLocalSuperuser)
return true;
var allowedRoles = Role.ValidRoleList("pool.set_other_config", connection);
return allowedRoles.Any(r => connection.Session.Roles.Contains(r));
}
public override bool AllowedToDismiss()
{
return !Dismissing && ConnectionsManager.XenConnectionsCopy.Any(AllowedToDismiss);
}
public override void Dismiss()
{
foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy)
{
if (!AllowedToDismiss(connection))
continue;
Pool pool = Helpers.GetPoolOfOne(connection);
if (pool == null)
continue;
var otherConfig = pool.other_config;
Dismiss(otherConfig);
Pool.set_other_config(connection.Session, pool.opaque_ref, otherConfig);
}
Updates.RemoveUpdate(this);
}
public override bool IsDismissed()
{
lock (connectionsLock)
@ -155,6 +199,8 @@ namespace XenAdmin.Alerts
}
}
protected abstract void Dismiss(Dictionary<string, string> otherConfig);
protected abstract bool IsDismissed(IXenConnection connection);
}
}

View File

@ -31,6 +31,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using XenAdmin.Core;
using XenAdmin.Network;
using XenAPI;
@ -40,6 +41,8 @@ namespace XenAdmin.Alerts
{
public class XenServerVersionAlert : XenServerUpdateAlert
{
public const string LAST_SEEN_SERVER_VERSION_KEY = "XenCenter.LastSeenServerVersion";
public readonly XenServerVersion Version;
public XenServerVersionAlert(XenServerVersion version)
@ -76,15 +79,31 @@ namespace XenAdmin.Alerts
Dictionary<string, string> other_config = pool.other_config;
if (other_config.ContainsKey(Updates.LAST_SEEN_SERVER_VERSION_KEY))
if (other_config.ContainsKey(LAST_SEEN_SERVER_VERSION_KEY))
{
List<string> current = new List<string>(other_config[Updates.LAST_SEEN_SERVER_VERSION_KEY].Split(','));
List<string> current = new List<string>(other_config[LAST_SEEN_SERVER_VERSION_KEY].Split(','));
if (current.Contains(Version.Version.ToString()))
return true;
}
return false;
}
protected override void Dismiss(Dictionary<string, string> otherConfig)
{
if (otherConfig.ContainsKey(LAST_SEEN_SERVER_VERSION_KEY))
{
List<string> current = new List<string>(otherConfig[LAST_SEEN_SERVER_VERSION_KEY].Split(','));
if (current.Contains(Version.Version.ToString()))
return;
current.Add(Version.Version.ToString());
otherConfig[LAST_SEEN_SERVER_VERSION_KEY] = string.Join(",", current.ToArray());
}
else
{
otherConfig.Add(LAST_SEEN_SERVER_VERSION_KEY, Version.Version.ToString());
}
}
public override bool Equals(Alert other)
{
if (other is XenServerVersionAlert versionAlert)

View File

@ -32,17 +32,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using XenAdmin.Actions;
using XenAPI;
using XenAdmin.Alerts;
using XenAdmin.Network;
using System.Diagnostics;
using System.Windows.Forms;
using XenAdmin.Dialogs;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using XenAdmin.Actions;
using XenAdmin.Alerts;
using XenAdmin.Alerts.Types;
using XenCenterLib;
using XenAdmin.Dialogs;
using XenAdmin.Network;
using XenAPI;
namespace XenAdmin.Core
{
@ -50,9 +50,12 @@ namespace XenAdmin.Core
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string CheckForUpdatesUrl = Registry.CustomUpdatesXmlLocation ?? BrandManager.UpdatesUrl;
public static event Action<bool, string> CheckForUpdatesCompleted;
public static event Action CheckForUpdatesStarted;
public static event Action RestoreDismissedUpdatesStarted;
public static event Action<CollectionChangeEventArgs> UpdateAlertCollectionChanged;
private static readonly object downloadedUpdatesLock = new object();
private static List<XenServerVersion> XenServerVersionsForAutoCheck = new List<XenServerVersion>();
@ -61,119 +64,27 @@ namespace XenAdmin.Core
public static List<XenServerVersion> XenServerVersions = new List<XenServerVersion>();
private static readonly object updateAlertsLock = new object();
private static readonly ChangeableList<Alert> updateAlerts = new ChangeableList<Alert>();
private static readonly List<Alert> updateAlerts = new List<Alert>();
public const string IgnorePatchKey = "XenCenter.IgnorePatches";
public const string LAST_SEEN_SERVER_VERSION_KEY = "XenCenter.LastSeenServerVersion";
public static IEnumerable<Alert> UpdateAlerts => updateAlerts;
public static int UpdateAlertsCount => updateAlerts.Count;
private static void AddUpdate(Alert update)
/// <summary>
/// Locks and creates a new list of the update alerts
/// </summary>
public static List<Alert> UpdateAlerts
{
try
get
{
lock (updateAlertsLock)
{
if(!updateAlerts.Contains(update))
{
updateAlerts.Add(update);
}
}
}
catch (Exception e)
{
log.Error("Failed to add update", e);
return updateAlerts.ToList();
}
}
public static void RemoveUpdate(Alert update)
{
try
{
lock (updateAlertsLock)
{
if(updateAlerts.Contains(update))
{
updateAlerts.Remove(update);
}
}
}
catch (Exception e)
{
log.Error("Failed to remove update", e);
}
}
/// <summary>
/// Dismisses the updates in the given list i.e. they are added in the
/// other_config list of each pool and removed from the Updates.UpdateAlerts list.
/// </summary>
public static void DismissUpdates(List<Alert> toBeDismissed)
{
if (toBeDismissed.Count == 0)
return;
foreach(IXenConnection connection in ConnectionsManager.XenConnectionsCopy)
{
if (!Alert.AllowedToDismiss(connection))
continue;
Pool pool = Helpers.GetPoolOfOne(connection);
if (pool == null)
continue;
Dictionary<string, string> other_config = pool.other_config;
foreach (Alert alert in toBeDismissed)
{
if (alert is XenServerPatchAlert patchAlert)
{
if (other_config.ContainsKey(IgnorePatchKey))
{
List<string> current = new List<string>(other_config[IgnorePatchKey].Split(','));
if (current.Contains(patchAlert.Patch.Uuid, StringComparer.OrdinalIgnoreCase))
continue;
current.Add(patchAlert.Patch.Uuid);
other_config[IgnorePatchKey] = string.Join(",", current.ToArray());
}
else
{
other_config.Add(IgnorePatchKey, patchAlert.Patch.Uuid);
}
}
if (alert is XenServerVersionAlert versionAlert)
{
if (other_config.ContainsKey(LAST_SEEN_SERVER_VERSION_KEY))
{
List<string> current = new List<string>(other_config[LAST_SEEN_SERVER_VERSION_KEY].Split(','));
if (current.Contains(versionAlert.Version.Version.ToString()))
continue;
current.Add(versionAlert.Version.Version.ToString());
other_config[LAST_SEEN_SERVER_VERSION_KEY] = string.Join(",", current.ToArray());
}
else
{
other_config.Add(LAST_SEEN_SERVER_VERSION_KEY, versionAlert.Version.Version.ToString());
}
}
RemoveUpdate(alert);
}
Pool.set_other_config(connection.Session, pool.opaque_ref, other_config);
}
}
private static Alert FindUpdate(Predicate<Alert> predicate)
{
lock (updateAlertsLock)
return updateAlerts.Find(predicate);
}
updateAlerts.Remove(update);
UpdateAlertCollectionChanged?.Invoke(new CollectionChangeEventArgs(CollectionChangeAction.Remove, update));
}
/// <summary>
/// If AutomaticCheck is enabled it checks for updates regardless the
@ -270,46 +181,25 @@ namespace XenAdmin.Core
return action.Succeeded;
}
public static string CheckForUpdatesUrl => Registry.CustomUpdatesXmlLocation ?? BrandManager.UpdatesUrl;
private static void actionCompleted(ActionBase sender)
{
Program.AssertOffEventThread();
DownloadUpdatesXmlAction action = sender as DownloadUpdatesXmlAction;
if (action == null)
if (!(sender is DownloadUpdatesXmlAction action))
return;
bool succeeded = action.Succeeded;
string errorMessage = string.Empty;
lock (updateAlertsLock)
updateAlerts.Clear();
if (succeeded)
{
lock (downloadedUpdatesLock)
{
XenCenterVersions = action.XenCenterVersions;
XenServerVersionsForAutoCheck = action.XenServerVersionsForAutoCheck;
XenServerVersions = action.XenServerVersions;
XenServerPatches = action.XenServerPatches;
}
var xenCenterAlerts = NewXenCenterUpdateAlerts(XenCenterVersions, Program.Version);
if (xenCenterAlerts != null)
updateAlerts.AddRange(xenCenterAlerts.Where(a=>!a.IsDismissed()));
var xenServerUpdateAlerts = NewXenServerVersionAlerts(XenServerVersionsForAutoCheck);
if (xenServerUpdateAlerts != null)
updateAlerts.AddRange(xenServerUpdateAlerts.Where(a=>!a.CanIgnore));
var xenServerPatchAlerts = NewXenServerPatchAlerts(XenServerVersions, XenServerPatches);
if (xenServerPatchAlerts != null)
updateAlerts.AddRange(xenServerPatchAlerts.Where(alert => !alert.CanIgnore));
}
else
{
@ -332,29 +222,37 @@ namespace XenAdmin.Core
errorMessage = Messages.AVAILABLE_UPDATES_INTERNAL_ERROR;
}
if (CheckForUpdatesCompleted != null)
CheckForUpdatesCompleted(succeeded, errorMessage);
lock (updateAlertsLock)
{
updateAlerts.Clear();
if (succeeded)
{
var xenCenterAlerts = NewXenCenterUpdateAlerts(XenCenterVersions, Program.Version);
updateAlerts.AddRange(xenCenterAlerts.Where(a => !a.IsDismissed()));
var xenServerUpdateAlerts = NewXenServerVersionAlerts(XenServerVersionsForAutoCheck);
updateAlerts.AddRange(xenServerUpdateAlerts.Where(a => !a.CanIgnore));
var xenServerPatchAlerts = NewXenServerPatchAlerts(XenServerVersions, XenServerPatches);
updateAlerts.AddRange(xenServerPatchAlerts.Where(a => !a.CanIgnore));
}
}
UpdateAlertCollectionChanged?.Invoke(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, UpdateAlerts));
CheckForUpdatesCompleted?.Invoke(succeeded, errorMessage);
}
public static void RegisterCollectionChanged(CollectionChangeEventHandler handler)
{
updateAlerts.CollectionChanged += handler;
}
public static void DeregisterCollectionChanged(CollectionChangeEventHandler handler)
{
updateAlerts.CollectionChanged -= handler;
}
public static List<XenCenterUpdateAlert> NewXenCenterUpdateAlerts(List<XenCenterVersion> xenCenterVersions,
Version currentProgramVersion)
{
if (Helpers.CommonCriteriaCertificationRelease)
return null;
return new List<XenCenterUpdateAlert>();
var alerts = new List<XenCenterUpdateAlert>();
XenCenterVersion latest = null, latestCr = null;
if (xenCenterVersions.Count != 0 && currentProgramVersion != new Version(0, 0, 0, 0))
{
var latestVersions = from v in xenCenterVersions where v.Latest select v;
@ -395,7 +293,7 @@ namespace XenAdmin.Core
List<XenServerPatch> xenServerPatches)
{
if (Helpers.CommonCriteriaCertificationRelease)
return null;
return new List<XenServerPatchAlert>();
var alerts = new List<XenServerPatchAlert>();
@ -778,7 +676,7 @@ namespace XenAdmin.Core
public static List<XenServerVersionAlert> NewXenServerVersionAlerts(List<XenServerVersion> xenServerVersions)
{
if (Helpers.CommonCriteriaCertificationRelease)
return null;
return new List<XenServerVersionAlert>();
var latestVersion = xenServerVersions.FindAll(item => item.Latest).OrderByDescending(v => v.Version).FirstOrDefault();
var latestCrVersion = xenServerVersions.FindAll(item => item.LatestCr).OrderByDescending(v => v.Version).FirstOrDefault();
@ -840,38 +738,54 @@ namespace XenAdmin.Core
return serverVersions;
}
public static void CheckServerVersion()
public static void RefreshUpdateAlerts(UpdateType flags)
{
var alerts = NewXenServerVersionAlerts(XenServerVersionsForAutoCheck);
if (alerts == null)
return;
var alerts = new List<XenServerUpdateAlert>();
alerts.ForEach(CheckUpdate);
}
if (flags.HasFlag(UpdateType.ServerVersion))
alerts.AddRange(NewXenServerVersionAlerts(XenServerVersionsForAutoCheck));
public static void CheckServerPatches()
{
var alerts = NewXenServerPatchAlerts(XenServerVersions, XenServerPatches);
if (alerts == null)
return;
if (flags.HasFlag(UpdateType.ServerPatches))
alerts.AddRange(NewXenServerPatchAlerts(XenServerVersions, XenServerPatches));
alerts.ForEach(CheckUpdate);
}
bool changed = false;
private static void CheckUpdate(XenServerUpdateAlert alert)
{
var existingAlert = FindUpdate(a => a.Equals(alert));
if (existingAlert != null && alert.CanIgnore)
RemoveUpdate(existingAlert);
else if (existingAlert is XenServerUpdateAlert updAlert)
try
{
RemoveUpdate(updAlert);
updAlert.CopyConnectionsAndHosts(alert);
AddUpdate(updAlert);
lock (updateAlertsLock)
{
foreach (var alert in alerts)
{
var existingAlert = updateAlerts.FirstOrDefault(a => a.Equals(alert));
if (existingAlert != null && alert.CanIgnore)
{
updateAlerts.Remove(existingAlert);
changed = true;
}
else if (existingAlert is XenServerUpdateAlert updAlert)
{
updateAlerts.Remove(updAlert);
updAlert.CopyConnectionsAndHosts(alert);
if (!updateAlerts.Contains(updAlert))
updateAlerts.Add(updAlert);
changed = true;
}
else if (!alert.CanIgnore && !updateAlerts.Contains(alert))
{
updateAlerts.Add(alert);
changed = true;
}
}
}
if (changed)
UpdateAlertCollectionChanged?.Invoke(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, UpdateAlerts));
}
catch (Exception e)
{
log.Error("Failed to refresh the updates", e);
}
else if (!alert.CanIgnore)
AddUpdate(alert);
}
public static void RestoreDismissedUpdates()
@ -883,8 +797,7 @@ namespace XenAdmin.Core
var action = new ParallelAction(Messages.RESTORE_DISMISSED_UPDATES, Messages.RESTORING, Messages.COMPLETED, actions, true, false);
action.Completed += action_Completed;
if (RestoreDismissedUpdatesStarted != null)
RestoreDismissedUpdatesStarted();
RestoreDismissedUpdatesStarted?.Invoke();
action.RunAsync();
}
@ -902,7 +815,7 @@ namespace XenAdmin.Core
private static XenServerPatchAlert FindPatchAlert(Predicate<XenServerPatch> predicate)
{
var existingAlert = FindUpdate(a => a is XenServerPatchAlert patchAlert && predicate(patchAlert.Patch));
var existingAlert = UpdateAlerts.FirstOrDefault(a => a is XenServerPatchAlert patchAlert && predicate(patchAlert.Patch));
if (existingAlert != null)
return existingAlert as XenServerPatchAlert;
@ -991,5 +904,14 @@ namespace XenAdmin.Core
Alert.RemoveAlert(a => a is HotfixEligibilityAlert);
Alert.AddAlertRange(alerts);
}
[Flags]
public enum UpdateType : short
{
None = 0,
ServerPatches = 1,
ServerVersion = 2,
XenCenterVersion = 4
}
}
}

View File

@ -253,7 +253,7 @@ namespace XenAdmin
//ClipboardViewer is registered in OnHandleCreated
OtherConfigAndTagsWatcher.RegisterEventHandlers();
Alert.RegisterAlertCollectionChanged(XenCenterAlerts_CollectionChanged);
Updates.RegisterCollectionChanged(Updates_CollectionChanged);
Updates.UpdateAlertCollectionChanged += Updates_CollectionChanged;
ConnectionsManager.History.CollectionChanged += History_CollectionChanged;
//ConnectionsManager.XenConnections.CollectionChanged is registered in OnShown
Properties.Settings.Default.SettingChanging += Default_SettingChanging;
@ -266,7 +266,7 @@ namespace XenAdmin
Clip.UnregisterClipboardViewer();
OtherConfigAndTagsWatcher.DeregisterEventHandlers();
Alert.DeregisterAlertCollectionChanged(XenCenterAlerts_CollectionChanged);
Updates.DeregisterCollectionChanged(Updates_CollectionChanged);
Updates.UpdateAlertCollectionChanged -= Updates_CollectionChanged;
ConnectionsManager.History.CollectionChanged -= History_CollectionChanged;
ConnectionsManager.XenConnections.CollectionChanged -= XenConnection_CollectionChanged;
Properties.Settings.Default.SettingChanging -= Default_SettingChanging;
@ -873,8 +873,7 @@ namespace XenAdmin
{
CloseActiveWizards(connection);
Alert.RemoveAlert(alert => alert.Connection != null && alert.Connection.Equals(connection));
Updates.CheckServerPatches();
Updates.CheckServerVersion();
Updates.RefreshUpdateAlerts(Updates.UpdateType.ServerPatches | Updates.UpdateType.ServerVersion);
RequestRefreshTreeView();
}
@ -997,8 +996,7 @@ namespace XenAdmin
ThreadPool.QueueUserWorkItem(HealthCheck.CheckForAnalysisResults, connection);
ThreadPool.QueueUserWorkItem(InformHealthCheckEnrollment, connection);
Updates.CheckServerPatches();
Updates.CheckServerVersion();
Updates.RefreshUpdateAlerts(Updates.UpdateType.ServerPatches | Updates.UpdateType.ServerVersion);
Updates.CheckHotfixEligibility(connection);
HealthCheck.SendMetadataToHealthCheck();
@ -1122,8 +1120,7 @@ namespace XenAdmin
// other_config may contain HideFromXenCenter
UpdateToolbars();
// other_config contains which patches to ignore
Updates.CheckServerPatches();
Updates.CheckServerVersion();
Updates.RefreshUpdateAlerts(Updates.UpdateType.ServerPatches | Updates.UpdateType.ServerVersion);
break;
case "name_label":
@ -1169,17 +1166,11 @@ namespace XenAdmin
case "patches":
if (!Helpers.ElyOrGreater(host))
{
Updates.CheckServerPatches();
Updates.CheckServerVersion();
}
Updates.RefreshUpdateAlerts(Updates.UpdateType.ServerPatches | Updates.UpdateType.ServerVersion);
break;
case "updates":
if (Helpers.ElyOrGreater(host))
{
Updates.CheckServerPatches();
Updates.CheckServerVersion();
}
Updates.RefreshUpdateAlerts(Updates.UpdateType.ServerPatches | Updates.UpdateType.ServerVersion);
break;
}
}
@ -2571,11 +2562,11 @@ namespace XenAdmin
navigationPane.SelectObject(obj);
}
private void Updates_CollectionChanged(object sender, CollectionChangeEventArgs e)
private void Updates_CollectionChanged(CollectionChangeEventArgs e)
{
Program.Invoke(this, () =>
{
int updatesCount = Updates.UpdateAlertsCount;
int updatesCount = Updates.UpdateAlerts.Count;
navigationPane.UpdateNotificationsButton(NotificationsSubMode.Updates, updatesCount);
statusLabelUpdates.Text = string.Format(Messages.NOTIFICATIONS_SUBMODE_UPDATES_STATUS, updatesCount);

View File

@ -522,7 +522,7 @@ namespace XenAdmin.TabPages
}
}
DismissAlerts(new List<Alert> {(Alert) clickedRow.Tag});
DismissAlerts((Alert)clickedRow.Tag);
}
private void tsmiDismissAll_Click(object sender, EventArgs e)
@ -559,7 +559,7 @@ namespace XenAdmin.TabPages
return;
var alerts = result == DialogResult.No
? (from DataGridViewRow row in GridViewAlerts.Rows select row.Tag as Alert)
? (from DataGridViewRow row in GridViewAlerts.Rows select row.Tag as Alert).ToArray()
: Alert.Alerts;
DismissAlerts(alerts);
@ -588,7 +588,7 @@ namespace XenAdmin.TabPages
if (GridViewAlerts.SelectedRows.Count > 0)
{
var selectedAlerts = from DataGridViewRow row in GridViewAlerts.SelectedRows select row.Tag as Alert;
DismissAlerts(selectedAlerts);
DismissAlerts(selectedAlerts.ToArray());
}
}
@ -621,7 +621,7 @@ namespace XenAdmin.TabPages
{
toolStripButtonExportAll.Enabled = Alert.NonDismissingAlertCount > 0;
tsmiDismissAll.Enabled = Alert.AllowedToDismiss(Alert.Alerts);
tsmiDismissAll.Enabled = Alert.Alerts.Any(a => a.AllowedToDismiss());
tsmiDismissAll.AutoToolTip = !tsmiDismissAll.Enabled;
tsmiDismissAll.ToolTipText = tsmiDismissAll.Enabled
? string.Empty
@ -629,16 +629,25 @@ namespace XenAdmin.TabPages
? Messages.DELETE_ANY_MESSAGE_RBAC_BLOCKED
: Messages.NO_MESSAGES_TO_DISMISS;
var selectedAlerts = from DataGridViewRow row in GridViewAlerts.SelectedRows
select row.Tag as Alert;
var selectedAlerts = (from DataGridViewRow row in GridViewAlerts.SelectedRows
select row.Tag as Alert).ToArray();
tsmiDismissSelected.Enabled = Alert.AllowedToDismiss(selectedAlerts);
tsmiDismissSelected.Enabled = selectedAlerts.Any(a => a.AllowedToDismiss());
tsmiDismissSelected.AutoToolTip = !tsmiDismissSelected.Enabled;
tsmiDismissSelected.ToolTipText = tsmiDismissSelected.Enabled
? string.Empty
: Messages.DELETE_MESSAGE_RBAC_BLOCKED;
toolStripSplitButtonDismiss.Enabled = tsmiDismissAll.Enabled || tsmiDismissSelected.Enabled;
toolStripSplitButtonDismiss.AutoToolTip = tsmiDismissAll.AutoToolTip || tsmiDismissSelected.AutoToolTip;
if (toolStripSplitButtonDismiss.AutoToolTip)
{
if (!string.IsNullOrEmpty(tsmiDismissAll.ToolTipText))
toolStripSplitButtonDismiss.ToolTipText = tsmiDismissAll.ToolTipText;
else if (!string.IsNullOrEmpty(tsmiDismissSelected.ToolTipText))
toolStripSplitButtonDismiss.ToolTipText = tsmiDismissSelected.ToolTipText;
}
if (toolStripSplitButtonDismiss.DefaultItem != null && !toolStripSplitButtonDismiss.DefaultItem.Enabled)
{
@ -656,23 +665,20 @@ namespace XenAdmin.TabPages
#region Alert dismissal
private void DismissAlerts(IEnumerable<Alert> alerts)
private void DismissAlerts(params Alert[] alerts)
{
var groups = from Alert alert in alerts
where alert != null && !alert.Dismissing
where alert != null && alert.AllowedToDismiss()
group alert by alert.Connection
into g
select new { Connection = g.Key, Alerts = g };
foreach (var g in groups)
{
if (Alert.AllowedToDismiss(g.Connection))
{
foreach (var alert in g.Alerts)
alert.Dismissing = true;
Rebuild();
new DeleteAllAlertsAction(g.Connection, g.Alerts).RunAsync();
}
foreach (var alert in g.Alerts)
alert.Dismissing = true;
new DeleteAllAlertsAction(g.Alerts.ToList(), g.Connection).RunAsync();
}
}
@ -682,7 +688,7 @@ namespace XenAdmin.TabPages
{
var items = new List<ToolStripItem>();
if (Alert.AllowedToDismiss(alert))
if (alert.AllowedToDismiss())
{
var dismiss = new ToolStripMenuItem(Messages.ALERT_DISMISS);
dismiss.Click += ToolStripMenuItemDismiss_Click;
@ -797,8 +803,7 @@ namespace XenAdmin.TabPages
{
foreach (DataGridViewRow row in GridViewAlerts.Rows)
{
var a = row.Tag as Alert;
if (a != null && !a.Dismissing)
if (row.Tag is Alert a && !a.Dismissing)
stream.WriteLine(a.GetAlertDetailsCSVQuotes());
}
}

View File

@ -70,7 +70,6 @@ namespace XenAdmin.TabPages
InitializeComponent();
spinner.SuccessImage = SystemIcons.Information.ToBitmap();
tableLayoutPanel1.Visible = false;
UpdateButtonEnablement();
toolStripSplitButtonDismiss.DefaultItem = dismissAllToolStripMenuItem;
toolStripSplitButtonDismiss.Text = dismissAllToolStripMenuItem.Text;
@ -86,6 +85,8 @@ namespace XenAdmin.TabPages
{
checksQueue--;
}
UpdateButtonEnablement();
}
#region NotificationPage overrides
@ -99,7 +100,7 @@ namespace XenAdmin.TabPages
protected override void RegisterEventHandlers()
{
Updates.RegisterCollectionChanged(UpdatesCollectionChanged);
Updates.UpdateAlertCollectionChanged += UpdatesCollectionChanged;
Updates.RestoreDismissedUpdatesStarted += Updates_RestoreDismissedUpdatesStarted;
Updates.CheckForUpdatesStarted += CheckForUpdates_CheckForUpdatesStarted;
Updates.CheckForUpdatesCompleted += CheckForUpdates_CheckForUpdatesCompleted;
@ -107,7 +108,7 @@ namespace XenAdmin.TabPages
protected override void DeregisterEventHandlers()
{
Updates.DeregisterCollectionChanged(UpdatesCollectionChanged);
Updates.UpdateAlertCollectionChanged -= UpdatesCollectionChanged;
Updates.RestoreDismissedUpdatesStarted -= Updates_RestoreDismissedUpdatesStarted;
Updates.CheckForUpdatesStarted -= CheckForUpdates_CheckForUpdatesStarted;
Updates.CheckForUpdatesCompleted -= CheckForUpdates_CheckForUpdatesCompleted;
@ -117,12 +118,12 @@ namespace XenAdmin.TabPages
#endregion
private void UpdatesCollectionChanged(object sender, CollectionChangeEventArgs e)
private void UpdatesCollectionChanged(CollectionChangeEventArgs e)
{
switch (e.Action)
{
case CollectionChangeAction.Add:
Rebuild(); // rebuild entire alert list to ensure filtering and sorting
case CollectionChangeAction.Refresh:
Rebuild();
break;
case CollectionChangeAction.Remove:
if (e.Element is Alert a)
@ -422,7 +423,7 @@ namespace XenAdmin.TabPages
{
// versions with no minimum patches
var updatesList = new List<string>();
var alerts = new List<Alert>(Updates.UpdateAlerts);
var alerts = Updates.UpdateAlerts;
if (alerts.Count == 0)
return String.Empty;
@ -581,7 +582,7 @@ namespace XenAdmin.TabPages
ToggleCentreWarningVisibility();
var updates = new List<Alert>(Updates.UpdateAlerts);
var updates = Updates.UpdateAlerts;
if (updates.Count == 0)
return;
@ -610,7 +611,7 @@ namespace XenAdmin.TabPages
private void ToggleCentreWarningVisibility()
{
if (byUpdateToolStripMenuItem.Checked && Updates.UpdateAlertsCount == 0)
if (byUpdateToolStripMenuItem.Checked && Updates.UpdateAlerts.Count == 0)
{
spinner.ShowSuccessImage();
labelProgress.Text = Messages.AVAILABLE_UPDATES_NOT_FOUND;
@ -691,7 +692,29 @@ namespace XenAdmin.TabPages
{
if (byUpdateToolStripMenuItem.Checked)
{
toolStripButtonExportAll.Enabled = toolStripSplitButtonDismiss.Enabled = Updates.UpdateAlertsCount > 0;
var allAlerts = Updates.UpdateAlerts;
toolStripButtonExportAll.Enabled = allAlerts.Count > 0;
dismissSelectedToolStripMenuItem.Enabled = (from DataGridViewRow row in dataGridViewUpdates.SelectedRows
select row.Tag as Alert).Any(a => a.AllowedToDismiss());
dismissAllToolStripMenuItem.Enabled = allAlerts.Any(a => a.AllowedToDismiss());
toolStripSplitButtonDismiss.Enabled = dismissAllToolStripMenuItem.Enabled || dismissSelectedToolStripMenuItem.Enabled;
if (toolStripSplitButtonDismiss.DefaultItem != null && !toolStripSplitButtonDismiss.DefaultItem.Enabled)
{
foreach (ToolStripItem item in toolStripSplitButtonDismiss.DropDownItems)
{
if (item.Enabled)
{
toolStripSplitButtonDismiss.DefaultItem = item;
toolStripSplitButtonDismiss.Text = item.Text;
break;
}
}
}
ShowInformationHelper();
}
else
{
@ -700,8 +723,19 @@ namespace XenAdmin.TabPages
}
}
private void ShowInformationHelper(string reason)
private void ShowInformationHelper()
{
string reason = null;
foreach (DataGridViewRow row in dataGridViewUpdates.SelectedRows)
{
if (row.Tag is XenServerPatchAlert alert)
{
reason = alert.CannotApplyReason;
break;
}
}
if (string.IsNullOrEmpty(reason))
{
tableLayoutPanel1.Visible = false;
@ -758,25 +792,22 @@ namespace XenAdmin.TabPages
{
var items = new List<ToolStripItem>();
var patchAlert = alert as XenServerPatchAlert;
if (Alert.AllowedToDismiss(alert))
if (alert.AllowedToDismiss())
{
var dismiss = new ToolStripMenuItem(Messages.ALERT_DISMISS);
dismiss.Click += ToolStripMenuItemDismiss_Click;
items.Add(dismiss);
}
if (patchAlert != null && patchAlert.CanApply && !string.IsNullOrEmpty(patchAlert.Patch.PatchUrl) && patchAlert.RequiredXenCenterVersion == null)
if (alert is XenServerPatchAlert patchAlert && patchAlert.CanApply &&
!string.IsNullOrEmpty(patchAlert.Patch.PatchUrl) && patchAlert.RequiredXenCenterVersion == null)
{
var download = new ToolStripMenuItem(Messages.UPDATES_DOWNLOAD_AND_INSTALL);
download.Click += ToolStripMenuItemDownload_Click;
items.Add(download);
}
var updateAlert = alert as XenServerUpdateAlert;
if (updateAlert != null && updateAlert.RequiredXenCenterVersion != null)
if (alert is XenServerUpdateAlert updateAlert && updateAlert.RequiredXenCenterVersion != null)
{
var downloadNewXenCenter = new ToolStripMenuItem(Messages.UPDATES_DOWNLOAD_REQUIRED_XENCENTER);
downloadNewXenCenter.Click += ToolStripMenuItemDownloadNewXenCenter_Click;
@ -803,28 +834,17 @@ namespace XenAdmin.TabPages
#region Update dismissal
private void DismissUpdates(IEnumerable<Alert> alerts)
private void DismissUpdates(List<Alert> alerts)
{
var groups = from Alert alert in alerts
where alert != null && !alert.Dismissing
group alert by alert.Connection
into g
select new { Connection = g.Key, Alerts = g };
var filtered = alerts.Where(a => a.AllowedToDismiss()).ToList();
foreach (var g in groups)
{
if (Alert.AllowedToDismiss(g.Connection))
{
foreach (Alert alert in g.Alerts)
{
alert.Dismissing = true;
}
toolStripButtonRestoreDismissed.Enabled = false;
DeleteAllAlertsAction action = new DeleteAllAlertsAction(g.Connection, g.Alerts);
action.Completed += DeleteAllAlertsAction_Completed;
action.RunAsync();
}
}
foreach (Alert alert in filtered)
alert.Dismissing = true;
toolStripButtonRestoreDismissed.Enabled = false;
var action = new DeleteAllAlertsAction(filtered);
action.Completed += DeleteAllAlertsAction_Completed;
action.RunAsync();
}
/// <summary>
@ -890,7 +910,7 @@ namespace XenAdmin.TabPages
return;
var alerts = result == DialogResult.No
? from DataGridViewRow row in dataGridViewUpdates.Rows select row.Tag as Alert
? (from DataGridViewRow row in dataGridViewUpdates.Rows select row.Tag as Alert).ToList()
: Updates.UpdateAlerts;
DismissUpdates(alerts);
@ -923,7 +943,7 @@ namespace XenAdmin.TabPages
if (dataGridViewUpdates.SelectedRows.Count > 0)
{
var selectedAlerts = from DataGridViewRow row in dataGridViewUpdates.SelectedRows select row.Tag as Alert;
DismissUpdates(selectedAlerts);
DismissUpdates(selectedAlerts.ToList());
}
}
@ -961,7 +981,7 @@ namespace XenAdmin.TabPages
}
}
DismissUpdates(new List<Alert> { (Alert)clickedRow.Tag });
DismissUpdates(new List<Alert> {(Alert)clickedRow.Tag});
}
private DataGridViewRow FindAlertRow(ToolStripMenuItem toolStripMenuItem)
@ -1071,17 +1091,10 @@ namespace XenAdmin.TabPages
private void dataGridViewUpdates_SelectionChanged(object sender, EventArgs e)
{
string reason = null;
if (_buildInProgress)
return;
if (dataGridViewUpdates.SelectedRows.Count > 0)
{
var alert = dataGridViewUpdates.SelectedRows[0].Tag as XenServerPatchAlert;
if (alert != null)
reason = alert.CannotApplyReason;
}
ShowInformationHelper(reason);
UpdateButtonEnablement();
}
private void dataGridViewUpdates_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
@ -1249,15 +1262,15 @@ namespace XenAdmin.TabPages
if (exportAll)
{
foreach (Alert a in Updates.UpdateAlerts)
var alerts = Updates.UpdateAlerts;
foreach (Alert a in alerts)
stream.WriteLine(a.GetUpdateDetailsCSVQuotes());
}
else
{
foreach (DataGridViewRow row in dataGridViewUpdates.Rows)
{
var a = row.Tag as Alert;
if (a != null)
if (row.Tag is Alert a)
stream.WriteLine(a.GetUpdateDetailsCSVQuotes());
}
}

View File

@ -250,7 +250,7 @@ namespace XenAdmin.Wizards.PatchingWizard
{
CleanUploadedPatches();
RemoveDownloadedPatches();
Updates.CheckServerPatches();
Updates.RefreshUpdateAlerts(Updates.UpdateType.ServerPatches);
base.FinishWizard();
}

View File

@ -364,7 +364,7 @@ namespace XenAdmin.Wizards.PatchingWizard
{
try
{
var updates = Updates.UpdateAlerts.ToList();
var updates = Updates.UpdateAlerts;
if (dataGridViewPatches.SortedColumn != null)
{
@ -484,7 +484,7 @@ namespace XenAdmin.Wizards.PatchingWizard
private void _backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
Updates.CheckServerPatches();
Updates.RefreshUpdateAlerts(Updates.UpdateType.ServerPatches);
}
private void _backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)

View File

@ -29,24 +29,35 @@
* SUCH DAMAGE.
*/
using XenAdmin.Network;
using XenAPI;
namespace XenAdmin.Actions
{
public class DestroyMessageAction : PureAsyncAction
{
private readonly string OpaqueRef;
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public DestroyMessageAction(IXenConnection connection, string messageopaqueref)
: base(connection, "destroying message", string.Format("message opaque_ref = {0}", messageopaqueref), true)
private readonly string _opaqueRef;
public DestroyMessageAction(Message message)
: base(message.Connection, "destroying message", string.Format("message opaque_ref = {0}", message.opaque_ref), true)
{
OpaqueRef = messageopaqueref;
_opaqueRef = message.opaque_ref;
}
protected override void Run()
{
Message.destroy(Session, OpaqueRef);
try
{
Message.destroy(Session, _opaqueRef);
}
catch (Failure exn)
{
if (exn.ErrorDescription[0] != Failure.HANDLE_INVALID)
throw;
log.Error(exn);
}
}
}
}

View File

@ -216,24 +216,14 @@ namespace XenAdmin.Alerts
/// </summary>
public DateTime Timestamp => _timestamp;
/// <summary>
/// Dismisses the Alert: marks it as dealt with in some way. May only be called once.
/// </summary>
public virtual void Dismiss()
{
RemoveAlert(this);
}
/// <summary>
/// Dismiss the alert, making a single API call if this is a remote message (Dismiss will launch a complete
/// action).
///
/// The normal behaviour here is the same as Dismiss() -- it's only when overridden by MessageAlert that there
/// is a difference.
/// </summary>
public virtual void DismissSingle(Session s)
public virtual bool AllowedToDismiss()
{
Dismiss();
return !Dismissing;
}
public virtual bool IsDismissed()
@ -278,7 +268,7 @@ namespace XenAdmin.Alerts
/// </summary>
public abstract string HelpID { get; }
public IXenConnection Connection;
public IXenConnection Connection { get; protected set; }
public virtual bool Equals(Alert other)
{
@ -358,52 +348,8 @@ namespace XenAdmin.Alerts
sortResult = string.Compare(alert1.uuid, alert2.uuid);
return sortResult;
}
#region Update dismissal
public static bool AllowedToDismiss(Alert alert)
{
return AllowedToDismiss(new[] { alert });
}
public static bool AllowedToDismiss(IEnumerable<Alert> alerts)
{
var alertConnections = (from Alert alert in alerts
where alert != null && !alert.Dismissing
let con = alert.Connection
select con).Distinct();
return alertConnections.Any(AllowedToDismiss);
}
/// <summary>
/// Checks the user has sufficient RBAC privileges to clear updates on a given connection
/// </summary>
public static bool AllowedToDismiss(IXenConnection c)
{
// check if local alert
if (c == null)
return true;
// have we disconnected? Alert will disappear soon, but for now block dismissal.
if (c.Session == null)
return false;
if (c.Session.IsLocalSuperuser)
return true;
List<Role> rolesAbleToCompleteAction = Role.ValidRoleList("Message.destroy", c);
foreach (Role possibleRole in rolesAbleToCompleteAction)
{
if (c.Session.Roles.Contains(possibleRole))
return true;
}
return false;
}
#endregion
}
public enum AlertPriority
{
/// <summary>

View File

@ -11954,7 +11954,7 @@ namespace XenAdmin {
}
/// <summary>
/// Looks up a localized string similar to There are no alerts remaining which you have permission to dismiss..
/// Looks up a localized string similar to You have no permission to dismiss the selected alerts..
/// </summary>
public static string DELETE_MESSAGE_RBAC_BLOCKED {
get {

View File

@ -4278,7 +4278,7 @@ This will also delete its subfolders.</value>
<value>Are you sure you want to delete '{0}'? This operation cannot be undone.</value>
</data>
<data name="DELETE_MESSAGE_RBAC_BLOCKED" xml:space="preserve">
<value>There are no alerts remaining which you have permission to dismiss.</value>
<value>You have no permission to dismiss the selected alerts.</value>
</data>
<data name="DELETE_PATCH" xml:space="preserve">
<value>Delete Patch</value>