diff --git a/XenAdmin/Core/Updates.cs b/XenAdmin/Core/Updates.cs index 03a24c885..532bde780 100644 --- a/XenAdmin/Core/Updates.cs +++ b/XenAdmin/Core/Updates.cs @@ -573,51 +573,28 @@ namespace XenAdmin.Core return recommendedPatches; } - public static UpgradeSequence GetUpgradeSequence(IXenConnection conn) + public static List GetMinimalPatches(IXenConnection conn) { - if (XenServerVersions == null) - return null; - - Host master = Helpers.GetMaster(conn); - if (master == null) - return null; - var version = GetCommonServerVersionOfHostsInAConnection(conn, XenServerVersions); - - if (version != null) - { - if (version.MinimalPatches == null) - return null; - - var uSeq = new UpgradeSequence(); - uSeq.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 (uSeq.MinimalPatches.Count > 0) - { - // assuming that the new version update (if there is one) is the last one in the minimal patches list - var lastUpdate = uSeq.MinimalPatches[uSeq.MinimalPatches.Count - 1]; - - var newServerVersion = XenServerVersions.FirstOrDefault( - v => v.IsVersionAvailableAsAnUpdate && v.PatchUuid.Equals(lastUpdate.Uuid, StringComparison.OrdinalIgnoreCase)); - - if (newServerVersion != null && newServerVersion.MinimalPatches != null) - uSeq.MinimalPatches.AddRange(newServerVersion.MinimalPatches); - } - - List hosts = conn.Cache.Hosts.ToList(); - - foreach (Host h in hosts) - { - uSeq[h] = GetUpgradeSequenceForHost(h, uSeq.MinimalPatches); - } - - return uSeq; - } - else - { + 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; } /// @@ -626,43 +603,25 @@ namespace XenAdmin.Core /// Connection for the pool /// The alert that refers the version-update /// Also add the minimum patches for the new version (true) or not (false). - /// - public static UpgradeSequence GetUpgradeSequence(IXenConnection conn, XenServerPatchAlert alert, bool updateTheNewVersion) + public static List GetMinimalPatches(IXenConnection conn, XenServerPatchAlert alert, bool updateTheNewVersion) { Debug.Assert(conn != null); Debug.Assert(alert != null); - var uSeq = new UpgradeSequence(); - - if (XenServerVersions == null) - return null; - - Host master = Helpers.GetMaster(conn); - if (master == null) - return null; - - var version = GetCommonServerVersionOfHostsInAConnection(conn, XenServerVersions); - // the pool has to be homogeneous - if (version != null) - { - uSeq.MinimalPatches = new List(); - uSeq.MinimalPatches.Add(alert.Patch); + var version = GetCommonServerVersionOfHostsInAConnection(conn, XenServerVersions); + if (version == null) + return null; - // 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) - { - uSeq.MinimalPatches.AddRange(alert.NewServerVersion.MinimalPatches); - } - - conn.Cache.Hosts.ToList().ForEach(h => - uSeq[h] = GetUpgradeSequenceForHost(h, uSeq.MinimalPatches) - ); - - return uSeq; + 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 null; + return minimalPatches; } @@ -673,11 +632,10 @@ namespace XenAdmin.Core /// private static XenServerVersion GetCommonServerVersionOfHostsInAConnection(IXenConnection connection, List xsVersions) { - XenServerVersion commonXenServerVersion = null; - - if (connection == null) + if (connection == null || xsVersions == null) return null; - + + XenServerVersion commonXenServerVersion = null; List hosts = connection.Cache.Hosts.ToList(); foreach (Host host in hosts) @@ -707,114 +665,48 @@ namespace XenAdmin.Core return commonXenServerVersion; } - private static List GetUpgradeSequenceForHost(Host h, List latestPatches) + 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(); - var appliedUpdateUuids = new List(); - bool elyOrGreater = Helpers.ElyOrGreater(h); - - if (elyOrGreater) + //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++) { - appliedUpdateUuids = h.AppliedUpdates().Select(u => u.uuid).ToList(); - } - else - { - appliedUpdateUuids = h.AppliedPatches().Select(p => p.uuid).ToList(); - } + var p = neededPatches[i]; - var neededPatches = new List(latestPatches); + if (appliedUpdateUuids.Any(apu => string.Equals(apu, p.Uuid, StringComparison.OrdinalIgnoreCase))) + continue; //the patch has been applied - //Iterate through latestPatches once; in each iteration, move the first item from L0 that has its dependencies met to the end of the Update Schedule (US) - for (int ii = 0; ii < neededPatches.Count; ii++) - { - var p = neededPatches[ii]; - - //checking requirements - if (//not applied yet - !appliedUpdateUuids.Any(apu => string.Equals(apu, p.Uuid, StringComparison.OrdinalIgnoreCase)) - // and either no requirements or they are meet - && (p.RequiredPatches == null - || p.RequiredPatches.Count == 0 - // all requirements met? - || p.RequiredPatches.All( - rp => - //sequence already has the required-patch - (sequence.Count != 0 && sequence.Any(useqp => string.Equals(useqp.Uuid, rp, StringComparison.OrdinalIgnoreCase))) - - //the required-patch has already been applied - || (appliedUpdateUuids.Count != 0 && appliedUpdateUuids.Any(apu => string.Equals(apu, rp, StringComparison.OrdinalIgnoreCase))) - ) - )) + 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(ii); + neededPatches.RemoveAt(i); //resetting position - the loop will start on 0. item - ii = -1; + i = -1; } } return sequence; } - public class UpgradeSequence : Dictionary> - { - private IEnumerable AllPatches - { - get - { - foreach (var patches in this.Values) - foreach(var patch in patches) - yield return patch; - } - } - - public List UniquePatches - { - get - { - var uniquePatches = new List(); - - foreach (var mp in MinimalPatches) - { - if (AllPatches.Any(p => p.Uuid == mp.Uuid)) - { - uniquePatches.Add(mp); - } - } - - return uniquePatches; - } - } - - public bool AllHostsUpToDate - { - get - { - if (this.Count == 0) - return false; - - foreach (var host in this.Keys) - { - if (this[host].Count > 0) - return false; - } - - return true; - } - } - - public List MinimalPatches - { - set; - get; - } - } - public static List NewXenServerVersionAlerts(List xenServerVersions) { if (Helpers.CommonCriteriaCertificationRelease) diff --git a/XenAdmin/Diagnostics/Problems/HostProblem/ConflictingUpdatePresent.cs b/XenAdmin/Diagnostics/Problems/HostProblem/ConflictingUpdatePresent.cs index 0d3480d42..683475135 100644 --- a/XenAdmin/Diagnostics/Problems/HostProblem/ConflictingUpdatePresent.cs +++ b/XenAdmin/Diagnostics/Problems/HostProblem/ConflictingUpdatePresent.cs @@ -31,25 +31,25 @@ using XenAdmin.Diagnostics.Checks; using XenAPI; -using System.Collections.Generic; + namespace XenAdmin.Diagnostics.Problems.HostProblem { public class ConflictingUpdatePresent : HostProblem { - private readonly string confilctedUpdates; + private readonly string conflictingUpdates; - public ConflictingUpdatePresent(Check check, string confilctedUpdates, Host host) + public ConflictingUpdatePresent(Check check, string conflictingUpdates, Host host) : base(check, host) { - this.confilctedUpdates = confilctedUpdates; + this.conflictingUpdates = conflictingUpdates; } public override string Description { get { - return string.Format(Messages.UPDATES_WIZARD_PRECHECK_FAILED_CONFLICTING_UPDATE, ServerName, confilctedUpdates); + return string.Format(Messages.UPDATES_WIZARD_PRECHECK_FAILED_CONFLICTING_UPDATE, ServerName, conflictingUpdates); } } diff --git a/XenAdmin/Diagnostics/Problems/HostProblem/PrecheckFailed.cs b/XenAdmin/Diagnostics/Problems/HostProblem/PrecheckFailed.cs index 361e437df..087ec2f0b 100644 --- a/XenAdmin/Diagnostics/Problems/HostProblem/PrecheckFailed.cs +++ b/XenAdmin/Diagnostics/Problems/HostProblem/PrecheckFailed.cs @@ -38,13 +38,11 @@ namespace XenAdmin.Diagnostics.Problems.HostProblem public class PrecheckFailed : HostProblem { private readonly Failure Failure; - private readonly Host _host; public PrecheckFailed(Check check, Host host, Failure failure) : base(check, host) { Failure = failure; - _host = host; } public override string Description diff --git a/XenAdmin/Wizards/PatchingWizard/PatchingWizard.cs b/XenAdmin/Wizards/PatchingWizard/PatchingWizard.cs index 8a65fb1f4..3fba8f7cc 100644 --- a/XenAdmin/Wizards/PatchingWizard/PatchingWizard.cs +++ b/XenAdmin/Wizards/PatchingWizard/PatchingWizard.cs @@ -45,8 +45,8 @@ namespace XenAdmin.Wizards.PatchingWizard public enum WizardMode { SingleUpdate, AutomatedUpdates, NewVersion } /// - /// Remember that equals for patches dont work across connections because - /// we are not allow to override equals. YOU SHOULD NOT USE ANY OPERATION THAT IMPLIES CALL EQUALS OF Pool_path or Host_patch + /// Remember that equals for patches don't work across connections because + /// we are not allow to override equals. YOU SHOULD NOT USE ANY OPERATION THAT IMPLIES CALL EQUALS OF Pool_patch or Host_patch /// You should do it manually or use delegates. /// public partial class PatchingWizard : XenWizardBase diff --git a/XenAdmin/Wizards/PatchingWizard/PatchingWizard_AutomatedUpdatesPage.cs b/XenAdmin/Wizards/PatchingWizard/PatchingWizard_AutomatedUpdatesPage.cs index 4990b7190..0c863a84b 100644 --- a/XenAdmin/Wizards/PatchingWizard/PatchingWizard_AutomatedUpdatesPage.cs +++ b/XenAdmin/Wizards/PatchingWizard/PatchingWizard_AutomatedUpdatesPage.cs @@ -44,7 +44,10 @@ using System.Linq; using XenAdmin.Core; using System.Text; using System.Diagnostics; +using System.Runtime.Remoting.Messaging; using XenAdmin.Alerts; +using HostActionTuple = System.Tuple, System.Collections.Generic.List>; + namespace XenAdmin.Wizards.PatchingWizard { @@ -125,189 +128,192 @@ namespace XenAdmin.Wizards.PatchingWizard protected override void PageLoadedCore(PageLoadedDirection direction) { if (_thisPageIsCompleted) - { return; - } Debug.Assert(WizardMode == WizardMode.AutomatedUpdates || WizardMode == WizardMode.NewVersion && UpdateAlert != null); if (WizardMode == WizardMode.AutomatedUpdates) - { labelTitle.Text = Messages.PATCHINGWIZARD_UPLOAD_AND_INSTALL_TITLE_AUTOMATED_MODE; - } else if (WizardMode == WizardMode.NewVersion) - { labelTitle.Text = Messages.PATCHINGWIZARD_UPLOAD_AND_INSTALL_TITLE_NEW_VERSION_AUTOMATED_MODE; - } - foreach (var pool in SelectedPools) - { - var master = Helpers.GetMaster(pool.Connection); - - var planActions = new List(); - var delayedActionsByHost = new Dictionary>(); - var finalActions = new List(); - - foreach (var host in pool.Connection.Cache.Hosts) - { - delayedActionsByHost.Add(host, new List()); - } - - var hosts = pool.Connection.Cache.Hosts; - - //if any host is not licensed for automated updates - bool automatedUpdatesRestricted = pool.Connection.Cache.Hosts.Any(Host.RestrictBatchHotfixApply); - - var us = WizardMode == WizardMode.NewVersion - ? Updates.GetUpgradeSequence(pool.Connection, UpdateAlert, ApplyUpdatesToNewVersion && !automatedUpdatesRestricted) - : Updates.GetUpgradeSequence(pool.Connection); - - Debug.Assert(us != null, "Update sequence should not be null."); - - if (us != null) - { - foreach (var patch in us.UniquePatches) - { - var hostsToApply = us.Where(u => u.Value.Contains(patch)).Select(u => u.Key).ToList(); - hostsToApply.Sort(); - - planActions.Add(new DownloadPatchPlanAction(master.Connection, patch, AllDownloadedPatches, PatchFromDisk)); - planActions.Add(new UploadPatchToMasterPlanAction(master.Connection, patch, patchMappings, AllDownloadedPatches, PatchFromDisk)); - planActions.Add(new PatchPrechecksOnMultipleHostsInAPoolPlanAction(master.Connection, patch, hostsToApply, patchMappings)); - - foreach (var host in hostsToApply) - { - planActions.Add(new ApplyXenServerPatchPlanAction(host, patch, patchMappings)); - - if (patch.GuidanceMandatory) - { - var action = patch.after_apply_guidance == after_apply_guidance.restartXAPI && - delayedActionsByHost[host].Any(a => a is RestartHostPlanAction) - ? new RestartHostPlanAction(host, host.GetRunningVMs(), restartAgentFallback:true) - : GetAfterApplyGuidanceAction(host, patch.after_apply_guidance); - - if (action != null) - { - planActions.Add(action); - // remove all delayed actions of the same kind that has already been added - // (because this action is guidance-mandatory=true, therefore - // it will run immediately, making delayed ones obsolete) - delayedActionsByHost[host].RemoveAll(a => action.GetType() == a.GetType()); - } - } - else - { - var action = GetAfterApplyGuidanceAction(host, patch.after_apply_guidance); - // add the action if it's not already in the list - if (action != null && !delayedActionsByHost[host].Any(a => a.GetType() == action.GetType())) - delayedActionsByHost[host].Add(action); - } - } - - //clean up master at the end: - planActions.Add(new RemoveUpdateFileFromMasterPlanAction(master, patchMappings, patch)); - - }//patch - } - - //add a revert pre-check action for this pool - var problemsToRevert = ProblemsResolvedPreCheck.Where(p => hosts.ToList().Select(h => h.uuid).ToList().Contains(p.Check.Host.uuid)).ToList(); - if (problemsToRevert.Count > 0) - { - finalActions.Add(new UnwindProblemsAction(problemsToRevert, string.Format(Messages.REVERTING_RESOLVED_PRECHECKS_POOL, pool.Connection.Name))); - } - - if (planActions.Count > 0) - { - var bgw = new UpdateProgressBackgroundWorker(planActions, delayedActionsByHost, finalActions); - backgroundWorkers.Add(bgw); - - } - } //foreach in SelectedMasters - - foreach (var bgw in backgroundWorkers) - { - bgw.DoWork += WorkerDoWork; - bgw.WorkerReportsProgress = true; - bgw.ProgressChanged += WorkerProgressChanged; - bgw.RunWorkerCompleted += WorkerCompleted; - bgw.WorkerSupportsCancellation = true; - bgw.RunWorkerAsync(); - } - - if (backgroundWorkers.Count == 0) + if (!StartUpgradeWorkers()) { _thisPageIsCompleted = true; _nextEnabled = true; - OnPageUpdated(); } } #region automatic_mode - private List doneActions = new List(); - private List inProgressActions = new List(); - private List errorActions = new List(); + private bool StartUpgradeWorkers() + { + bool atLeastOneWorkerStarted = false; + + foreach (var pool in SelectedPools) + { + //if any host is not licensed for automated updates + bool automatedUpdatesRestricted = pool.Connection.Cache.Hosts.Any(Host.RestrictBatchHotfixApply); + + var minimalPatches = WizardMode == WizardMode.NewVersion + ? Updates.GetMinimalPatches(pool.Connection, UpdateAlert, ApplyUpdatesToNewVersion && !automatedUpdatesRestricted) + : Updates.GetMinimalPatches(pool.Connection); + + if (minimalPatches == null) + continue; + + var master = Helpers.GetMaster(pool.Connection); + var planActions = new List(); + + var uploadedPatches = new List(); + var hosts = pool.Connection.Cache.Hosts.ToList(); + hosts.Sort();//master first + + foreach (var host in hosts) + { + var patchSequence = Updates.GetPatchSequenceForHost(host, minimalPatches); + if (patchSequence == null) + continue; + + var planActionsPerHost = new List(); + var delayedActionsPerHost = new List(); + + foreach (var patch in patchSequence) + { + if (!uploadedPatches.Contains(patch)) + { + planActionsPerHost.Add(new DownloadPatchPlanAction(master.Connection, patch, AllDownloadedPatches, PatchFromDisk)); + planActionsPerHost.Add(new UploadPatchToMasterPlanAction(master.Connection, patch, patchMappings, AllDownloadedPatches, PatchFromDisk)); + uploadedPatches.Add(patch); + } + + planActionsPerHost.Add(new PatchPrecheckOnHostPlanAction(master.Connection, patch, host, patchMappings)); + planActionsPerHost.Add(new ApplyXenServerPatchPlanAction(host, patch, patchMappings)); + + if (patch.GuidanceMandatory) + { + var action = patch.after_apply_guidance == after_apply_guidance.restartXAPI && delayedActionsPerHost.Any(a => a is RestartHostPlanAction) + ? new RestartHostPlanAction(host, host.GetRunningVMs(), restartAgentFallback: true) + : GetAfterApplyGuidanceAction(host, patch.after_apply_guidance); + + if (action != null) + { + planActionsPerHost.Add(action); + // remove all delayed actions of the same kind that has already been added + // (because this action is guidance-mandatory=true, therefore + // it will run immediately, making delayed ones obsolete) + delayedActionsPerHost.RemoveAll(a => action.GetType() == a.GetType()); + } + } + else + { + var action = GetAfterApplyGuidanceAction(host, patch.after_apply_guidance); + // add the action if it's not already in the list + if (action != null && delayedActionsPerHost.All(a => a.GetType() != action.GetType())) + delayedActionsPerHost.Add(action); + } + } + + planActions.Add(new HostActionTuple(host, planActionsPerHost, delayedActionsPerHost)); + } + + var finalActions = new List(); + + //clean up master at the end + foreach (var patch in uploadedPatches) + finalActions.Add(new RemoveUpdateFileFromMasterPlanAction(master, patchMappings, patch)); + + //add a revert pre-check action for this pool + var problemsToRevert = ProblemsResolvedPreCheck.Where(p => hosts.ToList().Select(h => h.uuid).ToList().Contains(p.Check.Host.uuid)).ToList(); + if (problemsToRevert.Count > 0) + finalActions.Add(new UnwindProblemsAction(problemsToRevert, string.Format(Messages.REVERTING_RESOLVED_PRECHECKS_POOL, pool.Connection.Name))); + + if (planActions.Count > 0) + { + atLeastOneWorkerStarted = true; + StartNewWorker(planActions, finalActions); + } + } + + return atLeastOneWorkerStarted; + } + + private void StartNewWorker(List planActions, List finalActions) + { + var bgw = new UpdateProgressBackgroundWorker(planActions, finalActions); + backgroundWorkers.Add(bgw); + bgw.DoWork += WorkerDoWork; + bgw.WorkerReportsProgress = true; + bgw.ProgressChanged += WorkerProgressChanged; + bgw.RunWorkerCompleted += WorkerCompleted; + bgw.WorkerSupportsCancellation = true; + bgw.RunWorkerAsync(); + } private void WorkerProgressChanged(object sender, ProgressChangedEventArgs e) { - var actionsWorker = sender as BackgroundWorker; + var actionsWorker = sender as UpdateProgressBackgroundWorker; + if (actionsWorker == null) + return; - Program.Invoke(Program.MainWindow, () => + if (!actionsWorker.CancellationPending) + { + PlanAction action = (PlanAction)e.UserState; + if (action != null) { - if (!actionsWorker.CancellationPending) + if (e.ProgressPercentage == 0) { - PlanAction action = (PlanAction)e.UserState; - if (action != null) - { - if (e.ProgressPercentage == 0) - { - inProgressActions.Add(action); - } - else - { - doneActions.Add(action); - inProgressActions.Remove(action); - - progressBar.Value += e.ProgressPercentage/backgroundWorkers.Count; //extend with error handling related numbers - } - } - - UpdateStatusTextBox(); + actionsWorker.InProgressActions.Add(action); } - }); + else + { + actionsWorker.DoneActions.Add(action); + actionsWorker.InProgressActions.Remove(action); + + progressBar.Value += e.ProgressPercentage / backgroundWorkers.Count; //extend with error handling related numbers + } + } + + UpdateStatusTextBox(); + } } private void UpdateStatusTextBox() { - var sb = new StringBuilder(); + var allsb = new StringBuilder(); - foreach (var pa in doneActions) + foreach (var bgw in backgroundWorkers) { - if (pa.Visible) + var sb = new StringBuilder(); + + foreach (var pa in bgw.DoneActions) { - sb.Append(pa.ProgressDescription ?? pa.ToString()); - sb.AppendLine(Messages.DONE); + if (pa.Error != null) + { + sb.Append(pa.ProgressDescription ?? pa.ToString()); + sb.AppendLine(Messages.ERROR); + } + else if (pa.Visible) + { + sb.Append(pa.ProgressDescription ?? pa.ToString()); + sb.AppendLine(Messages.DONE); + } } - } - foreach (var pa in errorActions) - { - sb.Append(pa.ProgressDescription ?? pa.ToString()); - sb.AppendLine(Messages.ERROR); - } - - foreach (var pa in inProgressActions) - { - if (pa.Visible) + foreach (var pa in bgw.InProgressActions) { - sb.Append(pa.ProgressDescription ?? pa.ToString()); - sb.AppendLine(); + if (pa.Visible) + { + sb.Append(pa.ProgressDescription ?? pa.ToString()); + sb.AppendLine(); + } } + + allsb.Append(sb); } - textBoxLog.Text = sb.ToString(); + textBoxLog.Text = allsb.ToString(); textBoxLog.SelectionStart = textBoxLog.Text.Length; textBoxLog.ScrollToCaret(); @@ -323,31 +329,28 @@ namespace XenAdmin.Wizards.PatchingWizard try { - //running actions (non-delayed) - foreach (var a in bgw.PlanActions) + foreach (var hostActions in bgw.HostActions) { - action = a; + var host = hostActions.Item1; + var planActions = hostActions.Item2; // priority actions - if (bgw.CancellationPending) + foreach (var a in planActions) { - doWorkEventArgs.Cancel = true; - return; + action = a; + + if (bgw.CancellationPending) + { + doWorkEventArgs.Cancel = true; + return; + } + + RunPlanAction(bgw, action); } - RunPlanAction(bgw, action); - } + // running delayed actions, but skipping the ones that should be skipped + var delayedActions = hostActions.Item3; + var restartActions = delayedActions.Where(a => a is RestartHostPlanAction).ToList(); - // running delayed actions, but skipping the ones that should be skipped - // iterating through hosts, master first - var hostsOrdered = bgw.DelayedActionsByHost.Keys.ToList(); - hostsOrdered.Sort(); //master first - - foreach (var h in hostsOrdered) - { - var actions = bgw.DelayedActionsByHost[h]; - var restartActions = actions.Where(a => a is RestartHostPlanAction).ToList(); - - //run all restart-alike plan actions foreach (var a in restartActions) { action = a; @@ -361,9 +364,8 @@ namespace XenAdmin.Wizards.PatchingWizard RunPlanAction(bgw, action); } - var otherActions = actions.Where(a => !(a is RestartHostPlanAction)).ToList(); + var otherActions = delayedActions.Where(a => !(a is RestartHostPlanAction)).ToList(); - //run the rest foreach (var a in otherActions) { action = a; @@ -378,7 +380,7 @@ namespace XenAdmin.Wizards.PatchingWizard // - this host is pre-Ely and there isn't any delayed restart plan action, or // - this host is Ely or above and live patching must have succeeded or there isn't any delayed restart plan action if (restartActions.Count <= 0 || - (Helpers.ElyOrGreater(h) && h.Connection.TryResolveWithTimeout(new XenRef(h.opaque_ref)).updates_requiring_reboot.Count <= 0)) + (Helpers.ElyOrGreater(host) && host.Connection.TryResolveWithTimeout(new XenRef(host.opaque_ref)).updates_requiring_reboot.Count <= 0)) { RunPlanAction(bgw, action); } @@ -387,7 +389,7 @@ namespace XenAdmin.Wizards.PatchingWizard //skip running it, but still need to report progress, mainly for the progress bar action.Visible = false; - bgw.ReportProgress(100/bgw.ActionsCount, action); + bgw.ReportProgress(100 / bgw.ActionsCount, action); } } } @@ -408,8 +410,8 @@ namespace XenAdmin.Wizards.PatchingWizard } catch (Exception e) { - errorActions.Add(action); - inProgressActions.Remove(action); + bgw.DoneActions.Add(action); + bgw.InProgressActions.Remove(action); log.Error("Failed to carry out plan.", e); log.Debug(action.Title); @@ -419,36 +421,37 @@ namespace XenAdmin.Wizards.PatchingWizard //this pool failed, we will stop here, but try to remove update files at least try { - var positionOfFailedAction = bgw.PlanActions.IndexOf(action); + if (action is DownloadPatchPlanAction || action is UploadPatchToMasterPlanAction) + return; - // can try to clean up the host after a failed PlanAction from bgw.PlanActions only - if (positionOfFailedAction != -1 && !(action is DownloadPatchPlanAction || action is UploadPatchToMasterPlanAction)) + var pos = 0; + if (action is RemoveUpdateFileFromMasterPlanAction) + pos = bgw.FinalActions.IndexOf(action) + 1; + + for (int i = pos; i < bgw.FinalActions.Count; i++) { - int pos = positionOfFailedAction; + action = bgw.FinalActions[i]; - if (!(bgw.PlanActions[pos] is RemoveUpdateFileFromMasterPlanAction)) //can't do anything if the remove action has failed + if (bgw.CancellationPending) { - while (++pos < bgw.PlanActions.Count) - { - if (bgw.PlanActions[pos] is RemoveUpdateFileFromMasterPlanAction) //find the next remove - { - bgw.PlanActions[pos].Run(); - break; - } - } + doWorkEventArgs.Cancel = true; + return; } + + if (action is RemoveUpdateFileFromMasterPlanAction) + RunPlanAction(bgw, action); } } catch (Exception ex2) { //already in an error case - best effort - log.Error("Failed to clean up (this was a best effort attempt)", ex2); } - - bgw.ReportProgress(0); + finally + { + bgw.ReportProgress(0); + } } - } private void RunPlanAction(UpdateProgressBackgroundWorker bgw, PlanAction action) @@ -471,31 +474,26 @@ namespace XenAdmin.Wizards.PatchingWizard private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { - var bgw = (UpdateProgressBackgroundWorker)sender; - - Program.Invoke(Program.MainWindow, () => + if (!e.Cancelled) + { + Exception exception = e.Result as Exception; + if (exception != null) { - if (!e.Cancelled) - { - Exception exception = e.Result as Exception; - if (exception != null) - { - //not showing exceptions in the meantime - } + //not showing exceptions in the meantime + } - //if all finished - if (backgroundWorkers.All(w => !w.IsBusy)) - { - AllWorkersFinished(); - ShowErrors(); + //if all finished + if (backgroundWorkers.All(w => !w.IsBusy)) + { + AllWorkersFinished(); + ShowErrors(); - _thisPageIsCompleted = true; - - _cancelEnabled = false; - _nextEnabled = true; - } - } - }); + _thisPageIsCompleted = true; + + _cancelEnabled = false; + _nextEnabled = true; + } + } OnPageUpdated(); } @@ -521,32 +519,14 @@ namespace XenAdmin.Wizards.PatchingWizard private void ShowErrors() { - if (ErrorMessages != null) + var sb = new StringBuilder(); + sb.AppendLine(); + + int errorCount = 0; + + foreach (var bgw in backgroundWorkers) { - labelTitle.Text = Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_FAILED; - labelError.Text = Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR; - - textBoxLog.Text += ErrorMessages; - - log.ErrorFormat("Error message displayed: {0}", labelError.Text); - pictureBox1.Image = SystemIcons.Error.ToBitmap(); - panel1.Visible = true; - } - } - - private string ErrorMessages - { - get - { - if (errorActions.Count == 0) - return null; - - var sb = new StringBuilder(); - - sb.AppendLine(); - sb.AppendLine(errorActions.Count > 1 ? Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERRORS_OCCURRED : Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR_OCCURRED); - - foreach (var action in errorActions) + foreach (var action in bgw.DoneActions) { var exception = action.Error; if (exception == null) @@ -557,21 +537,35 @@ namespace XenAdmin.Wizards.PatchingWizard log.Error(exception); - if (exception != null && exception.InnerException != null && exception.InnerException is Failure) + var innerEx = exception.InnerException as Failure; + if (innerEx != null) { - var innerEx = exception.InnerException as Failure; log.Error(innerEx); - sb.AppendLine(innerEx.Message); } else { sb.AppendLine(exception.Message); } + errorCount++; } - - return sb.ToString(); } + + if (errorCount == 0) + return; + + labelTitle.Text = Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_FAILED; + labelError.Text = Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR; + + textBoxLog.Text += "\r\n"; + textBoxLog.Text += errorCount > 1 + ? Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERRORS_OCCURRED + : Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR_OCCURRED; + textBoxLog.Text += sb.ToString(); + + log.ErrorFormat("Error message displayed: {0}", labelError.Text); + pictureBox1.Image = SystemIcons.Error.ToBitmap(); + panel1.Visible = true; } private void AllWorkersFinished() diff --git a/XenAdmin/Wizards/PatchingWizard/PatchingWizard_PrecheckPage.cs b/XenAdmin/Wizards/PatchingWizard/PatchingWizard_PrecheckPage.cs index b473b4dfb..731e560ab 100644 --- a/XenAdmin/Wizards/PatchingWizard/PatchingWizard_PrecheckPage.cs +++ b/XenAdmin/Wizards/PatchingWizard/PatchingWizard_PrecheckPage.cs @@ -392,15 +392,25 @@ namespace XenAdmin.Wizards.PatchingWizard //if any host is not licensed for automated updates bool automatedUpdatesRestricted = pool.Connection.Cache.Hosts.Any(Host.RestrictBatchHotfixApply); - var us = WizardMode == WizardMode.NewVersion - ? Updates.GetUpgradeSequence(pool.Connection, UpdateAlert, ApplyUpdatesToNewVersion && !automatedUpdatesRestricted) - : Updates.GetUpgradeSequence(pool.Connection); + var minimalPatches = WizardMode == WizardMode.NewVersion + ? Updates.GetMinimalPatches(pool.Connection, UpdateAlert, ApplyUpdatesToNewVersion && !automatedUpdatesRestricted) + : Updates.GetMinimalPatches(pool.Connection); - log.InfoFormat("Minimal patches for {0}: {1}", pool.Name(), us != null && us.MinimalPatches != null ? string.Join(",", us.MinimalPatches.Select(p => p.Name)) : ""); - - if (us == null) + if (minimalPatches == null) continue; + var us = new Dictionary>(); + var hosts = pool.Connection.Cache.Hosts; + + foreach (var h in hosts) + { + var ps = Updates.GetPatchSequenceForHost(h, minimalPatches); + if (ps != null) + us[h] = ps; + } + + log.InfoFormat("Minimal patches for {0}: {1}", pool.Name(), string.Join(",", minimalPatches.Select(p => p.Name))); + bool elyOrGreater = Helpers.ElyOrGreater(pool.Connection); foreach (Host host in us.Keys) diff --git a/XenAdmin/Wizards/PatchingWizard/PatchingWizard_SelectServers.cs b/XenAdmin/Wizards/PatchingWizard/PatchingWizard_SelectServers.cs index 71deb201e..c6496a365 100644 --- a/XenAdmin/Wizards/PatchingWizard/PatchingWizard_SelectServers.cs +++ b/XenAdmin/Wizards/PatchingWizard/PatchingWizard_SelectServers.cs @@ -203,9 +203,8 @@ namespace XenAdmin.Wizards.PatchingWizard } //check updgrade sequences - Updates.UpgradeSequence us = Updates.GetUpgradeSequence(host.Connection); - - if (us == null) //version not supported + var minimalPatches = Updates.GetMinimalPatches(host.Connection); + if (minimalPatches == null) //version not supported { row.Enabled = false; row.Cells[3].ToolTipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_AUTOMATED_UPDATES_NOT_SUPPORTED_HOST_VERSION; @@ -222,8 +221,8 @@ namespace XenAdmin.Wizards.PatchingWizard return; } - //if there is a host missing from the upgrade sequence - if (host.Connection.Cache.Hosts.Any(h => !us.Keys.Contains(h))) + var us = Updates.GetPatchSequenceForHost(host, minimalPatches); + if (us == null) { row.Enabled = false; row.Cells[3].ToolTipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_SERVER_NOT_AUTO_UPGRADABLE; @@ -231,8 +230,8 @@ namespace XenAdmin.Wizards.PatchingWizard return; } - //if all hosts are up-to-date - if (us.AllHostsUpToDate) + //if host is up to date + if (us.Count == 0) { row.Enabled = false; row.Cells[3].ToolTipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_SERVER_UP_TO_DATE; diff --git a/XenAdmin/Wizards/PatchingWizard/PlanActions/PatchPrechecksOnMultipleHostsAction.cs b/XenAdmin/Wizards/PatchingWizard/PlanActions/PatchPrechecksOnMultipleHostsAction.cs index 2a74b88d3..e22215452 100644 --- a/XenAdmin/Wizards/PatchingWizard/PlanActions/PatchPrechecksOnMultipleHostsAction.cs +++ b/XenAdmin/Wizards/PatchingWizard/PlanActions/PatchPrechecksOnMultipleHostsAction.cs @@ -31,28 +31,25 @@ using System; using System.Collections.Generic; -using XenAdmin.Actions; using XenAdmin.Core; using XenAPI; -using System.Linq; -using System.IO; using XenAdmin.Network; using XenAdmin.Diagnostics.Checks; -using System.Diagnostics; + namespace XenAdmin.Wizards.PatchingWizard.PlanActions { - class PatchPrechecksOnMultipleHostsInAPoolPlanAction : PlanActionWithSession + class PatchPrecheckOnHostPlanAction : PlanActionWithSession { private readonly XenServerPatch patch; private readonly List mappings; - private List hosts = null; + private readonly Host host; - public PatchPrechecksOnMultipleHostsInAPoolPlanAction(IXenConnection connection, XenServerPatch patch, List hosts, List mappings) + public PatchPrecheckOnHostPlanAction(IXenConnection connection, XenServerPatch patch, Host host, List mappings) : base(connection, string.Format(Messages.UPDATES_WIZARD_RUNNING_PRECHECK, patch.Name, connection.Name)) { this.patch = patch; - this.hosts = hosts; + this.host = host; this.mappings = mappings; } @@ -63,46 +60,32 @@ namespace XenAdmin.Wizards.PatchingWizard.PlanActions if (mapping != null && (mapping.Pool_patch != null || mapping.Pool_update != null)) { - foreach (var host in hosts) + if (Cancelling) + throw new CancelledException(); + + try { - if (Cancelling) - { - throw new CancelledException(); - } + PatchPrecheckCheck check = mapping.Pool_patch == null + ? new PatchPrecheckCheck(host, mapping.Pool_update) + : new PatchPrecheckCheck(host, mapping.Pool_patch); - try - { - PatchPrecheckCheck check = null; + var problems = check.RunAllChecks(); - if (mapping.Pool_patch != null) - { - check = new PatchPrecheckCheck(host, mapping.Pool_patch); - } - else - { - check = new PatchPrecheckCheck(host, mapping.Pool_update); - } + Diagnostics.Problems.Problem problem = null; - var problems = check.RunAllChecks(); + if (problems != null && problems.Count > 0) + problem = problems[0]; - Diagnostics.Problems.Problem problem = null; + if (problem != null) + throw new Exception(string.Format("{0}: {1}. {2}", host, problem.Title, problem.Description)); - if (problems != null && problems.Count > 0) - problem = problems[0]; - - if (problem != null) - { - throw new Exception(string.Format("{0}: {1}. {2}", host, problem.Title, problem.Description)); - } - } - catch (Exception ex) - { - log.Error(string.Format("Precheck failed on host {0}", host.Name()), ex); - throw ex; - } + } + catch (Exception ex) + { + log.Error(string.Format("Precheck failed on host {0}", host.Name()), ex); + throw; } } - } } } diff --git a/XenAdmin/Wizards/PatchingWizard/PlanActions/PlanAction.cs b/XenAdmin/Wizards/PatchingWizard/PlanActions/PlanAction.cs index ab79241b2..336d9a31f 100644 --- a/XenAdmin/Wizards/PatchingWizard/PlanActions/PlanAction.cs +++ b/XenAdmin/Wizards/PatchingWizard/PlanActions/PlanAction.cs @@ -45,7 +45,6 @@ namespace XenAdmin.Wizards.PatchingWizard.PlanActions private int _percentComplete; public event EventHandler OnProgressChange; - public event EventHandler OnActionError; public event Action StatusChanged; public Exception Error; protected bool Cancelling = false; @@ -121,8 +120,6 @@ namespace XenAdmin.Wizards.PatchingWizard.PlanActions else { Error = e; - if (OnActionError != null) - OnActionError(this, new EventArgs()); throw; } } diff --git a/XenAdmin/Wizards/PatchingWizard/PlanActions/RemoveUpdateFilesFromMaster.cs b/XenAdmin/Wizards/PatchingWizard/PlanActions/RemoveUpdateFilesFromMaster.cs index a78d710e3..a0641e26c 100644 --- a/XenAdmin/Wizards/PatchingWizard/PlanActions/RemoveUpdateFilesFromMaster.cs +++ b/XenAdmin/Wizards/PatchingWizard/PlanActions/RemoveUpdateFilesFromMaster.cs @@ -34,7 +34,7 @@ using XenAdmin.Core; using XenAPI; using System.Linq; using System; -using XenAdmin.Actions; + namespace XenAdmin.Wizards.PatchingWizard.PlanActions { @@ -45,7 +45,7 @@ namespace XenAdmin.Wizards.PatchingWizard.PlanActions private readonly Host master = null; public RemoveUpdateFileFromMasterPlanAction(Host master, List patchMappings, XenServerPatch patch) - : base(master.Connection, string.Format(Messages.UPDATES_WIZARD_REMOVING_UPDATES_FROM_POOL, master.Name())) + : base(master.Connection, string.Format(Messages.UPDATES_WIZARD_REMOVING_UPDATES_FROM_POOL, patch.Name, master.Name())) { this.patchMappings = patchMappings; this.patch = patch; @@ -63,13 +63,13 @@ namespace XenAdmin.Wizards.PatchingWizard.PlanActions { Pool_patch poolPatch = null; - if (mapping != null || mapping.Pool_patch != null && mapping.Pool_patch.opaque_ref != null) + if (mapping != null && mapping.Pool_patch != null && mapping.Pool_patch.opaque_ref != null) { poolPatch = mapping.Pool_patch; } else { - poolPatch = session.Connection.Cache.Pool_patches.FirstOrDefault(pp => string.Equals(pp.uuid, patch.Uuid, System.StringComparison.InvariantCultureIgnoreCase)); + poolPatch = session.Connection.Cache.Pool_patches.FirstOrDefault(pp => string.Equals(pp.uuid, patch.Uuid, StringComparison.InvariantCultureIgnoreCase)); } if (poolPatch != null && poolPatch.opaque_ref != null) @@ -82,7 +82,7 @@ namespace XenAdmin.Wizards.PatchingWizard.PlanActions } else { - if (mapping != null || mapping.Pool_update != null && mapping.Pool_update.opaque_ref != null) + if (mapping != null && mapping.Pool_update != null && mapping.Pool_update.opaque_ref != null) { var poolUpdate = mapping.Pool_update; diff --git a/XenAdmin/Wizards/PatchingWizard/UpdateProgressBackgroundWorker.cs b/XenAdmin/Wizards/PatchingWizard/UpdateProgressBackgroundWorker.cs index 29e10ae52..1fffc93e0 100644 --- a/XenAdmin/Wizards/PatchingWizard/UpdateProgressBackgroundWorker.cs +++ b/XenAdmin/Wizards/PatchingWizard/UpdateProgressBackgroundWorker.cs @@ -34,25 +34,23 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using XenAdmin.Wizards.PatchingWizard.PlanActions; -using XenAPI; +using HostActionTuple = System.Tuple, System.Collections.Generic.List>; namespace XenAdmin.Wizards.PatchingWizard { class UpdateProgressBackgroundWorker : BackgroundWorker { private readonly int _actionsCount; - - public List PlanActions { get; private set; } - public Dictionary> DelayedActionsByHost {get; private set; } + public List HostActions { get; private set; } public List FinalActions { get; private set; } + public readonly List DoneActions = new List(); + public readonly List InProgressActions = new List(); - public UpdateProgressBackgroundWorker(List planActions, - Dictionary> delayedActionsByHost, List finalActions) + public UpdateProgressBackgroundWorker(List planActions, List finalActions) { - PlanActions = planActions; - DelayedActionsByHost = delayedActionsByHost; + HostActions = planActions; FinalActions = finalActions; - _actionsCount = PlanActions.Count + DelayedActionsByHost.Sum(kvp => kvp.Value.Count) + FinalActions.Count; + _actionsCount = HostActions.Sum(t => t.Item2.Count + t.Item3.Count) + FinalActions.Count; } public int ActionsCount @@ -62,11 +60,15 @@ namespace XenAdmin.Wizards.PatchingWizard public new void CancelAsync() { - if (PlanActions != null) - PlanActions.ForEach(pa => + if (HostActions != null) + HostActions.ForEach(ha => { - if (!pa.IsComplete) - pa.Cancel(); + var cur = ha; + cur.Item2.ForEach(pa => + { + if (!pa.IsComplete) + pa.Cancel(); + }); }); base.CancelAsync(); diff --git a/XenAdminTests/UnitTests/BatchUpdatesTests/BatchUpdatesTests.cs b/XenAdminTests/UnitTests/BatchUpdatesTests/BatchUpdatesTests.cs index a58740293..1c779f4af 100644 --- a/XenAdminTests/UnitTests/BatchUpdatesTests/BatchUpdatesTests.cs +++ b/XenAdminTests/UnitTests/BatchUpdatesTests/BatchUpdatesTests.cs @@ -32,12 +32,12 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using XenAdmin.Actions; using Moq; using XenAdmin.Core; using XenAPI; using System.Linq; + namespace XenAdminTests.UnitTests { [TestFixture, Category(TestCategories.Unit)] @@ -104,11 +104,11 @@ namespace XenAdminTests.UnitTests [Test] public void GetUpgradeSequenceForNullConnection() { - Assert.AreEqual(XenAdmin.Core.Updates.GetUpgradeSequence(null), null); + Assert.AreEqual(Updates.GetMinimalPatches(null), null); } /// - /// Version exist + /// Version exists /// No patches in updates /// Nothing installed on host /// Result: update sequence has the host, but empty sequence @@ -122,22 +122,17 @@ namespace XenAdminTests.UnitTests var master = GetAMockHost(serverVersions.First().Oem, serverVersions.First().BuildNumber); SetXenServerVersionsInUpdates(serverVersions); - // Act - - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); - - // Assert + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.NotNull(minimalPatches); + Assert.AreEqual(0, minimalPatches.Count); + var upgradeSequence = Updates.GetPatchSequenceForHost(master.Object, minimalPatches); Assert.NotNull(upgradeSequence); - Assert.AreEqual(1, upgradeSequence.Count); - Assert.NotNull(upgradeSequence.First().Key); - Assert.AreEqual(upgradeSequence.First().Key.uuid, master.Object.uuid); - Assert.NotNull(upgradeSequence.First().Value); - Assert.AreEqual(upgradeSequence.First().Value.Count, 0); + Assert.AreEqual(0, upgradeSequence.Count); } /// - /// Version exist + /// Version exists /// 1 patch in updates. 1 in minimal list. /// 1 has to be installed on host /// Result: update sequence has the host, with the one single patch in it @@ -152,24 +147,20 @@ namespace XenAdminTests.UnitTests var master = GetAMockHost(serverVersions.First().Oem, serverVersions.First().BuildNumber); SetXenServerVersionsInUpdates(serverVersions); - // Act - - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); - - // Assert + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.NotNull(minimalPatches); + Assert.AreEqual(1, minimalPatches.Count); + var upgradeSequence = Updates.GetPatchSequenceForHost(master.Object, minimalPatches); Assert.NotNull(upgradeSequence); Assert.AreEqual(1, upgradeSequence.Count); - Assert.NotNull(upgradeSequence.First().Key); - Assert.AreEqual(upgradeSequence.First().Key.uuid, master.Object.uuid); - Assert.NotNull(upgradeSequence.First().Value); - Assert.AreEqual(upgradeSequence.First().Value.Count, 1); - Assert.AreEqual(upgradeSequence.First().Value[0], patch); + Assert.NotNull(upgradeSequence[0]); + Assert.AreEqual(upgradeSequence[0], patch); } /// - /// Version exist + /// Version exists /// 1 patch in updates. 1 in minimal list. But it is applied already on master. /// 0 has to be installed on host /// @@ -184,26 +175,21 @@ namespace XenAdminTests.UnitTests Pool_patch pool_patch = new Pool_patch(); pool_patch.uuid = patch.Uuid; - var master = GetAMockHost(serverVersions.First().Oem, serverVersions.First().BuildNumber, new List() { pool_patch }); + var master = GetAMockHost(serverVersions.First().Oem, serverVersions.First().BuildNumber, new List {pool_patch}); SetXenServerVersionsInUpdates(serverVersions); - // Act - - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); - - // Assert + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.NotNull(minimalPatches); + Assert.AreEqual(1, minimalPatches.Count); + var upgradeSequence = Updates.GetPatchSequenceForHost(master.Object, minimalPatches); Assert.NotNull(upgradeSequence); - Assert.AreEqual(1, upgradeSequence.Count); - Assert.NotNull(upgradeSequence.First().Key); - Assert.AreEqual(upgradeSequence.First().Key.uuid, master.Object.uuid); - Assert.NotNull(upgradeSequence.First().Value); - Assert.AreEqual(upgradeSequence.First().Value.Count, 0); + Assert.AreEqual(0, upgradeSequence.Count); } /// - /// Version exist + /// Version exists /// 100 patch in updates. 100 in minimal list. /// 100 has to be installed on host /// Result: update sequence has all the 100 patches in it @@ -215,27 +201,21 @@ namespace XenAdminTests.UnitTests var serverVersions = GetAVersionWithGivenNumberOfPatches(100); var master = GetAMockHost(serverVersions.First().Oem, serverVersions.First().BuildNumber); SetXenServerVersionsInUpdates(serverVersions); - - // Act - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.NotNull(minimalPatches); + Assert.AreEqual(100, minimalPatches.Count); - // Assert - + var upgradeSequence = Updates.GetPatchSequenceForHost(master.Object, minimalPatches); Assert.NotNull(upgradeSequence); - Assert.AreEqual(1, upgradeSequence.Count); - var seqToCheck = upgradeSequence.First(); - Assert.NotNull(seqToCheck.Key); - Assert.AreEqual(seqToCheck.Key.uuid, master.Object.uuid); - Assert.NotNull(seqToCheck.Value); - Assert.AreEqual(seqToCheck.Value.Count, 100); + Assert.AreEqual(100, upgradeSequence.Count); - for (int ii = 100; ii < 0; --ii) - Assert.AreEqual(seqToCheck.Value[ii], serverVersions.First().Patches[ii]); + for (int i = 100; i < 0; --i) + Assert.AreEqual(upgradeSequence[i], serverVersions[0].Patches[i]); } /// - /// Version exist + /// Version exists /// 100 patch in updates. 51 in minimal list. /// 51 has to be installed on host /// Result: update sequence has all the 51 patches in it @@ -249,29 +229,25 @@ namespace XenAdminTests.UnitTests RemoveANumberOfPatchesPseudoRandomly(serverVersions.First().MinimalPatches, 49); SetXenServerVersionsInUpdates(serverVersions); - // Act - - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); - // Assert - Assert.NotNull(upgradeSequence); - Assert.AreEqual(1, upgradeSequence.Count); - var seqToCheck = upgradeSequence.First(); - Assert.NotNull(seqToCheck.Key); - Assert.AreEqual(seqToCheck.Key.uuid, master.Object.uuid); - Assert.NotNull(seqToCheck.Value); - Assert.AreEqual(seqToCheck.Value.Count, 51); + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.NotNull(minimalPatches); + Assert.AreEqual(51, minimalPatches.Count); - foreach (var patch in seqToCheck.Value) + var upgradeSequence = Updates.GetPatchSequenceForHost(master.Object, minimalPatches); + Assert.NotNull(upgradeSequence); + Assert.AreEqual(51, upgradeSequence.Count); + + foreach (var patch in upgradeSequence) Assert.IsTrue(serverVersions.First().MinimalPatches.Contains(patch)); - Assert.False(seqToCheck.Value.Exists(seqpatch => !serverVersions.First().MinimalPatches.Contains(seqpatch))); + Assert.False(upgradeSequence.Exists(seqpatch => !serverVersions.First().MinimalPatches.Contains(seqpatch))); } /// - /// Version exist + /// Version exists /// 100 patch in updates. 51 in minimal list. /// 41 has to be installed on host (only 41 of 51, because 10 is already installed from the Minimal list (as well as 5 other)) /// Result: update sequence has all the 41 patches in it and all are from Minimal List and also not installed on the host @@ -299,30 +275,25 @@ namespace XenAdminTests.UnitTests SetXenServerVersionsInUpdates(serverVersions); - // Act - - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); - // Assert + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.NotNull(minimalPatches); + Assert.AreEqual(51, minimalPatches.Count); + var upgradeSequence = Updates.GetPatchSequenceForHost(master.Object, minimalPatches); Assert.NotNull(upgradeSequence); - Assert.AreEqual(1, upgradeSequence.Count); - var seqToCheck = upgradeSequence.First(); - Assert.NotNull(seqToCheck.Key); - Assert.AreEqual(seqToCheck.Key.uuid, master.Object.uuid); - Assert.NotNull(seqToCheck.Value); - Assert.AreEqual(seqToCheck.Value.Count, 41); + Assert.AreEqual(41, upgradeSequence.Count); - foreach (var patch in seqToCheck.Value) + foreach (var patch in upgradeSequence) Assert.IsTrue(serverVersions.First().MinimalPatches.Contains(patch) && !pool_patches.Exists(p => p.uuid == patch.Uuid)); - Assert.False(seqToCheck.Value.Exists(seqpatch => !serverVersions.First().MinimalPatches.Contains(seqpatch))); - Assert.True(seqToCheck.Value.Exists(seqpatch => !pool_patches.Exists(pp => pp.uuid == seqpatch.Uuid))); + Assert.False(upgradeSequence.Exists(seqpatch => !serverVersions.First().MinimalPatches.Contains(seqpatch))); + Assert.True(upgradeSequence.Exists(seqpatch => !pool_patches.Exists(pp => pp.uuid == seqpatch.Uuid))); } /// - /// Version exist + /// Version exists /// 2 patch in updates. 1 in minimal list. /// 1 has to be installed on host /// Result: update sequence has the host, with the one single patch in it @@ -337,24 +308,21 @@ namespace XenAdminTests.UnitTests RemoveANumberOfPatchesPseudoRandomly(serverVersions.First().MinimalPatches, 1); SetXenServerVersionsInUpdates(serverVersions); - // Act - - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); - // Assert + + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.NotNull(minimalPatches); + Assert.AreEqual(1, minimalPatches.Count); + var upgradeSequence = Updates.GetPatchSequenceForHost(master.Object, minimalPatches); Assert.NotNull(upgradeSequence); Assert.AreEqual(1, upgradeSequence.Count); - Assert.NotNull(upgradeSequence.First().Key); - Assert.AreEqual(upgradeSequence.First().Key.uuid, master.Object.uuid); - Assert.NotNull(upgradeSequence.First().Value); - Assert.AreEqual(upgradeSequence.First().Value.Count, 1); - Assert.AreEqual(upgradeSequence.First().Value[0], serverVersions.First().MinimalPatches[0]); + Assert.AreEqual(upgradeSequence[0], serverVersions.First().MinimalPatches[0]); } /// - /// Version exist + /// Version exists /// 2 patch in updates. 2 in minimal list. /// 2 has to be installed on host /// Result: update sequence has the host, with the one single patch in it @@ -368,20 +336,18 @@ namespace XenAdminTests.UnitTests var master = GetAMockHost(serverVersions.First().Oem, serverVersions.First().BuildNumber); SetXenServerVersionsInUpdates(serverVersions); - // Act - - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); - // Assert + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.NotNull(minimalPatches); + Assert.AreEqual(2, minimalPatches.Count); + + var upgradeSequence = Updates.GetPatchSequenceForHost(master.Object, minimalPatches); Assert.NotNull(upgradeSequence); - Assert.AreEqual(1, upgradeSequence.Count); - Assert.NotNull(upgradeSequence.First().Key); - Assert.AreEqual(upgradeSequence.First().Key.uuid, master.Object.uuid); - Assert.NotNull(upgradeSequence.First().Value); - Assert.AreEqual(upgradeSequence.First().Value.Count, 2); - Assert.AreEqual(upgradeSequence.First().Value[0], serverVersions.First().MinimalPatches[0]); - Assert.AreEqual(upgradeSequence.First().Value[1], serverVersions.First().MinimalPatches[1]); + Assert.AreEqual(2, upgradeSequence.Count); + + for (int i = 0; i < upgradeSequence.Count; i++) + Assert.AreEqual(upgradeSequence[i], serverVersions.First().MinimalPatches[i]); } @@ -393,18 +359,13 @@ namespace XenAdminTests.UnitTests public void NoInfoForCurrentVersion() { // Arrange - var serverVersions = GetAVersionWithGivenNumberOfPatches(200); var master = GetAMockHost(serverVersions.First().Oem, serverVersions.First().BuildNumber + "to_make_it_not_match"); SetXenServerVersionsInUpdates(serverVersions); - // Act - - var upgradeSequence = XenAdmin.Core.Updates.GetUpgradeSequence(master.Object.Connection); - - // Assert - - Assert.Null(upgradeSequence); + // assert + var minimalPatches = Updates.GetMinimalPatches(master.Object.Connection); + Assert.Null(minimalPatches); } diff --git a/XenModel/Messages.Designer.cs b/XenModel/Messages.Designer.cs index 3bc59db0f..38ec01999 100755 --- a/XenModel/Messages.Designer.cs +++ b/XenModel/Messages.Designer.cs @@ -35000,7 +35000,7 @@ namespace XenAdmin { } /// - /// Looks up a localized string similar to Removing update files from {0}.... + /// Looks up a localized string similar to Removing update file {0} from {1}.... /// public static string UPDATES_WIZARD_REMOVING_UPDATES_FROM_POOL { get { diff --git a/XenModel/Messages.resx b/XenModel/Messages.resx index 38adbd5a8..d6a93ce36 100755 --- a/XenModel/Messages.resx +++ b/XenModel/Messages.resx @@ -12068,7 +12068,7 @@ Check your settings and try again. Deleting update installation file {0} from {1}... - Removing update files from {0}... + Removing update file {0} from {1}... Click here to repair