mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-24 22:06:59 +01:00
Merge pull request #2089 from kc284/REQ-672
First batch of changes for CP-28281
This commit is contained in:
commit
15edb13997
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
2
XenModel/Messages.Designer.cs
generated
2
XenModel/Messages.Designer.cs
generated
@ -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 {
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user