mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-25 06:16:37 +01:00
CP-15719: UI: Implement error handling/reporting on Upload & Install page
Implemented error handling. If there has been an error in a pool, we stop executing actions in that pool immediately, and report the error once every other actions have finished. Error in one pool will not affect the upgrade process of other pools. Signed-off-by: Gabor Apati-Nagy <gabor.apati-nagy@citrix.com>
This commit is contained in:
parent
a05e92d001
commit
73d67f407d
@ -66,9 +66,6 @@ namespace XenAdmin.Wizards.PatchingWizard
|
||||
|
||||
private List<BackgroundWorker> backgroundWorkers = new List<BackgroundWorker>();
|
||||
|
||||
private List<UpgradeProgressDescriptor> upgradeProgressDescriptors = new List<UpgradeProgressDescriptor>();
|
||||
|
||||
|
||||
public PatchingWizard_AutoUpdatingPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
@ -118,7 +115,8 @@ namespace XenAdmin.Wizards.PatchingWizard
|
||||
|
||||
if (_thisPageHasBeenCompleted)
|
||||
{
|
||||
actionsWorker = null;
|
||||
backgroundWorkers.ForEach(bgw => bgw.CancelAsync());
|
||||
backgroundWorkers.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -128,6 +126,7 @@ namespace XenAdmin.Wizards.PatchingWizard
|
||||
{
|
||||
Dictionary<Host, List<PlanAction>> planActionsByHost = new Dictionary<Host, List<PlanAction>>();
|
||||
Dictionary<Host, List<PlanAction>> delayedActionsByHost = new Dictionary<Host, List<PlanAction>>();
|
||||
planActionsPerPool.Add(master, new List<PlanAction>());
|
||||
|
||||
foreach (var host in master.Connection.Cache.Hosts)
|
||||
{
|
||||
@ -135,7 +134,6 @@ namespace XenAdmin.Wizards.PatchingWizard
|
||||
delayedActionsByHost.Add(host, new List<PlanAction>());
|
||||
}
|
||||
|
||||
//var master = Helpers.GetMaster(pool.Connection);
|
||||
var hosts = master.Connection.Cache.Hosts;
|
||||
|
||||
var us = Updates.GetUpgradeSequence(master.Connection);
|
||||
@ -163,15 +161,10 @@ namespace XenAdmin.Wizards.PatchingWizard
|
||||
UpdateDelayedAfterPatchGuidanceActionListForHost(delayedActionsByHost[host], host, patch);
|
||||
}
|
||||
|
||||
// now add all non-delayed actions to the pool action list
|
||||
// now add all non-delayed actions to the pool action list
|
||||
|
||||
foreach (var kvp in planActionsByHost)
|
||||
{
|
||||
if (!planActionsPerPool.ContainsKey(master))
|
||||
{
|
||||
planActionsPerPool.Add(master, new List<PlanAction>());
|
||||
}
|
||||
|
||||
planActionsPerPool[master].AddRange(kvp.Value);
|
||||
kvp.Value.Clear();
|
||||
}
|
||||
@ -188,47 +181,40 @@ namespace XenAdmin.Wizards.PatchingWizard
|
||||
delayedActions.AddRange(kvp.Value);
|
||||
}
|
||||
|
||||
var upd = new UpgradeProgressDescriptor(master, planActionsPerPool[master], delayedActions);
|
||||
upgradeProgressDescriptors.Add(upd);
|
||||
if (planActionsPerPool.ContainsKey(master) && planActionsPerPool[master].Count > 0)
|
||||
{
|
||||
var bgw = new UpdateProgressBackgroundWorker(master, planActionsPerPool[master], delayedActions);
|
||||
backgroundWorkers.Add(bgw);
|
||||
|
||||
}
|
||||
} //foreach in SelectedMasters
|
||||
|
||||
|
||||
//var planActions = new List<PlanAction>();
|
||||
|
||||
//actions list for serial execution
|
||||
foreach (var desc in upgradeProgressDescriptors)
|
||||
foreach (var bgw in backgroundWorkers)
|
||||
{
|
||||
//planActions.AddRange(actionListPerPool.Value);
|
||||
|
||||
var bgw = new BackgroundWorker();
|
||||
backgroundWorkers.Add(bgw);
|
||||
|
||||
bgw.DoWork += new DoWorkEventHandler(PatchingWizardAutomaticPatchWork);
|
||||
bgw.DoWork += new DoWorkEventHandler(WorkerDoWork);
|
||||
bgw.WorkerReportsProgress = true;
|
||||
bgw.ProgressChanged += new ProgressChangedEventHandler(actionsWorker_ProgressChanged);
|
||||
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(actionsWorker_RunWorkerCompleted);
|
||||
bgw.ProgressChanged += new ProgressChangedEventHandler(WorkerProgressChanged);
|
||||
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerCompleted);
|
||||
bgw.WorkerSupportsCancellation = true;
|
||||
bgw.RunWorkerAsync(desc);
|
||||
bgw.RunWorkerAsync();
|
||||
}
|
||||
|
||||
//planActions.Add(new UnwindProblemsAction(ProblemsResolvedPreCheck));
|
||||
if (backgroundWorkers.Count == 0)
|
||||
{
|
||||
_thisPageHasBeenCompleted = true;
|
||||
_nextEnabled = true;
|
||||
|
||||
//actionsWorker = new BackgroundWorker();
|
||||
//actionsWorker.DoWork += new DoWorkEventHandler(PatchingWizardAutomaticPatchWork);
|
||||
//actionsWorker.WorkerReportsProgress = true;
|
||||
//actionsWorker.ProgressChanged += new ProgressChangedEventHandler(actionsWorker_ProgressChanged);
|
||||
//actionsWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(actionsWorker_RunWorkerCompleted);
|
||||
//actionsWorker.WorkerSupportsCancellation = true;
|
||||
//actionsWorker.RunWorkerAsync(planActions);
|
||||
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 void actionsWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
|
||||
private void WorkerProgressChanged(object sender, ProgressChangedEventArgs e)
|
||||
{
|
||||
var actionsWorker = sender as BackgroundWorker;
|
||||
|
||||
@ -247,90 +233,91 @@ namespace XenAdmin.Wizards.PatchingWizard
|
||||
progressBar.Value += (int)((float)e.ProgressPercentage / (float)backgroundWorkers.Count); //extend with error handling related numbers
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var pa in doneActions)
|
||||
{
|
||||
sb.Append(pa);
|
||||
sb.AppendLine(Messages.DONE);
|
||||
}
|
||||
|
||||
foreach (var pa in inProgressActions)
|
||||
{
|
||||
sb.Append(pa);
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
textBoxLog.Text = sb.ToString();
|
||||
UpdateStatusTextBox();
|
||||
}
|
||||
}
|
||||
|
||||
private void PatchingWizardAutomaticPatchWork(object sender, DoWorkEventArgs doWorkEventArgs)
|
||||
private void UpdateStatusTextBox()
|
||||
{
|
||||
var actionsWorker = sender as BackgroundWorker;
|
||||
var sb = new StringBuilder();
|
||||
|
||||
UpgradeProgressDescriptor descriptor = (UpgradeProgressDescriptor)doWorkEventArgs.Argument;
|
||||
foreach (var pa in doneActions)
|
||||
{
|
||||
sb.Append(pa);
|
||||
sb.AppendLine(Messages.DONE);
|
||||
}
|
||||
|
||||
var actionList = descriptor.planActions.Concat(descriptor.delayedActions).ToList();
|
||||
foreach (var pa in inProgressActions)
|
||||
{
|
||||
sb.Append(pa);
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
foreach (PlanAction action in actionList)
|
||||
textBoxLog.Text = sb.ToString();
|
||||
}
|
||||
|
||||
private void WorkerDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
|
||||
{
|
||||
var bgw = sender as UpdateProgressBackgroundWorker;
|
||||
|
||||
foreach (PlanAction action in bgw.AllActions)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (actionsWorker.CancellationPending)
|
||||
if (bgw.CancellationPending)
|
||||
{
|
||||
//descriptor.InProgressAction = null;
|
||||
doWorkEventArgs.Cancel = true;
|
||||
return;
|
||||
}
|
||||
//descriptor.InProgressAction = action;
|
||||
|
||||
actionsWorker.ReportProgress(0, action);
|
||||
|
||||
bgw.ReportProgress(0, action);
|
||||
action.Run();
|
||||
Thread.Sleep(1000);
|
||||
|
||||
//descriptor.doneActions.Add(action);
|
||||
bgw.doneActions.Add(action);
|
||||
|
||||
actionsWorker.ReportProgress((int)((1.0 / (double)actionList.Count) * 100), action);
|
||||
bgw.ReportProgress((int)((1.0 / (double)bgw.AllActions.Count) * 100), action);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//descriptor.FailedWithExceptionAction = action;
|
||||
bgw.FailedWithExceptionAction = action;
|
||||
errorActions.Add(action);
|
||||
|
||||
log.Error("Failed to carry out plan.", e);
|
||||
log.Debug(actionList);
|
||||
log.Debug(bgw.AllActions);
|
||||
doWorkEventArgs.Result = new Exception(action.Title, e);
|
||||
break;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//descriptor.InProgressAction = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void actionsWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
||||
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
var bgw = (UpdateProgressBackgroundWorker)sender;
|
||||
|
||||
if (!e.Cancelled)
|
||||
{
|
||||
Exception exception = e.Result as Exception;
|
||||
if (exception != null)
|
||||
{
|
||||
FinishedWithErrors(exception);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
FinishedSuccessfully();
|
||||
//not showing exceptions in the meantime
|
||||
}
|
||||
|
||||
//if all finished
|
||||
if (backgroundWorkers.All(w => !w.IsBusy))
|
||||
{
|
||||
AllWorkersFinished();
|
||||
ShowErrors();
|
||||
|
||||
progressBar.Value = 100;
|
||||
_thisPageHasBeenCompleted = true;
|
||||
}
|
||||
|
||||
progressBar.Value = 100;
|
||||
_cancelEnabled = false;
|
||||
_nextEnabled = true;
|
||||
}
|
||||
OnPageUpdated();
|
||||
|
||||
_thisPageHasBeenCompleted = true;
|
||||
OnPageUpdated();
|
||||
}
|
||||
|
||||
private void UpdateDelayedAfterPatchGuidanceActionListForHost(List<PlanAction> delayedGuidances, Host host, XenServerPatch patch)
|
||||
@ -447,37 +434,66 @@ namespace XenAdmin.Wizards.PatchingWizard
|
||||
|
||||
#endregion
|
||||
|
||||
private void FinishedWithErrors(Exception exception)
|
||||
private void ShowErrors()
|
||||
{
|
||||
//labelTitle.Text = string.Format(Messages.UPDATE_WAS_NOT_COMPLETED, GetUpdateName());
|
||||
|
||||
string errorMessage = null;
|
||||
|
||||
if (exception != null && exception.InnerException != null && exception.InnerException is Failure)
|
||||
if (ErrorMessages != null)
|
||||
{
|
||||
var innerEx = exception.InnerException as Failure;
|
||||
errorMessage = innerEx.Message;
|
||||
labelTitle.Text = Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_FAILED;
|
||||
labelError.Text = Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR;
|
||||
|
||||
if (innerEx.ErrorDescription != null && innerEx.ErrorDescription.Count > 0)
|
||||
log.Error(string.Concat(innerEx.ErrorDescription.ToArray()));
|
||||
textBoxLog.Text += ErrorMessages;
|
||||
|
||||
log.ErrorFormat("Error message displayed: {0}", labelError.Text);
|
||||
pictureBox1.Image = SystemIcons.Error.ToBitmap();
|
||||
panel1.Visible = true;
|
||||
}
|
||||
|
||||
labelError.Text = errorMessage ?? string.Format(Messages.PATCHING_WIZARD_ERROR, exception.Message);
|
||||
|
||||
log.ErrorFormat("Error message displayed: {0}", labelError.Text);
|
||||
|
||||
pictureBox1.Image = SystemIcons.Error.ToBitmap();
|
||||
if (exception.InnerException is SupplementalPackInstallFailedException)
|
||||
{
|
||||
errorLinkLabel.Visible = true;
|
||||
errorLinkLabel.Tag = exception.InnerException;
|
||||
}
|
||||
panel1.Visible = true;
|
||||
}
|
||||
|
||||
private void FinishedSuccessfully()
|
||||
private string ErrorMessages
|
||||
{
|
||||
get
|
||||
{
|
||||
if (errorActions.Count == 0)
|
||||
return null;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine(errorActions.Count > 1 ? Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERRORS_OCCURED : Messages.PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR_OCCURED);
|
||||
|
||||
foreach (var action in errorActions)
|
||||
{
|
||||
var exception = action.Error;
|
||||
if (exception == null)
|
||||
{
|
||||
log.ErrorFormat("An action has failed with an empty exception. Action: {0}", action.ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exception != null && exception.InnerException != null && exception.InnerException is Failure)
|
||||
{
|
||||
var innerEx = exception.InnerException as Failure;
|
||||
sb.AppendLine(innerEx.Message);
|
||||
|
||||
if (innerEx.ErrorDescription != null && innerEx.ErrorDescription.Count > 0)
|
||||
sb.AppendLine(string.Concat(innerEx.ErrorDescription.ToArray()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exception is Failure && ((Failure)exception).ErrorDescription != null)
|
||||
sb.AppendLine(string.Concat(((Failure)exception).ErrorDescription.ToArray()));
|
||||
else
|
||||
sb.AppendLine(exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void AllWorkersFinished()
|
||||
{
|
||||
labelTitle.Text = Messages.PATCHINGWIZARD_UPDATES_DONE_AUTOMATIC_MODE;
|
||||
|
||||
pictureBox1.Image = null;
|
||||
labelError.Text = Messages.CLOSE_WIZARD_CLICK_FINISH;
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using XenAdmin.Wizards.PatchingWizard.PlanActions;
|
||||
using XenAPI;
|
||||
|
||||
namespace XenAdmin.Wizards.PatchingWizard
|
||||
{
|
||||
class UpdateProgressBackgroundWorker : BackgroundWorker
|
||||
{
|
||||
public List<PlanAction> PlanActions { get; private set; }
|
||||
public List<PlanAction> DelayedActions { get; private set; }
|
||||
private Host master;
|
||||
|
||||
public List<PlanAction> FinsihedActions = new List<PlanAction>();
|
||||
public PlanAction FailedWithExceptionAction = null;
|
||||
public List<PlanAction> doneActions = new List<PlanAction>();
|
||||
|
||||
public UpdateProgressBackgroundWorker(Host master, List<PlanAction> planActions, List<PlanAction> delayedActions)
|
||||
{
|
||||
this.master = master;
|
||||
this.PlanActions = planActions;
|
||||
this.DelayedActions = delayedActions;
|
||||
}
|
||||
|
||||
public List<PlanAction> AllActions
|
||||
{
|
||||
get
|
||||
{
|
||||
return PlanActions.Concat(DelayedActions).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public int TotalNumberOfActions
|
||||
{
|
||||
get
|
||||
{
|
||||
return PlanActions.Count + DelayedActions.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public int ProgressPercent
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)(1.0 / (double)TotalNumberOfActions * (double)(FinsihedActions.Count));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -869,6 +869,9 @@
|
||||
<Compile Include="Wizards\PatchingWizard\PlanActions\RemoveUpdateFilesFromMaster.cs" />
|
||||
<Compile Include="Wizards\PatchingWizard\PlanActions\RemoveUpdateFile.cs" />
|
||||
<Compile Include="Wizards\PatchingWizard\PlanActions\InstallSupplementalPackPlanAction.cs" />
|
||||
<Compile Include="Wizards\PatchingWizard\UpdateProgressBackgroundWorker.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Wizards\PatchingWizard\UpgradeProgressDescriptor.cs" />
|
||||
<Compile Include="XenSearch\TreeNodeGroupAcceptor.cs">
|
||||
</Compile>
|
||||
|
38
XenModel/Messages.Designer.cs
generated
38
XenModel/Messages.Designer.cs
generated
@ -25594,6 +25594,42 @@ namespace XenAdmin {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Update was not completed successfully.
|
||||
/// </summary>
|
||||
public static string PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR {
|
||||
get {
|
||||
return ResourceManager.GetString("PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Following error occured while automatic upgrade was in progress: .
|
||||
/// </summary>
|
||||
public static string PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR_OCCURED {
|
||||
get {
|
||||
return ResourceManager.GetString("PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR_OCCURED", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Following errors occured while automatic upgrade was in progress:.
|
||||
/// </summary>
|
||||
public static string PATCHINGWIZARD_AUTOUPDATINGPAGE_ERRORS_OCCURED {
|
||||
get {
|
||||
return ResourceManager.GetString("PATCHINGWIZARD_AUTOUPDATINGPAGE_ERRORS_OCCURED", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Update was not completed successfully.
|
||||
/// </summary>
|
||||
public static string PATCHINGWIZARD_AUTOUPDATINGPAGE_FAILED {
|
||||
get {
|
||||
return ResourceManager.GetString("PATCHINGWIZARD_AUTOUPDATINGPAGE_FAILED", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Upload and Install.
|
||||
/// </summary>
|
||||
@ -25964,7 +26000,7 @@ namespace XenAdmin {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to a.
|
||||
/// Looks up a localized string similar to This server is not licensed for automatic updating.
|
||||
/// </summary>
|
||||
public static string PATCHINGWIZARD_SELECTSERVERPAGE_HOST_UNLICENSED_FOR_BATCH_UPDATING {
|
||||
get {
|
||||
|
@ -8853,6 +8853,18 @@ However, there is not enough space to perform the repartitioning, so the current
|
||||
<data name="PASTE" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
<data name="PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR" xml:space="preserve">
|
||||
<value>Update was not completed successfully</value>
|
||||
</data>
|
||||
<data name="PATCHINGWIZARD_AUTOUPDATINGPAGE_ERRORS_OCCURED" xml:space="preserve">
|
||||
<value>Following errors occured while automatic upgrade was in progress:</value>
|
||||
</data>
|
||||
<data name="PATCHINGWIZARD_AUTOUPDATINGPAGE_ERROR_OCCURED" xml:space="preserve">
|
||||
<value>Following error occured while automatic upgrade was in progress: </value>
|
||||
</data>
|
||||
<data name="PATCHINGWIZARD_AUTOUPDATINGPAGE_FAILED" xml:space="preserve">
|
||||
<value>Update was not completed successfully</value>
|
||||
</data>
|
||||
<data name="PATCHINGWIZARD_AUTOUPDATINGPAGE_TEXT" xml:space="preserve">
|
||||
<value>Upload and Install</value>
|
||||
</data>
|
||||
|
Loading…
Reference in New Issue
Block a user