Merge pull request #2978 from kc284/actions

Compacted ParallelAction and MultipleAction constructors.
This commit is contained in:
Konstantina Chremmou 2022-04-01 16:07:30 +01:00 committed by GitHub
commit ef86be5347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 131 additions and 153 deletions

View File

@ -63,7 +63,8 @@ namespace XenAdmin.Core
}
else if (actions.Count > 1)
{
var parallelAction = new ParallelAction(null, "", "", "", actions, true, true);
var parallelAction = new ParallelAction(string.Empty, string.Empty, string.Empty, actions,
suppressHistory: true, showSubActionsDetails: true);
parallelAction.Completed += actionCompleted;
parallelAction.RunAsync();
}

View File

@ -764,7 +764,8 @@ namespace XenAdmin.Core
foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy)
actions.Add(new RestoreDismissedUpdatesAction(connection));
var action = new ParallelAction(Messages.RESTORE_DISMISSED_UPDATES, Messages.RESTORING, Messages.COMPLETED, actions, true, false);
var action = new ParallelAction(Messages.RESTORE_DISMISSED_UPDATES, Messages.RESTORING, Messages.COMPLETED,
actions, suppressHistory: true, showSubActionsDetails: false);
action.Completed += action_Completed;
RestoreDismissedUpdatesStarted?.Invoke();

View File

@ -109,11 +109,12 @@ namespace XenAdmin.Diagnostics.Problems.PoolProblem
if (subActions.Count == 0)
return null;
return subActions.Count == 1
? subActions[0]
: new ParallelAction(pool.Connection, Messages.ACTION_MULTIPLE_DR_TASK_CREATE_TITLE,
Messages.ACTION_MULTIPLE_DR_TASK_CREATE_START,
Messages.ACTION_MULTIPLE_DR_TASK_CREATE_END, subActions);
if (subActions.Count == 1)
return subActions[0];
return new ParallelAction(Messages.ACTION_MULTIPLE_DR_TASK_CREATE_TITLE,
Messages.ACTION_MULTIPLE_DR_TASK_CREATE_START,
Messages.ACTION_MULTIPLE_DR_TASK_CREATE_END, subActions, pool.Connection);
}
}

View File

@ -120,8 +120,9 @@ namespace XenAdmin.Dialogs
var batch = from VDI vdi in _vdis
select (AsyncAction)new MoveVirtualDiskAction(connection, vdi, SelectedSR);
new ParallelAction(connection, title, Messages.ACTION_MOVING_X_VDIS_STARTED,
Messages.ACTION_MOVING_X_VDIS_COMPLETED, batch.ToList(), BATCH_SIZE).RunAsync();
new ParallelAction(title, Messages.ACTION_MOVING_X_VDIS_STARTED,
Messages.ACTION_MOVING_X_VDIS_COMPLETED, batch.ToList(),
connection, maxNumberOfParallelActions: BATCH_SIZE).RunAsync();
}
}
@ -160,8 +161,9 @@ namespace XenAdmin.Dialogs
var batch = from VDI vdi in _vdis
select (AsyncAction)new MigrateVirtualDiskAction(connection, vdi, SelectedSR);
new ParallelAction(connection, title, Messages.ACTION_MIGRATING_X_VDIS_STARTED,
Messages.ACTION_MIGRATING_X_VDIS_COMPLETED, batch.ToList(), BATCH_SIZE).RunAsync();
new ParallelAction(title, Messages.ACTION_MIGRATING_X_VDIS_STARTED,
Messages.ACTION_MIGRATING_X_VDIS_COMPLETED, batch.ToList(),
connection, maxNumberOfParallelActions: BATCH_SIZE).RunAsync();
}
}
}

View File

@ -536,9 +536,9 @@ namespace XenAdmin.Wizards
if (actionList.Count == 1)
FinalAction = actionList[0];
else
FinalAction = new ParallelAction(xenConnection, Messages.NEW_SR_WIZARD_FINAL_ACTION_TITLE,
Messages.NEW_SR_WIZARD_FINAL_ACTION_START,
Messages.NEW_SR_WIZARD_FINAL_ACTION_END, actionList);
FinalAction = new ParallelAction(Messages.NEW_SR_WIZARD_FINAL_ACTION_TITLE,
Messages.NEW_SR_WIZARD_FINAL_ACTION_START,
Messages.NEW_SR_WIZARD_FINAL_ACTION_END, actionList, xenConnection);
// if this is a Disaster Recovery Task, it could be either a "Find existing SRs" or an "Attach SR needed for DR" case
if (m_srWizardType.DisasterRecoveryTask)

