2018-06-19 13:56:14 +02:00
/ * Copyright ( c ) Citrix Systems , Inc .
* All rights reserved .
*
* Redistribution and use in source and binary forms ,
* with or without modification , are permitted provided
* that the following conditions are met :
*
* * Redistributions of source code must retain the above
* copyright notice , this list of conditions and the
* following disclaimer .
* * Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the
* following disclaimer in the documentation and / or other
* materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES ,
* INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ,
* WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
* /
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
2018-11-07 17:07:53 +01:00
using System.Diagnostics ;
using System.Drawing ;
2018-06-19 13:56:14 +02:00
using System.Reflection ;
using log4net ;
using XenAdmin.Controls ;
using XenAdmin.Wizards.PatchingWizard.PlanActions ;
using XenAPI ;
using System.Linq ;
using XenAdmin.Core ;
using System.Text ;
2018-11-07 17:07:53 +01:00
using System.Windows.Forms ;
2018-07-02 16:48:33 +02:00
using XenAdmin.Diagnostics.Problems ;
2018-11-07 17:07:53 +01:00
using XenAdmin.Dialogs ;
2018-10-04 15:38:07 +02:00
using XenAdmin.Wizards.RollingUpgradeWizard.PlanActions ;
2018-06-29 20:49:28 +02:00
2018-06-19 13:56:14 +02:00
namespace XenAdmin.Wizards.PatchingWizard
{
2018-06-21 13:28:49 +02:00
public enum Status { NotStarted , Started , Cancelled , Completed }
2018-06-20 10:42:01 +02:00
public abstract partial class AutomatedUpdatesBasePage : XenTabPage
2018-06-19 13:56:14 +02:00
{
protected static readonly ILog log = LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2019-01-10 14:24:42 +01:00
protected bool _thisPageIsCompleted ;
2018-06-19 13:56:14 +02:00
2018-07-02 16:48:33 +02:00
public List < Problem > PrecheckProblemsActuallyResolved { private get ; set ; }
2018-06-19 13:56:14 +02:00
public List < Pool > SelectedPools { private get ; set ; }
2018-06-21 13:28:49 +02:00
public bool ApplyUpdatesToNewVersion { get ; set ; }
public Status Status { get ; private set ; }
2019-01-10 14:24:42 +01:00
protected bool IsSuccess
{
get { return _thisPageIsCompleted & & ! failedWorkers . Any ( ) ; }
}
2018-06-19 13:56:14 +02:00
private List < UpdateProgressBackgroundWorker > backgroundWorkers = new List < UpdateProgressBackgroundWorker > ( ) ;
private List < UpdateProgressBackgroundWorker > failedWorkers = new List < UpdateProgressBackgroundWorker > ( ) ;
2019-01-10 14:24:42 +01:00
private List < HostUpdateMapping > patchMappings = new List < HostUpdateMapping > ( ) ;
2018-10-04 15:33:17 +02:00
protected List < string > hostsThatWillRequireReboot = new List < string > ( ) ;
2018-06-22 11:58:08 +02:00
public Dictionary < XenServerPatch , string > AllDownloadedPatches = new Dictionary < XenServerPatch , string > ( ) ;
2018-06-19 13:56:14 +02:00
public AutomatedUpdatesBasePage ( )
{
InitializeComponent ( ) ;
panel1 . Visible = false ;
}
2018-06-20 10:42:01 +02:00
#region XenTabPage overrides
2018-06-19 13:56:14 +02:00
public override bool EnablePrevious ( )
{
return false ;
}
public override bool EnableNext ( )
{
2019-01-10 14:24:42 +01:00
return _thisPageIsCompleted ;
2018-06-19 13:56:14 +02:00
}
private bool _cancelEnabled = true ;
public override bool EnableCancel ( )
{
return _cancelEnabled ;
}
2018-12-20 13:33:26 +01:00
public override void PageCancelled ( ref bool cancel )
2018-06-19 13:56:14 +02:00
{
2018-12-20 14:23:49 +01:00
if ( _thisPageIsCompleted )
return ;
using ( var dlog = new ThreeButtonDialog (
new ThreeButtonDialog . Details ( SystemIcons . Warning , ReconsiderCancellationMessage ( ) , Text ) ,
ThreeButtonDialog . ButtonYes ,
ThreeButtonDialog . ButtonNo ) )
2018-06-19 13:56:14 +02:00
{
2018-12-20 14:23:49 +01:00
if ( dlog . ShowDialog ( this ) ! = DialogResult . Yes )
{
cancel = true ;
return ;
}
2018-06-19 13:56:14 +02:00
}
2018-12-20 14:23:49 +01:00
Status = Status . Cancelled ;
backgroundWorkers . ForEach ( bgw = > bgw . CancelAsync ( ) ) ;
2018-06-19 13:56:14 +02:00
}
2018-11-07 17:07:53 +01:00
2018-06-19 13:56:14 +02:00
protected override void PageLoadedCore ( PageLoadedDirection direction )
{
if ( _thisPageIsCompleted )
return ;
2019-01-10 14:24:42 +01:00
panel1 . Visible = false ;
2018-06-21 13:28:49 +02:00
Status = Status . NotStarted ;
2018-06-20 10:42:01 +02:00
labelTitle . Text = BlurbText ( ) ;
2018-06-19 13:56:14 +02:00
if ( ! StartUpgradeWorkers ( ) )
{
2018-06-21 13:28:49 +02:00
Status = Status . Completed ;
2018-06-19 13:56:14 +02:00
_thisPageIsCompleted = true ;
2019-02-22 14:10:21 +01:00
UpdateStatusOnCompletion ( ) ;
UpdateStatus ( ) ;
2018-06-19 13:56:14 +02:00
OnPageUpdated ( ) ;
}
2018-06-21 13:28:49 +02:00
else
{
Status = Status . Started ;
}
2018-06-19 13:56:14 +02:00
}
2018-06-20 10:42:01 +02:00
#endregion
#region Virtual members
2018-06-22 16:57:31 +02:00
protected abstract string BlurbText ( ) ;
protected abstract string SuccessMessageOnCompletion ( bool multiplePools ) ;
protected abstract string FailureMessageOnCompletion ( bool multiplePools ) ;
2018-08-17 13:05:16 +02:00
protected abstract string SuccessMessagePerPool ( Pool pool ) ;
2018-06-22 16:57:31 +02:00
protected abstract string FailureMessagePerPool ( bool multipleErrors ) ;
2018-06-26 15:04:51 +02:00
protected abstract string UserCancellationMessage ( ) ;
2018-12-20 14:23:49 +01:00
protected abstract string ReconsiderCancellationMessage ( ) ;
2018-06-20 10:42:01 +02:00
2018-06-29 20:49:28 +02:00
protected abstract List < HostPlan > GenerateHostPlans ( Pool pool , out List < Host > applicableHosts ) ;
2018-06-20 10:42:01 +02:00
2018-06-21 13:28:49 +02:00
protected virtual bool SkipInitialPlanActions ( Host host )
{
return false ;
}
2018-06-22 11:58:08 +02:00
protected virtual void DoAfterInitialPlanActions ( UpdateProgressBackgroundWorker bgw , Host host , List < Host > hosts ) { }
2018-06-20 10:42:01 +02:00
#endregion
2018-06-19 13:56:14 +02:00
2018-06-20 10:42:01 +02:00
#region background workers
2018-06-19 13:56:14 +02:00
private bool StartUpgradeWorkers ( )
{
2018-07-02 14:07:44 +02:00
//reset the background workers
backgroundWorkers = new List < UpdateProgressBackgroundWorker > ( ) ;
2019-01-10 14:24:42 +01:00
failedWorkers = new List < UpdateProgressBackgroundWorker > ( ) ;
2018-06-19 13:56:14 +02:00
bool atLeastOneWorkerStarted = false ;
2018-07-02 14:07:44 +02:00
2018-06-19 13:56:14 +02:00
foreach ( var pool in SelectedPools )
{
2018-06-29 20:49:28 +02:00
List < Host > applicableHosts ;
var planActions = GenerateHostPlans ( pool , out applicableHosts ) ;
2018-06-19 13:56:14 +02:00
var finalActions = new List < PlanAction > ( ) ;
2019-01-10 14:24:42 +01:00
if ( PrecheckProblemsActuallyResolved ! = null )
{
//add a revert pre-check action for this pool
var curPool = pool ;
var problemsToRevert = PrecheckProblemsActuallyResolved . Where ( a = >
a . SolutionAction ! = null & & Helpers . GetPoolOfOne ( a . SolutionAction . Connection ) . Equals ( curPool ) ) . ToList ( ) ;
2018-06-29 20:49:28 +02:00
2019-01-10 14:24:42 +01:00
if ( problemsToRevert . Count > 0 )
finalActions . Add ( new UnwindProblemsAction ( problemsToRevert , pool . Connection ) ) ;
}
2018-06-19 13:56:14 +02:00
if ( planActions . Count > 0 )
{
atLeastOneWorkerStarted = true ;
2018-08-17 13:05:16 +02:00
StartNewWorker ( pool , planActions , finalActions ) ;
2018-06-19 13:56:14 +02:00
}
}
return atLeastOneWorkerStarted ;
}
2018-08-17 13:05:16 +02:00
private void StartNewWorker ( Pool pool , List < HostPlan > planActions , List < PlanAction > finalActions )
2018-06-19 13:56:14 +02:00
{
2018-08-17 13:05:16 +02:00
var bgw = new UpdateProgressBackgroundWorker ( pool , planActions , finalActions )
2018-07-02 14:07:44 +02:00
{
WorkerReportsProgress = true ,
WorkerSupportsCancellation = true
} ;
2018-06-19 13:56:14 +02:00
bgw . DoWork + = WorkerDoWork ;
bgw . ProgressChanged + = WorkerProgressChanged ;
bgw . RunWorkerCompleted + = WorkerCompleted ;
2018-07-02 14:07:44 +02:00
backgroundWorkers . Add ( bgw ) ;
2018-06-19 13:56:14 +02:00
bgw . RunWorkerAsync ( ) ;
}
private void WorkerProgressChanged ( object sender , ProgressChangedEventArgs e )
{
2018-07-16 12:16:33 +02:00
var bgw = sender as UpdateProgressBackgroundWorker ;
if ( bgw = = null )
2018-06-19 13:56:14 +02:00
return ;
2018-07-16 12:16:33 +02:00
if ( ! bgw . CancellationPending )
2018-06-19 13:56:14 +02:00
{
PlanAction action = ( PlanAction ) e . UserState ;
if ( action ! = null )
{
if ( ! action . IsComplete )
{
2018-07-16 12:16:33 +02:00
if ( ! bgw . InProgressActions . Contains ( action ) )
bgw . InProgressActions . Add ( action ) ;
2018-06-19 13:56:14 +02:00
}
else
{
2018-07-16 12:16:33 +02:00
if ( ! bgw . DoneActions . Contains ( action ) )
bgw . DoneActions . Add ( action ) ;
bgw . InProgressActions . Remove ( action ) ;
2018-06-19 13:56:14 +02:00
if ( action . Error = = null )
{
// remove the successful action from the cleanup actions (we are running the cleanup actions in case of failures or if the user cancelled the process, but we shouldn't re-run the actions that have already been run)
2018-07-16 12:16:33 +02:00
bgw . CleanupActions . Remove ( action ) ;
}
else
{
if ( ! failedWorkers . Contains ( bgw ) )
failedWorkers . Add ( bgw ) ;
2018-06-19 13:56:14 +02:00
}
}
}
2018-06-28 19:52:14 +02:00
UpdateStatus ( ) ;
2018-06-19 13:56:14 +02:00
}
}
2018-06-28 19:52:14 +02:00
private void UpdateStatus ( )
2018-06-19 13:56:14 +02:00
{
2019-02-22 14:10:21 +01:00
var newVal = backgroundWorkers . Count > 0
? backgroundWorkers . Sum ( b = > b . PercentComplete ) / backgroundWorkers . Count
: 100 ;
2018-06-28 19:52:14 +02:00
if ( newVal < 0 )
newVal = 0 ;
else if ( newVal > 100 )
newVal = 100 ;
progressBar . Value = ( int ) newVal ;
2018-06-19 13:56:14 +02:00
var allsb = new StringBuilder ( ) ;
foreach ( var bgw in backgroundWorkers )
{
int bgwErrorCount = 0 ;
2018-06-26 15:04:51 +02:00
int bgwCancellationCount = 0 ;
2018-06-19 13:56:14 +02:00
var sb = new StringBuilder ( ) ;
var errorSb = new StringBuilder ( ) ;
2018-08-17 13:05:16 +02:00
if ( ! string . IsNullOrEmpty ( bgw . Name ) )
2018-06-19 13:56:14 +02:00
sb . AppendLine ( string . Format ( "{0}:" , bgw . Name ) ) ;
foreach ( var pa in bgw . DoneActions )
{
2018-06-26 15:04:51 +02:00
pa . ProgressHistory . ForEach ( step = > sb . AppendIndented ( step ) . AppendLine ( ) ) ;
2018-06-19 13:56:14 +02:00
if ( pa . Error ! = null )
{
2018-06-21 13:28:49 +02:00
if ( pa . Error is CancelledException )
{
2018-06-26 15:04:51 +02:00
bgwCancellationCount + + ;
2018-06-21 13:28:49 +02:00
continue ;
}
2018-06-19 13:56:14 +02:00
var innerEx = pa . Error . InnerException as Failure ;
2018-07-16 12:16:33 +02:00
errorSb . AppendLine ( innerEx = = null ? pa . Error . Message : innerEx . Message ) ;
2018-11-07 17:07:53 +01:00
if ( pa . IsSkippable )
{
Debug . Assert ( ! string . IsNullOrEmpty ( pa . Title ) ) ;
errorSb . AppendLine ( string . Format ( Messages . RPU_WIZARD_ERROR_SKIP_MSG , pa . Title ) ) . AppendLine ( ) ;
}
2018-06-19 13:56:14 +02:00
bgwErrorCount + + ;
}
}
foreach ( var pa in bgw . InProgressActions )
{
2018-06-26 15:04:51 +02:00
pa . ProgressHistory . ForEach ( step = > sb . AppendIndented ( step ) . AppendLine ( ) ) ;
2018-06-19 13:56:14 +02:00
}
sb . AppendLine ( ) ;
2018-06-26 15:04:51 +02:00
if ( bgwCancellationCount > 0 )
{
sb . AppendIndented ( UserCancellationMessage ( ) ) . AppendLine ( ) ;
}
else if ( bgwErrorCount > 0 )
2018-06-19 13:56:14 +02:00
{
2018-06-22 16:57:31 +02:00
sb . AppendIndented ( FailureMessagePerPool ( bgwErrorCount > 1 ) ) . AppendLine ( ) ;
2018-06-19 13:56:14 +02:00
sb . AppendIndented ( errorSb ) ;
}
else if ( ! bgw . IsBusy )
{
2018-08-17 13:05:16 +02:00
sb . AppendIndented ( SuccessMessagePerPool ( bgw . Pool ) ) . AppendLine ( ) ;
2018-06-19 13:56:14 +02:00
}
sb . AppendLine ( ) ;
allsb . Append ( sb ) ;
}
textBoxLog . Text = allsb . ToString ( ) ;
textBoxLog . SelectionStart = textBoxLog . Text . Length ;
textBoxLog . ScrollToCaret ( ) ;
}
private void WorkerDoWork ( object sender , DoWorkEventArgs doWorkEventArgs )
{
var bgw = sender as UpdateProgressBackgroundWorker ;
if ( bgw = = null )
return ;
PlanAction action = null ;
try
{
2018-06-25 01:59:21 +02:00
foreach ( var hp in bgw . HostPlans )
2018-06-19 13:56:14 +02:00
{
2018-06-25 01:59:21 +02:00
var host = hp . Host ;
2018-06-19 13:56:14 +02:00
2018-06-22 11:58:08 +02:00
// Step 1: InitialPlanActions (e.g. upgrade the host in the RPU case)
2018-06-25 01:59:21 +02:00
bgw . ProgressIncrement = bgw . InitialActionsIncrement ( hp ) ;
2018-06-21 13:28:49 +02:00
if ( ! SkipInitialPlanActions ( host ) )
2018-06-19 13:56:14 +02:00
{
2018-06-25 01:59:21 +02:00
var initialActions = hp . InitialPlanActions ;
2018-06-19 13:56:14 +02:00
2018-06-21 13:28:49 +02:00
foreach ( var a in initialActions )
2018-06-19 13:56:14 +02:00
{
2018-06-21 13:28:49 +02:00
action = a ;
2018-06-26 15:04:51 +02:00
bgw . RunPlanAction ( action , ref doWorkEventArgs ) ;
2018-06-21 13:28:49 +02:00
}
}
2018-06-22 11:58:08 +02:00
2018-06-25 01:59:21 +02:00
DoAfterInitialPlanActions ( bgw , host , bgw . HostPlans . Select ( h = > h . Host ) . ToList ( ) ) ;
2018-06-22 11:58:08 +02:00
// Step 2: UpdatesPlanActions (priority update action)
2018-06-25 01:59:21 +02:00
bgw . ProgressIncrement = bgw . UpdatesActionsIncrement ( hp ) ;
var planActions = hp . UpdatesPlanActions ;
2018-06-19 13:56:14 +02:00
foreach ( var a in planActions )
{
action = a ;
2018-06-26 15:04:51 +02:00
bgw . RunPlanAction ( action , ref doWorkEventArgs ) ;
2018-06-19 13:56:14 +02:00
}
2018-10-04 15:33:17 +02:00
// Step 3: Rearrange DelayedActions
2018-10-04 15:38:07 +02:00
var suppPackPlanAction = ( RpuUploadAndApplySuppPackPlanAction ) planActions . FirstOrDefault ( pa = > pa is RpuUploadAndApplySuppPackPlanAction ) ;
if ( suppPackPlanAction ! = null )
{
foreach ( var dpa in suppPackPlanAction . DelayedPlanActions )
{
if ( ! hp . DelayedPlanActions . Exists ( a = > a . GetType ( ) = = dpa . GetType ( ) ) )
hp . DelayedPlanActions . Add ( dpa ) ;
}
}
2018-10-04 15:33:17 +02:00
var restartHostPlanAction = ( RestartHostPlanAction ) hp . DelayedPlanActions . FirstOrDefault ( a = > a is RestartHostPlanAction ) ;
if ( restartHostPlanAction ! = null )
{
if ( restartHostPlanAction . SkipRestartHost ( host ) )
{
log . Debug ( "Did not find patches requiring reboot (live patching succeeded)."
+ " Skipping scheduled restart." ) ;
hp . DelayedPlanActions . RemoveAll ( a = > a is RestartHostPlanAction ) ;
}
else
{
hp . DelayedPlanActions . RemoveAll ( a = > a is RestartAgentPlanAction ) ;
}
}
// Step 4: DelayedActions
2018-06-25 01:59:21 +02:00
bgw . ProgressIncrement = bgw . DelayedActionsIncrement ( hp ) ;
2018-10-04 15:33:17 +02:00
// running delayed actions
2018-06-25 01:59:21 +02:00
var delayedActions = hp . DelayedPlanActions ;
2018-06-19 13:56:14 +02:00
var restartActions = delayedActions . Where ( a = > a is RestartHostPlanAction ) . ToList ( ) ;
foreach ( var a in restartActions )
{
action = a ;
2018-06-26 15:04:51 +02:00
bgw . RunPlanAction ( action , ref doWorkEventArgs ) ;
2018-06-19 13:56:14 +02:00
}
var otherActions = delayedActions . Where ( a = > ! ( a is RestartHostPlanAction ) ) . ToList ( ) ;
foreach ( var a in otherActions )
{
action = a ;
2018-10-04 15:33:17 +02:00
bgw . RunPlanAction ( action , ref doWorkEventArgs ) ;
2018-06-19 13:56:14 +02:00
}
}
2018-10-04 15:33:17 +02:00
// Step 5: FinalActions (eg. revert pre-checks)
2018-06-25 01:59:21 +02:00
bgw . ProgressIncrement = bgw . FinalActionsIncrement ;
2018-06-19 13:56:14 +02:00
foreach ( var a in bgw . FinalActions )
{
action = a ;
2018-06-26 15:04:51 +02:00
bgw . RunPlanAction ( action , ref doWorkEventArgs ) ;
2018-06-19 13:56:14 +02:00
}
}
catch ( Exception e )
{
if ( action . Error = = null )
action . Error = new Exception ( Messages . ERROR_UNKNOWN ) ;
2018-06-26 15:04:51 +02:00
log . ErrorFormat ( "Failed to carry out plan. {0} {1}" , action . CurrentProgressStep , e ) ;
doWorkEventArgs . Result = new Exception ( action . CurrentProgressStep , e ) ;
2018-06-19 13:56:14 +02:00
}
}
2018-06-26 15:04:51 +02:00
private void WorkerCompleted ( object sender , RunWorkerCompletedEventArgs e )
2018-06-19 13:56:14 +02:00
{
2018-06-26 15:04:51 +02:00
if ( e . Cancelled )
2018-06-25 01:09:25 +02:00
{
2019-02-22 14:10:21 +01:00
Status = Status . Cancelled ;
2018-06-26 15:04:51 +02:00
panel1 . Visible = true ;
labelError . Text = UserCancellationMessage ( ) ;
pictureBox1 . Image = Images . StaticImages . cancelled_action_16 ;
2018-11-07 17:07:53 +01:00
buttonRetry . Visible = buttonSkip . Visible = false ;
2018-06-26 15:04:51 +02:00
_thisPageIsCompleted = true ;
_cancelEnabled = false ;
2018-06-25 01:09:25 +02:00
}
2018-06-26 15:04:51 +02:00
else
2018-06-19 13:56:14 +02:00
{
var bgw = sender as UpdateProgressBackgroundWorker ;
2018-11-07 17:07:53 +01:00
var someWorkersCancelled = false ;
2018-06-26 15:04:51 +02:00
if ( bgw ! = null )
2018-06-19 13:56:14 +02:00
{
2018-06-28 19:52:14 +02:00
var workerSucceeded = true ;
2018-11-07 17:07:53 +01:00
var failedAction = bgw . DoneActions . FirstOrDefault ( a = > a . Error ! = null & & ! ( a . Error is CancelledException ) ) ;
if ( failedAction ! = null )
2018-06-28 19:52:14 +02:00
{
workerSucceeded = false ;
2018-11-07 17:07:53 +01:00
if ( failedAction . IsSkippable )
{
Debug . Assert ( ! string . IsNullOrEmpty ( failedAction . Title ) ) ;
bgw . FirstFailedSkippableAction = failedAction ;
}
2018-06-28 19:52:14 +02:00
}
2018-06-26 15:04:51 +02:00
if ( bgw . DoneActions . Any ( a = > a . Error is CancelledException ) )
2018-06-28 19:52:14 +02:00
{
workerSucceeded = false ;
2018-06-26 15:04:51 +02:00
someWorkersCancelled = true ;
2018-06-28 19:52:14 +02:00
}
if ( workerSucceeded )
bgw . PercentComplete = 100 ;
2018-06-19 13:56:14 +02:00
}
2019-02-22 14:10:21 +01:00
2018-06-19 13:56:14 +02:00
//if all finished
if ( backgroundWorkers . All ( w = > ! w . IsBusy ) )
2019-02-22 14:10:21 +01:00
UpdateStatusOnCompletion ( someWorkersCancelled ) ;
}
2018-11-07 17:07:53 +01:00
2019-02-22 14:10:21 +01:00
UpdateStatus ( ) ;
OnPageUpdated ( ) ;
}
2018-11-07 17:07:53 +01:00
2019-02-22 14:10:21 +01:00
private void UpdateStatusOnCompletion ( bool someWorkersCancelled = false )
{
Status = Status . Completed ;
panel1 . Visible = true ;
2018-11-07 17:07:53 +01:00
2019-02-22 14:10:21 +01:00
if ( failedWorkers . Any ( ) )
{
labelError . Text = FailureMessageOnCompletion ( backgroundWorkers . Count > 1 ) ;
pictureBox1 . Image = Images . StaticImages . _000_error_h32bit_16 ;
buttonRetry . Visible = true ;
buttonSkip . Visible = false ;
2018-06-19 13:56:14 +02:00
2019-02-22 14:10:21 +01:00
if ( failedWorkers . Any ( w = > w . FirstFailedSkippableAction ! = null ) )
buttonSkip . Visible = true ;
}
else if ( someWorkersCancelled )
{
labelError . Text = UserCancellationMessage ( ) ;
pictureBox1 . Image = Images . StaticImages . cancelled_action_16 ;
buttonRetry . Visible = buttonSkip . Visible = false ;
}
else
{
labelError . Text = SuccessMessageOnCompletion ( backgroundWorkers . Count > 1 ) ;
pictureBox1 . Image = Images . StaticImages . _000_Tick_h32bit_16 ;
buttonRetry . Visible = buttonSkip . Visible = false ;
2018-06-19 13:56:14 +02:00
}
2019-02-22 14:10:21 +01:00
_thisPageIsCompleted = true ;
_cancelEnabled = false ;
2018-06-19 13:56:14 +02:00
}
2019-02-22 14:10:21 +01:00
2018-11-07 17:07:53 +01:00
#endregion
2018-06-19 13:56:14 +02:00
private void RetryFailedActions ( )
{
panel1 . Visible = false ;
2018-11-07 17:07:53 +01:00
failedWorkers . ForEach ( bgw = > bgw . FirstFailedSkippableAction = null ) ;
2018-06-19 13:56:14 +02:00
var workers = new List < UpdateProgressBackgroundWorker > ( failedWorkers ) ;
failedWorkers . Clear ( ) ;
foreach ( var failedWorker in workers )
{
failedWorker . RunWorkerAsync ( ) ;
}
_thisPageIsCompleted = false ;
_cancelEnabled = true ;
OnPageUpdated ( ) ;
}
2018-11-07 17:07:53 +01:00
private void SkipFailedActions ( )
{
var skippableWorkers = failedWorkers . Where ( w = > w . FirstFailedSkippableAction ! = null ) . ToList ( ) ;
var msg = string . Join ( Environment . NewLine , skippableWorkers . Select ( w = > w . FirstFailedSkippableAction . Title ) ) ;
using ( var dlg = new ThreeButtonDialog (
new ThreeButtonDialog . Details ( SystemIcons . Warning ,
string . Format ( skippableWorkers . Count > 1 ? Messages . MESSAGEBOX_SKIP_RPU_STEPS : Messages . MESSAGEBOX_SKIP_RPU_STEP , msg ) ,
ParentForm ! = null ? ParentForm . Text : Messages . XENCENTER ) ,
ThreeButtonDialog . ButtonYes ,
ThreeButtonDialog . ButtonNo ) )
{
if ( dlg . ShowDialog ( this ) ! = DialogResult . Yes )
return ;
}
panel1 . Visible = false ;
foreach ( var worker in skippableWorkers )
{
failedWorkers . Remove ( worker ) ;
worker . RunWorkerAsync ( ) ;
}
_thisPageIsCompleted = false ;
_cancelEnabled = true ;
OnPageUpdated ( ) ;
}
2018-06-19 13:56:14 +02:00
private void buttonRetry_Click ( object sender , EventArgs e )
{
RetryFailedActions ( ) ;
}
2018-06-22 11:58:08 +02:00
2018-11-07 17:07:53 +01:00
private void buttonSkip_Click ( object sender , EventArgs e )
{
SkipFailedActions ( ) ;
}
2018-06-25 17:31:46 +02:00
protected HostPlan GetUpdatePlanActionsForHost ( Host host , List < Host > hosts , List < XenServerPatch > minimalPatches ,
List < XenServerPatch > uploadedPatches , KeyValuePair < XenServerPatch , string > patchFromDisk , bool repatriateVms = true )
2018-06-22 11:58:08 +02:00
{
var patchSequence = Updates . GetPatchSequenceForHost ( host , minimalPatches ) ;
if ( patchSequence = = null )
2018-06-25 01:59:21 +02:00
return new HostPlan ( host , null , null , null ) ;
2018-06-22 11:58:08 +02:00
var planActionsPerHost = new List < PlanAction > ( ) ;
var delayedActionsPerHost = new List < PlanAction > ( ) ;
foreach ( var patch in patchSequence )
{
if ( ! uploadedPatches . Contains ( patch ) )
{
planActionsPerHost . Add ( new DownloadPatchPlanAction ( host . Connection , patch , AllDownloadedPatches , patchFromDisk ) ) ;
2019-01-10 14:24:42 +01:00
planActionsPerHost . Add ( new UploadPatchToMasterPlanAction ( this , host . Connection , patch , patchMappings , AllDownloadedPatches , patchFromDisk , true ) ) ;
2018-06-22 11:58:08 +02:00
uploadedPatches . Add ( patch ) ;
}
2018-10-04 15:33:17 +02:00
planActionsPerHost . Add ( new PatchPrecheckOnHostPlanAction ( host . Connection , patch , host , patchMappings , hostsThatWillRequireReboot ) ) ;
2018-06-22 11:58:08 +02:00
planActionsPerHost . Add ( new ApplyXenServerPatchPlanAction ( host , patch , patchMappings ) ) ;
2018-10-04 15:33:17 +02:00
var action = GetAfterApplyGuidanceAction ( host , patch . after_apply_guidance ) ;
2018-07-02 17:23:14 +02:00
if ( action ! = null )
{
if ( patch . GuidanceMandatory )
2018-06-22 11:58:08 +02:00
{
2018-11-30 11:52:22 +01:00
// if this update requires a mandatory toolstack restart and there is a pending host reboot in the delayed actions,
// then the pending reboot should be carried out instead
if ( patch . after_apply_guidance = = after_apply_guidance . restartXAPI & & delayedActionsPerHost . Any ( a = > a is RestartHostPlanAction ) )
{
// replace the action with a host reboot action which will fall back to a toolstack restart if the reboot is not needed because the live patching succedeed
action = new RestartHostPlanAction ( host , host . GetRunningVMs ( ) , true , true , hostsThatWillRequireReboot ) ;
}
2018-06-22 11:58:08 +02:00
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 ( ) ) ;
}
2018-07-02 17:23:14 +02:00
else
{
// add the action if it's not already in the list
if ( delayedActionsPerHost . All ( a = > a . GetType ( ) ! = action . GetType ( ) ) )
delayedActionsPerHost . Add ( action ) ;
}
2018-06-22 11:58:08 +02:00
}
var isLastHostInPool = hosts . IndexOf ( host ) = = hosts . Count - 1 ;
if ( isLastHostInPool )
{
// add cleanup action for current patch at the end of the update seuence for the last host in the pool
var master = Helpers . GetMaster ( host . Connection ) ;
planActionsPerHost . Add ( new RemoveUpdateFileFromMasterPlanAction ( master , patchMappings , patch ) ) ;
}
}
2018-06-25 17:31:46 +02:00
if ( repatriateVms )
{
var lastRestart = delayedActionsPerHost . FindLast ( a = > a is RestartHostPlanAction )
? ? planActionsPerHost . FindLast ( a = > a is RestartHostPlanAction ) ;
2018-06-22 11:58:08 +02:00
2018-06-25 17:31:46 +02:00
if ( lastRestart ! = null )
( ( RestartHostPlanAction ) lastRestart ) . EnableOnly = false ;
}
2018-06-22 11:58:08 +02:00
2018-06-25 01:59:21 +02:00
return new HostPlan ( host , null , planActionsPerHost , delayedActionsPerHost ) ;
2018-06-22 11:58:08 +02:00
}
2018-10-04 15:33:17 +02:00
private PlanAction GetAfterApplyGuidanceAction ( Host host , after_apply_guidance guidance )
2018-06-22 11:58:08 +02:00
{
switch ( guidance )
{
case after_apply_guidance . restartHost :
2018-11-30 11:52:22 +01:00
return new RestartHostPlanAction ( host , host . GetRunningVMs ( ) , true , false , hostsThatWillRequireReboot ) ;
2018-06-22 11:58:08 +02:00
case after_apply_guidance . restartXAPI :
return new RestartAgentPlanAction ( host ) ;
case after_apply_guidance . restartHVM :
return new RebootVMsPlanAction ( host , host . GetRunningHvmVMs ( ) ) ;
case after_apply_guidance . restartPV :
return new RebootVMsPlanAction ( host , host . GetRunningPvVMs ( ) ) ;
default :
return null ;
}
}
2018-06-19 13:56:14 +02:00
}
}