Merge pull request #2089 from kc284/REQ-672

First batch of changes for CP-28281
This commit is contained in:
Mihaela Stoica 2018-06-08 11:32:26 +01:00 committed by GitHub
commit 15edb13997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 431 additions and 595 deletions

View File

@ -573,51 +573,28 @@ namespace XenAdmin.Core
return recommendedPatches;
}
public static UpgradeSequence GetUpgradeSequence(IXenConnection conn)
public static List<XenServerPatch> 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<XenServerPatch>(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<Host> 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<XenServerPatch>(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;
}
/// <summary>
@ -626,43 +603,25 @@ namespace XenAdmin.Core
/// <param name="conn">Connection for the pool</param>
/// <param name="alert">The alert that refers the version-update</param>
/// <param name="updateTheNewVersion">Also add the minimum patches for the new version (true) or not (false).</param>
/// <returns></returns>
public static UpgradeSequence GetUpgradeSequence(IXenConnection conn, XenServerPatchAlert alert, bool updateTheNewVersion)
public static List<XenServerPatch> 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<XenServerPatch>();
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<XenServerPatch> {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
/// <returns></returns>
private static XenServerVersion GetCommonServerVersionOfHostsInAConnection(IXenConnection connection, List<XenServerVersion> xsVersions)
{
XenServerVersion commonXenServerVersion = null;
if (connection == null)
if (connection == null || xsVersions == null)
return null;
XenServerVersion commonXenServerVersion = null;
List<Host> hosts = connection.Cache.Hosts.ToList();
foreach (Host host in hosts)
@ -707,114 +665,48 @@ namespace XenAdmin.Core
return commonXenServerVersion;
}
private static List<XenServerPatch> GetUpgradeSequenceForHost(Host h, List<XenServerPatch> latestPatches)
public static List<XenServerPatch> GetPatchSequenceForHost(Host h, List<XenServerPatch> 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<XenServerPatch>(minimalPatches);
var sequence = new List<XenServerPatch>();
var appliedUpdateUuids = new List<string>();
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<XenServerPatch>(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<Host, List<XenServerPatch>>
{
private IEnumerable<XenServerPatch> AllPatches
{
get
{
foreach (var patches in this.Values)
foreach(var patch in patches)
yield return patch;
}
}
public List<XenServerPatch> UniquePatches
{
get
{
var uniquePatches = new List<XenServerPatch>();
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<XenServerPatch> MinimalPatches
{
set;
get;
}
}
public static List<XenServerVersionAlert> NewXenServerVersionAlerts(List<XenServerVersion> xenServerVersions)
{
if (Helpers.CommonCriteriaCertificationRelease)

View File

@ -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);
}
}

View File

@ -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

View File

@ -45,8 +45,8 @@ namespace XenAdmin.Wizards.PatchingWizard
public enum WizardMode { SingleUpdate, AutomatedUpdates, NewVersion }
/// <summary>
/// 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.
/// </summary>
public partial class PatchingWizard : XenWizardBase

View File

@ -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<XenAPI.Host, System.Collections.Generic.List<XenAdmin.Wizards.PatchingWizard.PlanActions.PlanAction>, System.Collections.Generic.List<XenAdmin.Wizards.PatchingWizard.PlanActions.PlanAction>>;
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<PlanAction>();
var delayedActionsByHost = new Dictionary<Host, List<PlanAction>>();
var finalActions = new List<PlanAction>();
foreach (var host in pool.Connection.Cache.Hosts)
{
delayedActionsByHost.Add(host, new List<PlanAction>());
}
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<PlanAction> doneActions = new List<PlanAction>();
private List<PlanAction> inProgressActions = new List<PlanAction>();
private List<PlanAction> errorActions = new List<PlanAction>();
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<HostActionTuple>();
var uploadedPatches = new List<XenServerPatch>();
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<PlanAction>();
var delayedActionsPerHost = new List<PlanAction>();
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<PlanAction>();
//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<HostActionTuple> planActions, List<PlanAction> 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<Host>(h.opaque_ref)).updates_requiring_reboot.Count <= 0))
(Helpers.ElyOrGreater(host) && host.Connection.TryResolveWithTimeout(new XenRef<Host>(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()

View File

@ -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<Host, List<XenServerPatch>>();
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)

View File

@ -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;

View File

@ -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<PoolPatchMapping> mappings;
private List<Host> hosts = null;
private readonly Host host;
public PatchPrechecksOnMultipleHostsInAPoolPlanAction(IXenConnection connection, XenServerPatch patch, List<Host> hosts, List<PoolPatchMapping> mappings)
public PatchPrecheckOnHostPlanAction(IXenConnection connection, XenServerPatch patch, Host host, List<PoolPatchMapping> 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;
}
}
}
}
}

View File

@ -45,7 +45,6 @@ namespace XenAdmin.Wizards.PatchingWizard.PlanActions
private int _percentComplete;
public event EventHandler OnProgressChange;
public event EventHandler OnActionError;
public event Action<PlanAction, Host> 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;
}
}

View File

@ -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<PoolPatchMapping> 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;

View File

@ -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<XenAPI.Host, System.Collections.Generic.List<XenAdmin.Wizards.PatchingWizard.PlanActions.PlanAction>, System.Collections.Generic.List<XenAdmin.Wizards.PatchingWizard.PlanActions.PlanAction>>;
namespace XenAdmin.Wizards.PatchingWizard
{
class UpdateProgressBackgroundWorker : BackgroundWorker
{
private readonly int _actionsCount;
public List<PlanAction> PlanActions { get; private set; }
public Dictionary<Host, List<PlanAction>> DelayedActionsByHost {get; private set; }
public List<HostActionTuple> HostActions { get; private set; }
public List<PlanAction> FinalActions { get; private set; }
public readonly List<PlanAction> DoneActions = new List<PlanAction>();
public readonly List<PlanAction> InProgressActions = new List<PlanAction>();
public UpdateProgressBackgroundWorker(List<PlanAction> planActions,
Dictionary<Host, List<PlanAction>> delayedActionsByHost, List<PlanAction> finalActions)
public UpdateProgressBackgroundWorker(List<HostActionTuple> planActions, List<PlanAction> 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();

View File

@ -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);
}
/// <summary>
/// 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);
}
/// <summary>
/// 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);
}
/// <summary>
/// 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
/// </summary>
@ -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>() { pool_patch });
var master = GetAMockHost(serverVersions.First().Oem, serverVersions.First().BuildNumber, new List<Pool_patch> {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);
}
/// <summary>
/// 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]);
}
/// <summary>
/// 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)));
}
/// <summary>
/// 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)));
}
/// <summary>
/// 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]);
}
/// <summary>
/// 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);
}

View File

@ -35000,7 +35000,7 @@ namespace XenAdmin {
}
/// <summary>
/// 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}....
/// </summary>
public static string UPDATES_WIZARD_REMOVING_UPDATES_FROM_POOL {
get {

View File

@ -12068,7 +12068,7 @@ Check your settings and try again.</value>
<value>Deleting update installation file {0} from {1}... </value>
</data>
<data name="UPDATES_WIZARD_REMOVING_UPDATES_FROM_POOL" xml:space="preserve">
<value>Removing update files from {0}...</value>
<value>Removing update file {0} from {1}...</value>
</data>
<data name="UPDATES_WIZARD_REPAIR_SR" xml:space="preserve">
<value>Click here to repair</value>