View File

@ -815,7 +815,10 @@ namespace XenAdmin.Wizards.PatchingWizard
}
}
}
resolvePrechecksAction = new ParallelAction(Messages.PATCHINGWIZARD_PRECHECKPAGE_RESOLVING_ALL, Messages.PATCHINGWIZARD_PRECHECKPAGE_RESOLVING_ALL, Messages.COMPLETED, actions, true, false);
resolvePrechecksAction = new ParallelAction(Messages.PATCHINGWIZARD_PRECHECKPAGE_RESOLVING_ALL,
Messages.PATCHINGWIZARD_PRECHECKPAGE_RESOLVING_ALL, Messages.COMPLETED,
actions, suppressHistory: true, showSubActionsDetails: false);
StartResolvePrechecksAction();
}

View File

@ -39,58 +39,49 @@ using System.Linq;
namespace XenAdmin.Actions
{
// Run several actions. The outer action is asynchronous, but the subactions are run synchronously within that.
/// <summary>
/// Run several actions. The outer action is asynchronous, but the sub-actions are run synchronously within it.
/// </summary>
public class MultipleAction : AsyncAction, IDisposable
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected readonly List<AsyncAction> subActions;
private readonly string endDescription;
public bool ShowSubActionsDetails { get; private set; }
private readonly List<AsyncAction> _subActions;
private readonly string _endDescription;
private readonly bool _stopOnFirstException;
public bool ShowSubActionsDetails { get; }
public string SubActionTitle { get; private set; }
public string SubActionDescription { get; private set; }
public bool StopOnFirstException { get; private set; }
public MultipleAction(IXenConnection connection, string title, string startDescription, string endDescription, List<AsyncAction> subActions, bool suppressHistory)
public MultipleAction(IXenConnection connection, string title, string startDescription,
string endDescription, List<AsyncAction> subActions, bool suppressHistory = false,
bool showSubActionsDetails = false, bool stopOnFirstException = false)
: base(connection, title, startDescription, suppressHistory)
{
this.endDescription = endDescription;
this.subActions = subActions;
this.Completed += MultipleAction_Completed;
_endDescription = endDescription;
_subActions = subActions;
ShowSubActionsDetails = showSubActionsDetails;
_stopOnFirstException = stopOnFirstException;
Completed += MultipleAction_Completed;
RegisterEvents();
}
public MultipleAction(IXenConnection connection, string title, string startDescription, string endDescription, List<AsyncAction> subActions)
: this(connection, title, startDescription, endDescription, subActions, false)
{}
public MultipleAction(IXenConnection connection, string title, string startDescription, string endDescription,
List<AsyncAction> subActions, bool suppressHistory, bool showSubActionsDetails)
: this(connection, title, startDescription, endDescription, subActions, suppressHistory)
{
ShowSubActionsDetails = showSubActionsDetails;
}
public MultipleAction(IXenConnection connection, string title, string startDescription, string endDescription,
List<AsyncAction> subActions, bool suppressHistory, bool showSubActionsDetails, bool stopOnFirstException)
: this(connection, title, startDescription, endDescription, subActions, suppressHistory, showSubActionsDetails)
{
StopOnFirstException = stopOnFirstException;
}
// The multiple action gets its ApiMethodsToRoleCheck by accumulating the lists from each of the subactions
/// <summary>
/// The multiple action gets its ApiMethodsToRoleCheck by accumulating the lists from each of the sub-actions
/// </summary>
public override RbacMethodList GetApiMethodsToRoleCheck
{
get
{
RbacMethodList list = new RbacMethodList();
foreach (AsyncAction subAction in subActions)
var list = new RbacMethodList();
foreach (var subAction in _subActions)
list.AddRange(subAction.GetApiMethodsToRoleCheck);
return list;
}
}
protected override void Run()
protected sealed override void Run()
{
PercentComplete = 0;
var exceptions = new List<Exception>();
@ -98,12 +89,12 @@ namespace XenAdmin.Actions
RunSubActions(exceptions);
Tick(100, endDescription);
Tick(100, _endDescription);
if (exceptions.Count > 1)
{
foreach (Exception e in exceptions)
log.Error(e);
foreach (var e in exceptions)
_log.Error(e);
Exception = new Exception(Messages.SOME_ERRORS_ENCOUNTERED);
}
@ -113,12 +104,12 @@ namespace XenAdmin.Actions
public override void RecomputeCanCancel()
{
CanCancel = !this.IsCompleted && subActions.Any(a => !a.IsCompleted);
CanCancel = !IsCompleted && _subActions.Any(a => !a.IsCompleted);
}
protected override void CancelRelatedTask()
{
foreach (AsyncAction subAction in subActions.Where(a => !a.IsCompleted))
foreach (var subAction in _subActions.Where(a => !a.IsCompleted))
{
subAction.Cancel();
}
@ -126,20 +117,19 @@ namespace XenAdmin.Actions
private void RegisterEvents()
{
foreach (AsyncAction subAction in subActions)
foreach (var subAction in _subActions)
subAction.Changed += SubActionChanged;
}
private void DeregisterEvents()
private void DeRegisterEvents()
{
foreach (AsyncAction subAction in subActions)
foreach (var subAction in _subActions)
subAction.Changed -= SubActionChanged;
}
private void SubActionChanged(ActionBase sender)
{
AsyncAction subAction = sender as AsyncAction;
if (subAction != null)
if (sender is AsyncAction subAction)
{
SubActionTitle = subAction.Title;
SubActionDescription = subAction.Description;
@ -151,15 +141,15 @@ namespace XenAdmin.Actions
protected virtual void RecalculatePercentComplete()
{
int total = 0;
int n = subActions.Count;
foreach (var action in subActions)
int n = _subActions.Count;
foreach (var action in _subActions)
total += action.PercentComplete;
PercentComplete = (int)(total / n);
PercentComplete = total / n;
}
protected virtual void RunSubActions(List<Exception> exceptions)
{
foreach (AsyncAction subAction in subActions)
foreach (var subAction in _subActions)
{
if (Cancelling) // don't start any more actions
break;
@ -170,16 +160,14 @@ namespace XenAdmin.Actions
}
catch (Exception e)
{
Failure f = e as Failure;
if (f != null && Connection != null && f.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
{
if (e is Failure f && Connection != null && f.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
Failure.ParseRBACFailure(f, Connection, Session ?? Connection.Session);
}
exceptions.Add(e);
// Record the first exception we come to. Though later if there are more than one we will replace this with non specific one.
if (Exception == null)
Exception = e;
if (StopOnFirstException)
if (_stopOnFirstException)
break;
}
}
@ -189,9 +177,9 @@ namespace XenAdmin.Actions
{
// The MultipleAction can sometimes complete prematurely, for example
// if the sudo dialog is cancelled, of (less likely) if one of the
// subactions throws an exception in its GetApiMethodsToRoleCheck.
// In this case, we need to cancel this action's subactions.
foreach (AsyncAction subAction in subActions)
// sub-actions throws an exception in its GetApiMethodsToRoleCheck.
// In this case, we need to cancel this action's sub-actions.
foreach (var subAction in _subActions)
{
if (!subAction.IsCompleted)
subAction.Cancel();
@ -202,7 +190,7 @@ namespace XenAdmin.Actions
public void Dispose()
{
DeregisterEvents();
DeRegisterEvents();
}
#endregion

View File

@ -91,7 +91,7 @@ namespace XenAdmin.Actions
else
{
if (_runActionInParallel)
new ParallelAction(connection, _title, _startDescription, _endDescription, actionsByConnection[connection]).RunAsync();
new ParallelAction(_title, _startDescription, _endDescription, actionsByConnection[connection], connection).RunAsync();
else
new MultipleAction(connection, _title, _startDescription, _endDescription, actionsByConnection[connection]).RunAsync();
}
@ -104,7 +104,7 @@ namespace XenAdmin.Actions
else if (actionsWithNoConnection.Count > 1)
{
if (_runActionInParallel)
new ParallelAction(null, _title, _startDescription, _endDescription, actionsWithNoConnection).RunAsync();
new ParallelAction(_title, _startDescription, _endDescription, actionsWithNoConnection).RunAsync();
else
new MultipleAction(null, _title, _startDescription, _endDescription, actionsWithNoConnection).RunAsync();
}

View File

@ -44,99 +44,83 @@ namespace XenAdmin.Actions
/// </summary>
public class ParallelAction : MultipleAction
{
//Change parameter to increase the number of concurrent actions running
/// <summary>
/// Change this to increase the number of concurrent actions running
/// </summary>
private const int DEFAULT_MAX_NUMBER_OF_PARALLEL_ACTIONS = 25;
private Dictionary<IXenConnection, List<AsyncAction>> actionsByConnection = new Dictionary<IXenConnection, List<AsyncAction>>();
private Dictionary<IXenConnection, ProduceConsumerQueue> queuesByConnection = new Dictionary<IXenConnection, ProduceConsumerQueue>();
private readonly Dictionary<IXenConnection, List<AsyncAction>> _actionsByConnection = new Dictionary<IXenConnection, List<AsyncAction>>();
private readonly Dictionary<IXenConnection, ProduceConsumerQueue> _queuesByConnection = new Dictionary<IXenConnection, ProduceConsumerQueue>();
private List<AsyncAction> actionsWithNoConnection = new List<AsyncAction>();
private ProduceConsumerQueue queueWithNoConnection;
private readonly List<AsyncAction> _actionsWithNoConnection = new List<AsyncAction>();
private ProduceConsumerQueue _queueWithNoConnection;
private readonly int maxNumberOfParallelActions;
private int actionsCount;
private readonly int _maxNumberOfParallelActions;
private readonly int _actionsCount;
private readonly object _lock = new object();
private volatile int _completedActionsCount ;
public ParallelAction(IXenConnection connection, string title, string startDescription, string endDescription, List<AsyncAction> subActions, bool suppressHistory, bool showSubActionsDetails, int maxNumberOfParallelActions = DEFAULT_MAX_NUMBER_OF_PARALLEL_ACTIONS)
: base(connection, title, startDescription, endDescription, subActions, suppressHistory, showSubActionsDetails)
{
if (Connection != null)
{
actionsByConnection.Add(Connection, subActions);
actionsCount = subActions.Count;
}
else
GroupActionsByConnection();
this.maxNumberOfParallelActions = maxNumberOfParallelActions;
}
public ParallelAction(IXenConnection connection, string title, string startDescription, string endDescription, List<AsyncAction> subActions, int maxNumberOfParallelActions = DEFAULT_MAX_NUMBER_OF_PARALLEL_ACTIONS)
: this(connection, title, startDescription, endDescription, subActions, false, false, maxNumberOfParallelActions)
{ }
/// <summary>
/// Use this constructor to create a cross connection ParallelAction.
/// It takes a list of any number of actions, separates them by connections
/// and runs a certain number of them simultaneously on each connection, all connections in parallel.
/// Once one simultaneous action is finished the next one in the queue is started until all are complete.
/// Once a simultaneous action is finished the next one in the queue is started until all are complete.
/// </summary>
public ParallelAction(string title, string startDescription, string endDescription, List<AsyncAction> subActions, bool suppressHistory, bool showSubActionsDetails, int maxNumberOfParallelActions = DEFAULT_MAX_NUMBER_OF_PARALLEL_ACTIONS)
: base(null, title, startDescription, endDescription, subActions, suppressHistory, showSubActionsDetails)
public ParallelAction(string title, string startDescription, string endDescription,
List<AsyncAction> subActions, IXenConnection connection = null,
bool suppressHistory = false, bool showSubActionsDetails = false,
int maxNumberOfParallelActions = DEFAULT_MAX_NUMBER_OF_PARALLEL_ACTIONS)
: base(connection, title, startDescription, endDescription, subActions, suppressHistory, showSubActionsDetails)
{
GroupActionsByConnection();
this.maxNumberOfParallelActions = maxNumberOfParallelActions;
}
public ParallelAction(string title, string startDescription, string endDescription, List<AsyncAction> subActions, int maxNumberOfParallelActions = DEFAULT_MAX_NUMBER_OF_PARALLEL_ACTIONS)
: this(title, startDescription, endDescription, subActions, false, false, maxNumberOfParallelActions)
{ }
private void GroupActionsByConnection()
{
actionsCount = 0;
foreach (AsyncAction action in subActions)
if (Connection == null)
{
if (action.Connection != null)
foreach (var action in subActions)
{
if (action.Connection.IsConnected)
if (action.Connection == null)
{
if (!actionsByConnection.ContainsKey(action.Connection))
{
actionsByConnection.Add(action.Connection, new List<AsyncAction>());
}
_actionsWithNoConnection.Add(action);
_actionsCount++;
}
else if (action.Connection.IsConnected)
{
if (!_actionsByConnection.ContainsKey(action.Connection))
_actionsByConnection.Add(action.Connection, new List<AsyncAction>());
actionsByConnection[action.Connection].Add(action);
actionsCount++;
_actionsByConnection[action.Connection].Add(action);
_actionsCount++;
}
}
else
{
actionsWithNoConnection.Add(action);
actionsCount++;
}
}
else
{
_actionsByConnection.Add(Connection, subActions);
_actionsCount = subActions.Count;
}
_maxNumberOfParallelActions = maxNumberOfParallelActions;
}
protected override void RunSubActions(List<Exception> exceptions)
{
if (actionsCount == 0)
if (_actionsCount == 0)
return;
foreach (IXenConnection connection in actionsByConnection.Keys)
foreach (var connection in _actionsByConnection.Keys)
{
queuesByConnection[connection] = new ProduceConsumerQueue(Math.Min(maxNumberOfParallelActions, actionsByConnection[connection].Count));
foreach (AsyncAction subAction in actionsByConnection[connection])
_queuesByConnection[connection] = new ProduceConsumerQueue(Math.Min(_maxNumberOfParallelActions, _actionsByConnection[connection].Count));
foreach (AsyncAction subAction in _actionsByConnection[connection])
{
EnqueueAction(subAction, queuesByConnection[connection], exceptions);
EnqueueAction(subAction, _queuesByConnection[connection], exceptions);
}
}
if (actionsWithNoConnection.Count > 0)
queueWithNoConnection = new ProduceConsumerQueue(Math.Min(maxNumberOfParallelActions, actionsWithNoConnection.Count));
if (_actionsWithNoConnection.Count > 0)
_queueWithNoConnection = new ProduceConsumerQueue(Math.Min(_maxNumberOfParallelActions, _actionsWithNoConnection.Count));
foreach (AsyncAction subAction in actionsWithNoConnection)
foreach (var subAction in _actionsWithNoConnection)
{
EnqueueAction(subAction, queueWithNoConnection, exceptions);
EnqueueAction(subAction, _queueWithNoConnection, exceptions);
}
lock (_lock)
@ -145,7 +129,7 @@ namespace XenAdmin.Actions
}
}
void EnqueueAction(AsyncAction action, ProduceConsumerQueue queue, List<Exception> exceptions)
private void EnqueueAction(AsyncAction action, ProduceConsumerQueue queue, List<Exception> exceptions)
{
action.Completed += action_Completed;
queue.EnqueueItem(
@ -159,12 +143,12 @@ namespace XenAdmin.Actions
}
catch (Exception e)
{
Failure f = e as Failure;
if (f != null && Connection != null &&
if (e is Failure f && Connection != null &&
f.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
{
Failure.ParseRBACFailure(f, action.Connection, action.Session ?? action.Connection.Session);
}
exceptions.Add(e);
// Record the first exception we come to. Though later if there are more than one we will replace this with non specific one.
if (Exception == null)
@ -175,30 +159,30 @@ namespace XenAdmin.Actions
protected override void RecalculatePercentComplete()
{
if (actionsCount == 0)
if (_actionsCount == 0)
return;
int total = 0;
foreach (IXenConnection connection in actionsByConnection.Keys)
foreach (var connection in _actionsByConnection.Keys)
{
foreach (var action in actionsByConnection[connection])
foreach (var action in _actionsByConnection[connection])
total += action.PercentComplete;
}
foreach (var action in actionsWithNoConnection)
foreach (var action in _actionsWithNoConnection)
total += action.PercentComplete;
PercentComplete = (int)(total / actionsCount);
PercentComplete = total / _actionsCount;
}
private readonly object _lock = new object();
private volatile int i = 0;
void action_Completed(ActionBase sender)
private void action_Completed(ActionBase sender)
{
sender.Completed -= action_Completed;
lock (_lock)
{
i++;
if (i == actionsCount)
_completedActionsCount++;
if (_completedActionsCount == _actionsCount)
{
Monitor.Pulse(_lock);
PercentComplete = 100;
@ -210,13 +194,11 @@ namespace XenAdmin.Actions
{
base.MultipleAction_Completed(sender);
foreach (IXenConnection connection in queuesByConnection.Keys)
{
queuesByConnection[connection].StopWorkers(false);
}
foreach (var connection in _queuesByConnection.Keys)
_queuesByConnection[connection].StopWorkers(false);
if (queueWithNoConnection != null)
queueWithNoConnection.StopWorkers(false);
_queueWithNoConnection?.StopWorkers(false);
}
}
}