/* 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.Linq; using System.Reflection; using System.Threading; using log4net; using XenAdmin.Network; using XenAPI; namespace XenAdmin.Wizards.PatchingWizard.PlanActions { public abstract class PlanAction : IEquatable { protected static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private int _percentComplete; public event Action OnProgressChange; public Exception Error; protected bool Cancelling; private bool _running; private readonly Guid _actionId; private readonly object historyLock = new object(); private readonly Stack _progressHistory = new Stack(); public int PercentComplete { get { return _percentComplete; } protected set { _percentComplete = value; if (OnProgressChange != null) OnProgressChange(this); } } public List ProgressHistory { get { lock (historyLock) return _progressHistory.Reverse().ToList(); } } protected PlanAction() { _percentComplete = 0; _actionId = Guid.NewGuid(); } protected abstract void _Run(); public virtual void Run() { try { lock (historyLock) _progressHistory.Clear(); _running = true; PercentComplete = 0; _Run(); AddProgressStep(null); } catch (CancelledException e) { ReplaceProgressStep(CurrentProgressStep + Messages.PLAN_ACTION_CANCELLED_BY_USER); Error = e; throw; } catch (Exception e) { Failure f = e as Failure; if (f != null && f.ErrorDescription != null && f.ErrorDescription.Count > 1 && f.ErrorDescription[1].Contains(FriendlyErrorNames.SR_BACKEND_FAILURE_432)) { // ignore this exception (CA-62989) in order to allow the Upgrade wizard to continue // upgrading all the hosts in a pool. The detached SRs will be reported on Finish log.Warn(Messages.STORAGELINK_SR_NEEDS_REATTACH, f); } else { var curStep = CurrentProgressStep; if (!string.IsNullOrEmpty(curStep)) ReplaceProgressStep(CurrentProgressStep + Messages.PLAN_ACTION_ERROR); Error = e; throw; } } finally { _running = false; PercentComplete = 100; } } public string CurrentProgressStep { get { lock (historyLock) return _progressHistory.Count > 0 ? _progressHistory.Peek() : string.Empty; } } protected void AddProgressStep(string step) { lock (historyLock) { if (_progressHistory.Count > 0) { var popped = _progressHistory.Pop(); _progressHistory.Push(popped + Messages.PLAN_ACTION_DONE); } if (step != null) _progressHistory.Push(step); if (OnProgressChange != null) OnProgressChange(this); } } protected void ReplaceProgressStep(string step) { lock (historyLock) { if (_progressHistory.Count > 0) _progressHistory.Pop(); _progressHistory.Push(step); if (OnProgressChange != null) OnProgressChange(this); } } protected string PollTaskForResultAndDestroy(IXenConnection connection, ref Session session, XenRef task) { return PollTaskForResultAndDestroy(connection, ref session, task, 0, 100); } protected string PollTaskForResultAndDestroy(IXenConnection connection, ref Session session, XenRef task, int min, int max) { try { return PollTaskForResult(connection, ref session, task, progress => PercentComplete = progress, min, max); } finally { Task.destroy(session, task); } } protected static string PollTaskForResult(IXenConnection connection, ref Session session, XenRef task, Action updateProgressDelegate) { return PollTaskForResult(connection, ref session, task, updateProgressDelegate, 0, 100); } protected static String PollTaskForResult(IXenConnection connection, ref Session session, XenRef task, Action updateProgressDelegate, int min, int max) { Program.AssertOffEventThread(); task_status_type status; int progress; do { status = (task_status_type)Task.DoWithSessionRetry(connection, ref session, (Task.TaskStatusOp)Task.get_status, task.opaque_ref); progress = min + (int)((max - min) * (double)Task.DoWithSessionRetry(connection, ref session, (Task.TaskProgressOp)Task.get_progress, task.opaque_ref)); updateProgressDelegate(progress); Thread.Sleep(100); } while (status == task_status_type.pending || status == task_status_type.cancelling); if (status == task_status_type.failure) { throw new Failure(Task.get_error_info(session, task)); } else { return Task.get_result(session, task); } } public bool IsComplete { get { return (PercentComplete == 100 && !_running) || Error != null; } } public bool Equals(PlanAction other) { if (other == null) return false; return string.Equals(_actionId.ToString(), other._actionId.ToString(), StringComparison.OrdinalIgnoreCase); } public virtual void Cancel() { Cancelling = true; } } }