xenadmin/XenModel/Actions/Action.cs
Konstantina Chremmou 3e71da4e0a Do not trigger the Action.Change event when the PercentComplete or the Description
are set unless the new value is actually different. Small efficiency improvements
to the DownloadPatch action and plan action.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2018-06-28 12:25:56 +01:00

506 lines
14 KiB
C#

/* 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.Text;
using XenAdmin.Network;
using XenAPI;
using XenAdmin.Core;
namespace XenAdmin.Actions
{
public class ActionBase
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public string Title;
/// <summary>
/// The client-side, local time that this action started.
/// </summary>
public DateTime Started = DateTime.Now;
/// <summary>
/// The client-side, local time that this action finished.
/// </summary>
public DateTime Finished;
// Used for pairing incoming MessageActions with the action that triggered them
private Pool _pool;
private Host _host;
private VM _vM;
private SR _sR;
private VM _template;
private IXenConnection connection;
public virtual IXenConnection Connection
{
get{ return connection; }
protected set { connection = value; }
}
/// <summary>
/// Indicates whether XenCenter can exit safely while a task is still in
/// progress. If it's not safe, the MainWindow will try to cancel the
/// running task on exit. If it is safe, the task will be left to complete
/// after XenCenter has exited.
/// Use this for composite tasks when an action has more than one xapi call.
/// </summary>
private bool safeToExit = true;
public bool SafeToExit
{
get { return safeToExit; }
protected set { safeToExit = value; }
}
/// <summary>
/// A list of opaque_refs, giving all the objects that this action applies to.
/// Pool, Host, VM, SR, and Template will be added to this list automatically.
/// </summary>
public readonly List<string> AppliesTo = new List<string>();
public Pool Pool
{
get
{
return _pool;
}
set
{
_pool = value;
SetAppliesTo(Pool);
if (Pool != null && Pool.Connection != null &&
Helpers.GetPool(Pool.Connection) == null &&
Pool.Connection.Cache.Hosts.Length == 1)
{
SetAppliesTo(Pool.Connection.Cache.Hosts[0]);
}
}
}
public Host Host
{
get
{
return _host;
}
set
{
_host = value;
SetAppliesTo(Host);
}
}
public VM VM
{
get
{
return _vM;
}
set
{
_vM = value;
if (value == null)
return;
if (_vM.is_a_snapshot)
{
VM parentVM = _vM.Connection.Resolve<VM>(_vM.snapshot_of);
SetAppliesTo(parentVM);
}
else
SetAppliesTo(VM);
}
}
public SR SR
{
get
{
return _sR;
}
set
{
_sR = value;
SetAppliesTo(SR);
if (Host == null)
Host = SR.Home();
}
}
public VM Template
{
get
{
return _template;
}
set
{
_template = value;
SetAppliesTo(Template);
}
}
protected void SetAppliesTo(IXenObject xo)
{
if (xo == null)
return;
if (xo is Pool)
{
Pool pool = (Pool)xo;
AppliesTo.Add(pool.opaque_ref);
}
else if (xo is Host)
{
Host host = (Host)xo;
SetAppliesTo(Helpers.GetPoolOfOne(host.Connection));
AppliesTo.Add(host.opaque_ref);
}
else if (xo is VM)
{
VM vm = (VM)xo;
SetAppliesTo(vm.Home());
AppliesTo.Add(vm.opaque_ref);
}
else if (xo is SR)
{
SR sr = (SR)xo;
SetAppliesTo(sr.Home());
AppliesTo.Add(sr.opaque_ref);
}
else if (xo is VDI)
{
VDI vdi = (VDI)xo;
SetAppliesTo(vdi.Connection.Resolve(vdi.SR));
}
}
private string _description;
private bool _isCompleted = false;
private int _percentComplete = 0;
private Exception _exception = null;
#region Events
public event Action<ActionBase> Changed;
public event Action<ActionBase> Completed;
public static event Action<ActionBase> NewAction;
#endregion
public bool LogDescriptionChanges = true;
private bool showProgress = true;
public bool ShowProgress
{
get
{
return showProgress;
}
set
{
showProgress = value;
}
}
public ActionBase(string title, string description, bool suppressHistory)
: this(title, description, suppressHistory, false)
{
}
public ActionBase(string title, string description, bool suppressHistory, bool completeImmediately)
: this(title, description, suppressHistory, completeImmediately, null)
{}
public ActionBase(string title, string description, bool suppressHistory, bool completeImmediately, string error)
{
Title = title;
_description = description;
log.Debug(_description);
if (completeImmediately)
{
if (!string.IsNullOrEmpty(error))
_exception = new Exception(error);
Finished = DateTime.Now;
_percentComplete = 100;
_isCompleted = true;
}
if (NewAction != null && !suppressHistory)
NewAction(this);
}
public string Description
{
get { return _description; }
set
{
if (_description != value)
{
_description = value;
if (LogDescriptionChanges)
log.Debug(_description);
OnChanged();
}
}
}
public bool IsCompleted
{
get { return _isCompleted; }
set
{
if (_isCompleted != value)
{
_isCompleted = value;
OnChanged();
OnCompleted();
}
}
}
public virtual int PercentComplete
{
get { return _percentComplete; }
set
{
if (_percentComplete != value)
{
System.Diagnostics.Trace.Assert(0 <= value && value <= 100);
var percent = value;
if (percent < 0)
percent = 0;
else if (percent > 100)
percent = 100;
_percentComplete = percent;
OnChanged();
}
}
}
public bool SuppressProgressReport { get; set; }
public void Tick(int percent, string description)
{
if (_percentComplete != percent || _description != description)
{
_description = description;
if (percent < 0)
percent = 0;
else if (percent > 100)
percent = 100;
_percentComplete = percent;
OnChanged();
}
}
public bool Succeeded
{
get { return this.IsCompleted && this.Exception == null; }
}
public Exception Exception
{
get { return _exception; }
protected set
{
_exception = value;
OnChanged();
}
}
public void SetObject(IXenObject model)
{
if (model is Pool)
Pool = (Pool)model;
else if (model is Host)
Host = (Host)model;
else if (model is VM)
VM = (VM)model;
else if (model is SR)
SR = (SR)model;
else if (model is VDI)
{
VDI vdi = (VDI)model;
SetObject(vdi.Connection.Resolve(vdi.SR));
}
else if (model is VBD)
{
VBD vbd = (VBD)model;
SetObject(vbd.Connection.Resolve(vbd.VM));
}
}
public virtual bool CanCancel
{
get { return false; }
protected set { throw new InvalidOperationException(); }
}
public virtual void Cancel()
{
throw new InvalidOperationException();
}
protected void OnChanged()
{
if (Changed != null)
try
{
if (!SuppressProgressReport)
Changed(this);
}
catch (Exception e)
{
log.Debug(String.Format("Exception firing OnChanged for Action {0}", Title), e);
log.Debug(e, e);
}
}
protected virtual void OnCompleted()
{
if (Completed != null)
{
try
{
Completed(this);
}
catch (Exception ex)
{
log.Debug(String.Format("Exception firing OnCompleted for Action {0}", Title), ex);
log.Debug(ex, ex);
}
}
}
protected void MarkCompleted()
{
MarkCompletedCore();
}
protected void MarkCompleted(Exception e)
{
log.Debug(e, e);
Exception = e;
MarkCompletedCore();
}
private void MarkCompletedCore()
{
Finished = DateTime.Now;
PercentComplete = 100;
IsCompleted = true;
}
#region Audit logging
protected static readonly log4net.ILog AuditLog = log4net.LogManager.GetLogger("Audit");
protected virtual void AuditLogStarted()
{
AuditLog.InfoFormat("Operation started: {0}", AuditDescription());
}
protected virtual void AuditLogSuccess()
{
AuditLog.InfoFormat("Operation success: {0}", AuditDescription());
}
protected virtual void AuditLogFailure()
{
AuditLog.WarnFormat("Operation failure: {0}", AuditDescription());
}
protected virtual void AuditLogCancelled()
{
AuditLog.InfoFormat("Operation cancelled: {0}", AuditDescription());
}
protected virtual string AuditDescription()
{
return string.Format("{0}: {1}: {2}{3}", GetType().Name,
DescribeConnection(), DescribeObject(), Description);
}
protected virtual string DescribeConnection()
{
if(Connection == null)
return "Connection unknown";
return Helpers.GetName(Connection);
}
protected virtual string DescribeObject()
{
StringBuilder sb = new StringBuilder();
if (VM != null)
{
sb.Append(DescribeVM(VM));
}
if (Pool != null)
{
sb.Append(DescribePool(Pool));
}
if (Host != null)
{
sb.Append(DescribeHost(Host));
}
return sb.ToString();
}
protected virtual string DescribeVM(VM v)
{
return string.Format("VM {0} ({1}): ", v.uuid, v.Name());
}
protected virtual string DescribePool(Pool p)
{
return string.Format("Pool {0} ({1}): ", p.uuid, p.Name());
}
protected virtual string DescribeHost(Host h)
{
return string.Format("Host {0} ({1}): ", h.uuid, h.Name());
}
#endregion
}
}