/* 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; 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.Text; using XenAdmin.Alerts.Types; using XenCenterLib; namespace XenAdmin.Core { public class Updates { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public static event Action CheckForUpdatesCompleted; public static event Action CheckForUpdatesStarted; public static event Action RestoreDismissedUpdatesStarted; private static readonly object downloadedUpdatesLock = new object(); private static List XenServerVersionsForAutoCheck = new List(); private static List XenServerPatches = new List(); private static List XenCenterVersions = new List(); public static List XenServerVersions = new List(); private static readonly object updateAlertsLock = new object(); private static readonly ChangeableList updateAlerts = new ChangeableList(); public const string IgnorePatchKey = "XenCenter.IgnorePatches"; public const string LAST_SEEN_SERVER_VERSION_KEY = "XenCenter.LastSeenServerVersion"; public static IEnumerable UpdateAlerts { get { return updateAlerts; } } public static int UpdateAlertsCount { get { return updateAlerts.Count; } } private static void AddUpate(Alert update) { try { lock (updateAlertsLock) { if(!updateAlerts.Contains(update)) { updateAlerts.Add(update); } } } catch (Exception e) { log.Error("Failed to add update", e); } } 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); } } /// /// 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. /// /// public static void DismissUpdates(List toBeDismissed) { if (toBeDismissed.Count == 0) return; foreach(IXenConnection connection in ConnectionsManager.XenConnectionsCopy) { if (!Alert.AllowedToDismiss(connection)) continue; XenAPI.Pool pool = Helpers.GetPoolOfOne(connection); if (pool == null) continue; Dictionary other_config = pool.other_config; foreach (Alert alert in toBeDismissed) { if (alert is XenServerPatchAlert) { if (other_config.ContainsKey(IgnorePatchKey)) { List current = new List(other_config[IgnorePatchKey].Split(',')); if (current.Contains(((XenServerPatchAlert)alert).Patch.Uuid, StringComparer.OrdinalIgnoreCase)) continue; current.Add(((XenServerPatchAlert)alert).Patch.Uuid); other_config[IgnorePatchKey] = string.Join(",", current.ToArray()); } else { other_config.Add(IgnorePatchKey, ((XenServerPatchAlert)alert).Patch.Uuid); } } if (alert is XenServerVersionAlert) { if (other_config.ContainsKey(LAST_SEEN_SERVER_VERSION_KEY)) { List current = new List(other_config[LAST_SEEN_SERVER_VERSION_KEY].Split(',')); if (current.Contains(((XenServerVersionAlert)alert).Version.Version.ToString())) continue; current.Add(((XenServerVersionAlert)alert).Version.Version.ToString()); other_config[LAST_SEEN_SERVER_VERSION_KEY] = string.Join(",", current.ToArray()); } else { other_config.Add(LAST_SEEN_SERVER_VERSION_KEY, ((XenServerVersionAlert)alert).Version.Version.ToString()); } } Updates.RemoveUpdate(alert); } XenAPI.Pool.set_other_config(connection.Session, pool.opaque_ref, other_config); } } private static Alert FindUpdate(Predicate predicate) { lock (updateAlertsLock) return updateAlerts.Find(predicate); } /// /// If AutomaticCheck is enabled it checks for updates regardless the /// value of the parameter force. If AutomaticCheck is disabled it /// checks for all update types if force is true; forceRefresh causes /// the check for update action to run and refresh the Updates page /// public static void CheckForUpdates(bool force, bool forceRefresh = false) { if (Helpers.CommonCriteriaCertificationRelease) return; if (Properties.Settings.Default.AllowXenCenterUpdates || Properties.Settings.Default.AllowXenServerUpdates || Properties.Settings.Default.AllowPatchesUpdates || force || forceRefresh) { var action = CreateDownloadUpdatesXmlAction( CheckForUpdatesUrl, Properties.Settings.Default.AllowXenCenterUpdates || force, Properties.Settings.Default.AllowXenServerUpdates || force, Properties.Settings.Default.AllowPatchesUpdates || force); action.Completed += actionCompleted; if (CheckForUpdatesStarted != null) CheckForUpdatesStarted(); action.RunAsync(); } } public static DownloadUpdatesXmlAction CreateDownloadUpdatesXmlAction(string checkForUpdatesUrl, bool checkForXenCenter = false, bool checkForServerVersion = false, bool checkForPatches = false) { string userAgent = string.Format("{0}/{1}.{2} ({3}-bit)", Branding.BRAND_CONSOLE, Branding.XENCENTER_VERSION, Program.Version.Revision.ToString(), IntPtr.Size * 8); string userAgentId = GetUniqueIdHash(); return new DownloadUpdatesXmlAction(checkForXenCenter, checkForServerVersion, checkForPatches, userAgent, userAgentId, checkForUpdatesUrl); } internal static string GetUniqueIdHash() { string uniqueIdHash = "nil"; try { var managementObj = new System.Management.ManagementObject("Win32_OperatingSystem=@"); string serialNumber = (string)managementObj["SerialNumber"]; if (!string.IsNullOrWhiteSpace(serialNumber)) { var serialBytes = Encoding.ASCII.GetBytes(serialNumber); using (var md = new System.Security.Cryptography.MD5CryptoServiceProvider()) // MD5 to keep it short enough as this hash is not used for security in any way { var hash = md.ComputeHash(serialBytes); uniqueIdHash = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } } } catch (Exception ex) { log.Error(ex); } return uniqueIdHash; } /// /// It does exactly what CheckForUpdates(true) does, but this is sync and shows an ActionProgressDialog while running /// /// true if the action has succeeded public static bool CheckForUpdatesSync(Control parentForProgressDialog) { if (Helpers.CommonCriteriaCertificationRelease) return false; var action = CreateDownloadUpdatesXmlAction(CheckForUpdatesUrl, true, true, true); action.Completed += actionCompleted; if (CheckForUpdatesStarted != null) CheckForUpdatesStarted(); if (parentForProgressDialog != null) { using (var dialog = new ActionProgressDialog(action, ProgressBarStyle.Marquee)) dialog.ShowDialog(parentForProgressDialog); } else { action.RunExternal(action.Session); } return action.Succeeded; } public static string CheckForUpdatesUrl { get { return Registry.CustomUpdatesXmlLocation ?? Branding.CheckForUpdatesUrl; } } private static void actionCompleted(ActionBase sender) { Program.AssertOffEventThread(); DownloadUpdatesXmlAction action = sender as DownloadUpdatesXmlAction; if (action == null) 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 { if (action.Exception != null) { if (action.Exception is System.Net.Sockets.SocketException) { errorMessage = Messages.AVAILABLE_UPDATES_NETWORK_ERROR; } else { // Clean up and remove excess newlines, carriage returns, trailing nonsense string errorText = action.Exception.Message.Trim(); errorText = System.Text.RegularExpressions.Regex.Replace(errorText, @"\r\n+", ""); errorMessage = string.Format(Messages.AVAILABLE_UPDATES_ERROR, errorText); } } if (string.IsNullOrEmpty(errorMessage)) errorMessage = Messages.AVAILABLE_UPDATES_INTERNAL_ERROR; } if (CheckForUpdatesCompleted != null) CheckForUpdatesCompleted(succeeded, errorMessage); } public static void RegisterCollectionChanged(CollectionChangeEventHandler handler) { updateAlerts.CollectionChanged += handler; } public static void DeregisterCollectionChanged(CollectionChangeEventHandler handler) { updateAlerts.CollectionChanged -= handler; } public static List NewXenCenterUpdateAlerts(List xenCenterVersions, Version currentProgramVersion) { if (Helpers.CommonCriteriaCertificationRelease) return null; var alerts = new List(); 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; latest = latestVersions.FirstOrDefault(xcv => xcv.Lang == Program.CurrentLanguage) ?? latestVersions.FirstOrDefault(xcv => string.IsNullOrEmpty(xcv.Lang)); if (IsSuitableForXenCenterAlert(latest, currentProgramVersion)) alerts.Add(new XenCenterUpdateAlert(latest)); var latestCrVersions = from v in xenCenterVersions where v.LatestCr select v; latestCr = latestCrVersions.FirstOrDefault(xcv => xcv.Lang == Program.CurrentLanguage) ?? latestCrVersions.FirstOrDefault(xcv => string.IsNullOrEmpty(xcv.Lang)); if (latestCr != latest && IsSuitableForXenCenterAlert(latestCr, currentProgramVersion)) alerts.Add(new XenCenterUpdateAlert(latestCr)); } if (alerts.Count == 0) { log.Info(string.Format("Not alerting XenCenter update - latest = {0}, latestcr = {1}, detected = {2}", latest != null ? latest.VersionAndLang : "", latestCr != null ? latestCr.VersionAndLang : "", Program.VersionAndLanguage)); } return alerts; } private static bool IsSuitableForXenCenterAlert(XenCenterVersion toUse, Version currentProgramVersion) { if (toUse == null) return false; return toUse.Version > currentProgramVersion || (toUse.Version == currentProgramVersion && toUse.Lang == Program.CurrentLanguage && !FriendlyNameManager.IsCultureLoaded(Program.CurrentCulture)); } public static List NewXenServerPatchAlerts(List xenServerVersions, List xenServerPatches) { if (Helpers.CommonCriteriaCertificationRelease) return null; var alerts = new List(); var xenServerVersionsAsUpdates = xenServerVersions.Where(v => v.IsVersionAvailableAsAnUpdate); foreach (IXenConnection xenConnection in ConnectionsManager.XenConnectionsCopy) { Host master = Helpers.GetMaster(xenConnection); Pool pool = Helpers.GetPoolOfOne(xenConnection); List hosts = xenConnection.Cache.Hosts.ToList(); if (master == null || pool == null) continue; var serverVersions = new List(); foreach (Host host in hosts) { var serverVersion = GetServerVersions(host, xenServerVersions); serverVersions.AddRange(serverVersion); } serverVersions = serverVersions.Distinct().ToList(); if (serverVersions.Count == 0) continue; foreach (XenServerVersion xenServerVersion in serverVersions) { XenServerVersion version = xenServerVersion; List patches = xenServerPatches.FindAll(patch => version.Patches.Contains(patch)); if (patches.Count == 0) continue; foreach (XenServerPatch xenServerPatch in patches) { XenServerVersion newServerVersion = xenServerVersionsAsUpdates.FirstOrDefault(newVersion => newVersion.PatchUuid.Equals(xenServerPatch.Uuid, StringComparison.OrdinalIgnoreCase)); var alert = new XenServerPatchAlert(xenServerPatch, newServerVersion); var existingAlert = alerts.Find(al => al.Equals(alert)); if (existingAlert != null) alert = existingAlert; else alerts.Add(alert); if (!xenConnection.IsConnected) continue; XenServerPatch serverPatch = xenServerPatch; var noPatchHosts = hosts.Where(host => PatchCanBeInstalledOnHost(serverPatch, host)).ToList(); if (noPatchHosts.Count() == hosts.Count) alert.IncludeConnection(xenConnection); else alert.IncludeHosts(noPatchHosts); } } } return alerts; } private static bool PatchCanBeInstalledOnHost(XenServerPatch serverPatch, Host host) { Debug.Assert(serverPatch != null); Debug.Assert(host != null); // A patch is applicable to host if the patch is amongst the current version patches var patchIsApplicable = GetServerVersions(host, XenServerVersions).Any(v => v.Patches.Contains(serverPatch)); if (!patchIsApplicable) return false; // A patch can be installed on a host if: // 1. it is not already installed and // 2. the host has all the required patches installed and // 3. the host doesn't have any of the conflicting patches installed bool elyOrGreater = Helpers.ElyOrGreater(host); var appliedUpdates = host.AppliedUpdates(); var appliedPatches = host.AppliedPatches(); // 1. patch is not already installed if (elyOrGreater && appliedUpdates.Any(update => string.Equals(update.uuid, serverPatch.Uuid, StringComparison.OrdinalIgnoreCase))) return false; if (!elyOrGreater && appliedPatches.Any(patch => string.Equals(patch.uuid, serverPatch.Uuid, StringComparison.OrdinalIgnoreCase))) return false; // 2. the host has all the required patches installed if (serverPatch.RequiredPatches != null && serverPatch.RequiredPatches.Count > 0 && !serverPatch.RequiredPatches .All(requiredPatchUuid => elyOrGreater && appliedUpdates.Any(update => string.Equals(update.uuid, requiredPatchUuid, StringComparison.OrdinalIgnoreCase)) || !elyOrGreater && appliedPatches.Any(patch => string.Equals(patch.uuid, requiredPatchUuid, StringComparison.OrdinalIgnoreCase)) ) ) return false; // 3. the host doesn't have any of the conflicting patches installed if (serverPatch.ConflictingPatches != null && serverPatch.ConflictingPatches.Count > 0 && serverPatch.ConflictingPatches .Any(conflictingPatchUuid => elyOrGreater && appliedUpdates.Any(update => string.Equals(update.uuid, conflictingPatchUuid, StringComparison.OrdinalIgnoreCase)) || !elyOrGreater && appliedPatches.Any(patch => string.Equals(patch.uuid, conflictingPatchUuid, StringComparison.OrdinalIgnoreCase)) ) ) return false; return true; } /// /// If parameter is null, it returns latestcr XenCenter version if it is greater than current XC version, /// or null, if the current XC version is latestcr. /// If parameter is not null, it returns the minimum XenCenter version if it is greater than the current XC version, /// or null, if the minimum XC version couldn't be found or the current XC version is enough. /// /// /// public static XenCenterVersion GetRequiredXenCenterVersion(XenServerVersion serverVersion) { if (XenCenterVersions.Count == 0) return null; var currentProgramVersion = Program.Version; if (currentProgramVersion == new Version(0, 0, 0, 0)) return null; if (serverVersion == null) return XenCenterVersions.FirstOrDefault(xcv => xcv.LatestCr && xcv.Version > currentProgramVersion); var minXcVersion = serverVersion.MinimumXcVersion; if (minXcVersion == null) return null; var minimumXcVersion = XenCenterVersions.FirstOrDefault(xcv => xcv.Version == minXcVersion); return minimumXcVersion != null && minimumXcVersion.Version > currentProgramVersion ? minimumXcVersion : null; } /// /// This method returns the minimal set of patches for a host if this class already has information about them. Otherwise it returns empty list. /// Calling this function will not initiate a download or update. /// /// /// public static List RecommendedPatchesForHost(Host host) { var recommendedPatches = new List(); if (XenServerVersions == null) return null; var serverVersions = GetServerVersions(host, XenServerVersions); if (serverVersions.Count != 0) { var minimumPatches = serverVersions[0].MinimalPatches; if (minimumPatches == null) //unknown return null; bool elyOrGreater = Helpers.ElyOrGreater(host); var appliedPatches = host.AppliedPatches(); var appliedUpdates = host.AppliedUpdates(); if (elyOrGreater) { recommendedPatches = minimumPatches.FindAll(p => !appliedUpdates.Any(au => string.Equals(au.uuid, p.Uuid, StringComparison.OrdinalIgnoreCase))); } else { recommendedPatches = minimumPatches.FindAll(p => !appliedPatches.Any(ap => string.Equals(ap.uuid, p.Uuid, StringComparison.OrdinalIgnoreCase))); } } return recommendedPatches; } public static List GetMinimalPatches(IXenConnection conn) { var version = GetCommonServerVersionOfHostsInAConnection(conn, XenServerVersions); return GetMinimalPatches(version); } public static List GetMinimalPatches(Host host) { if (host == null || host.Connection == null || XenServerVersions== null) return null; var hostVersions = GetServerVersions(host, XenServerVersions); return GetMinimalPatches(hostVersions.FirstOrDefault()); } private static List GetMinimalPatches(XenServerVersion version) { if (version == null || version.MinimalPatches == null) return null; var minimalPatches = new List(version.MinimalPatches); // if there is a "new version" update in the update sequence, also add the minimal patches of this new version if (minimalPatches.Count > 0) { // assuming that the new version update (if there is one) is the last one in the minimal patches list var lastUpdate = minimalPatches[minimalPatches.Count - 1]; var newServerVersion = XenServerVersions.FirstOrDefault( v => v.IsVersionAvailableAsAnUpdate && v.PatchUuid.Equals(lastUpdate.Uuid, StringComparison.OrdinalIgnoreCase)); if (newServerVersion != null && newServerVersion.MinimalPatches != null) minimalPatches.AddRange(newServerVersion.MinimalPatches); } return minimalPatches; } /// /// Gets an upgrade sequence that contains a version upgrade, optionally followed by the minimal patches for the new version /// /// The alert that refers the version-update /// Also add the minimum patches for the new version (true) or not (false). public static List GetMinimalPatches(XenServerPatchAlert alert, bool updateTheNewVersion) { Debug.Assert(alert != null); var minimalPatches = new List {alert.Patch}; // if it's a version updgrade the min sequence will be this patch (the upgrade) and the min patches for the new version if (updateTheNewVersion && alert.NewServerVersion != null && alert.NewServerVersion.MinimalPatches != null) { minimalPatches.AddRange(alert.NewServerVersion.MinimalPatches); } return minimalPatches; } /// /// Gets all the patches for the given connection /// public static List GetAllPatches(IXenConnection conn) { var version = GetCommonServerVersionOfHostsInAConnection(conn, XenServerVersions); return GetAllPatches(version); } /// /// Gets an upgrade sequence that contains a version upgrade, optionally followed by all the patches for the new version /// public static List GetAllPatches(XenServerPatchAlert alert, bool updateTheNewVersion) { Debug.Assert(alert != null); var allPatches = new List { alert.Patch }; // if it's a version updgrade the update sequence will be this patch (the upgrade) and the patches for the new version if (updateTheNewVersion && alert.NewServerVersion != null) { var newVersionPatches = GetAllPatches(alert.NewServerVersion); if (newVersionPatches != null) allPatches.AddRange(newVersionPatches); } return allPatches; } /// /// Gets all the patches for the given server version, including the cumulative updates and the patches on those /// private static List GetAllPatches(XenServerVersion version) { if (version == null || version.Patches == null) return null; // exclude patches that are new versions (we will include the cumulative updates later) var excludedUuids = XenServerVersions.Where(v => v.IsVersionAvailableAsAnUpdate).Select(v => v.PatchUuid); var allPatches = new List(version.Patches.Where(p => !excludedUuids.Contains(p.Uuid))); // if there is a "new version" update in the minimal patches (e.g. a cumulative update), also add this new version update and all the patches on it if (version.MinimalPatches != null && version.MinimalPatches.Count > 0) { // assuming that the new version update (if there is one) is the last one in the minimal patches list var lastUpdate = version.MinimalPatches[version.MinimalPatches.Count - 1]; var newServerVersion = XenServerVersions.FirstOrDefault( v => v.IsVersionAvailableAsAnUpdate && v.PatchUuid.Equals(lastUpdate.Uuid, StringComparison.OrdinalIgnoreCase)); if (newServerVersion != null) { allPatches.Add(lastUpdate); if (newServerVersion.Patches != null) allPatches.AddRange(newServerVersion.Patches); } } return allPatches; } /// /// Returns a XenServerVersion if all hosts of the pool have the same version /// Returns null if it is unknown or they don't match /// /// private static XenServerVersion GetCommonServerVersionOfHostsInAConnection(IXenConnection connection, List xsVersions) { if (connection == null || xsVersions == null) return null; XenServerVersion commonXenServerVersion = null; List hosts = connection.Cache.Hosts.ToList(); foreach (Host host in hosts) { var hostVersions = GetServerVersions(host, xsVersions); var foundVersion = hostVersions.FirstOrDefault(); if (foundVersion == null) { return null; } else { if (commonXenServerVersion == null) { commonXenServerVersion = foundVersion; } else { if (commonXenServerVersion != foundVersion) return null; } } } return commonXenServerVersion; } public static List GetPatchSequenceForHost(Host h, List minimalPatches) { if (minimalPatches == null) return null; var appliedUpdateUuids = Helpers.ElyOrGreater(h) ? h.AppliedUpdates().Select(u => u.uuid).ToList() : h.AppliedPatches().Select(p => p.uuid).ToList(); var neededPatches = new List(minimalPatches); var sequence = new List(); //Iterate through minimalPatches once; in each iteration, move the first item from L0 //that has its dependencies met to the end of the update schedule for (int i = 0; i < neededPatches.Count; i++) { var p = neededPatches[i]; if (appliedUpdateUuids.Any(apu => string.Equals(apu, p.Uuid, StringComparison.OrdinalIgnoreCase))) continue; //the patch has been applied if (p.RequiredPatches == null || p.RequiredPatches.Count == 0 // no requirements || p.RequiredPatches.All(rp => //all the required patches are already in the sequence or have already been applied sequence.Any(useqp => string.Equals(useqp.Uuid, rp, StringComparison.OrdinalIgnoreCase)) || appliedUpdateUuids.Any(apu => string.Equals(apu, rp, StringComparison.OrdinalIgnoreCase)) ) ) { // this patch can be added to the upgrade sequence now sequence.Add(p); // by now the patch has either been added to the upgrade sequence or something already contains it among the installed patches neededPatches.RemoveAt(i); //resetting position - the loop will start on 0. item i = -1; } } return sequence; } public static List NewXenServerVersionAlerts(List xenServerVersions) { if (Helpers.CommonCriteriaCertificationRelease) return null; var latestVersion = xenServerVersions.FindAll(item => item.Latest).OrderByDescending(v => v.Version).FirstOrDefault(); var latestCrVersion = xenServerVersions.FindAll(item => item.LatestCr).OrderByDescending(v => v.Version).FirstOrDefault(); List alerts = new List(); if (latestVersion != null) alerts.Add(CreateAlertForXenServerVersion(latestVersion)); if (latestCrVersion != null && latestCrVersion != latestVersion) alerts.Add(CreateAlertForXenServerVersion(latestCrVersion)); return alerts; } private static XenServerVersionAlert CreateAlertForXenServerVersion(XenServerVersion version) { var alert = new XenServerVersionAlert(version); // the patch that installs this version, if any var patch = XenServerPatches.FirstOrDefault(p => p.Uuid.Equals(version.PatchUuid, StringComparison.OrdinalIgnoreCase)); foreach (IXenConnection xc in ConnectionsManager.XenConnectionsCopy) { if (!xc.IsConnected) continue; Host master = Helpers.GetMaster(xc); Pool pool = Helpers.GetPoolOfOne(xc); List hosts = xc.Cache.Hosts.ToList(); if (master == null || pool == null) continue; // Show the Upgrade alert for a host if: // - the host version is older than this version AND // - there is no patch (amongst the current version patches) that can update to this version OR, if there is a patch, the patch cannot be installed var outOfDateHosts = hosts.Where(host => new Version(Helpers.HostProductVersion(host)) < version.Version && (patch == null || !PatchCanBeInstalledOnHost(patch, host))).ToList(); if (outOfDateHosts.Count() == hosts.Count) alert.IncludeConnection(xc); else alert.IncludeHosts(outOfDateHosts); } return alert; } public static List GetServerVersions(Host host, List xenServerVersions) { var serverVersions = xenServerVersions.FindAll(version => { if (version.BuildNumber != string.Empty) return (host.BuildNumberRaw() == version.BuildNumber); return Helpers.HostProductVersion(host) == version.Version.ToString(); }); return serverVersions; } public static void CheckServerVersion() { var alerts = NewXenServerVersionAlerts(XenServerVersionsForAutoCheck); if (alerts == null || alerts.Count == 0) return; alerts.ForEach(a => CheckUpdate(a)); } public static void CheckServerPatches() { var alerts = NewXenServerPatchAlerts(XenServerVersions, XenServerPatches); if (alerts == null) return; alerts.ForEach(a => CheckUpdate(a)); } private static void CheckUpdate(XenServerUpdateAlert alert) { var existingAlert = FindUpdate(a => a.Equals(alert)); if (existingAlert != null && alert.CanIgnore) RemoveUpdate(existingAlert); else if (existingAlert != null) ((XenServerUpdateAlert)existingAlert).CopyConnectionsAndHosts(alert); else if (!alert.CanIgnore) AddUpate(alert); } public static void RestoreDismissedUpdates() { var actions = new List(); foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy) actions.Add(new RestoreDismissedUpdatesAction(connection)); var action = new ParallelAction(Messages.RESTORE_DISMISSED_UPDATES, Messages.RESTORING, Messages.COMPLETED, actions, true, false); action.Completed += action_Completed; if (RestoreDismissedUpdatesStarted != null) RestoreDismissedUpdatesStarted(); action.RunAsync(); } private static void action_Completed(ActionBase action) { Program.Invoke(Program.MainWindow, () => { Properties.Settings.Default.LatestXenCenterSeen = ""; Settings.TrySaveSettings(); CheckForUpdates(true); }); } private static XenServerPatchAlert FindPatchAlert(Predicate predicate) { var existingAlert = FindUpdate(a => a is XenServerPatchAlert && predicate(((XenServerPatchAlert)a).Patch)); if (existingAlert != null) return existingAlert as XenServerPatchAlert; if (XenServerPatches.Count == 0) return null; var xenServerPatch = XenServerPatches.FirstOrDefault(p => predicate(p)); if (xenServerPatch == null) return null; var newServerVersion = XenServerVersions.FirstOrDefault(v => v.IsVersionAvailableAsAnUpdate && v.PatchUuid.Equals(xenServerPatch.Uuid, StringComparison.OrdinalIgnoreCase)); return new XenServerPatchAlert(xenServerPatch, newServerVersion); } public static XenServerPatchAlert FindPatchAlertByUuid(string uuid) { if (string.IsNullOrEmpty(uuid)) return null; return FindPatchAlert(p => p.Uuid.Equals(uuid, StringComparison.OrdinalIgnoreCase)); } public static XenServerPatchAlert FindPatchAlertByName(string patchName) { if (string.IsNullOrEmpty(patchName)) return null; return FindPatchAlert(p => p.Name.Equals(patchName, StringComparison.OrdinalIgnoreCase)); } public static hotfix_eligibility HotfixEligibility(Host host, out XenServerVersion xenServerVersion) { xenServerVersion = null; if (XenServerVersions == null) return hotfix_eligibility.all; xenServerVersion = GetServerVersions(host, XenServerVersions).FirstOrDefault(); return xenServerVersion?.HotfixEligibility ?? hotfix_eligibility.all; } public static void CheckHotfixEligibility(IXenConnection connection) { var master = Helpers.GetMaster(connection); if (master == null) return; var hotfixEligibility = HotfixEligibility(master, out var xenServerVersion); if (xenServerVersion == null || hotfixEligibility == hotfix_eligibility.all || hotfixEligibility == hotfix_eligibility.premium && !master.IsFreeLicenseOrExpired()) { Alert.RemoveAlert(a => a is HotfixEligibilityAlert && connection.Equals(a.Connection)); return; } var alertIndex = Alert.FindAlertIndex(a => a is HotfixEligibilityAlert alert && connection.Equals(alert.Connection) && xenServerVersion == alert.Version); if (alertIndex == -1) { Alert.RemoveAlert(a => a is HotfixEligibilityAlert && connection.Equals(a.Connection)); // ensure that there is no other alert for this connection Alert.AddAlert(new HotfixEligibilityAlert(connection, xenServerVersion)); } else Alert.RefreshAlertAt(alertIndex); } public static void CheckHotfixEligibility() { var alerts = new List(); foreach (var connection in ConnectionsManager.XenConnectionsCopy) { if (!connection.IsConnected) continue; var master = Helpers.GetMaster(connection); if (master == null) continue; var hotfixEligibility = HotfixEligibility(master, out var xenServerVersion); if (xenServerVersion == null || hotfixEligibility == hotfix_eligibility.all || hotfixEligibility == hotfix_eligibility.premium && !master.IsFreeLicenseOrExpired()) continue; alerts.Add(new HotfixEligibilityAlert(connection, xenServerVersion)); } Alert.RemoveAlert(a => a is HotfixEligibilityAlert); Alert.AddAlertRange(alerts); } } }