CA-180128: Make RelatedTask property setter aware of ExpectDisruption flag

When the pool's ssl_legacy settings are changed, we may temporarily lose connection. The action generally copes well, because PollToCompletion is aware of the ExpectDisruption flag.
But when the action's RelatedTask is assigned, we perform more api calls, which may fail with WebException. When disruption is expected, we need to ignore it and try again on a new session (CancellingAction.DoWithSessionRetry).
This commit is contained in:
Mihaela Stoica 2015-10-06 16:25:32 +01:00
parent 427ca8081e
commit 1405ec29f4
2 changed files with 112 additions and 97 deletions

View File

@ -113,7 +113,7 @@ namespace XenAdmin.Actions
/// using the basic credentials of the IXenConnection. Important - will throw exceptions similar to connection.NewSession /// using the basic credentials of the IXenConnection. Important - will throw exceptions similar to connection.NewSession
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public Session NewSession() public override Session NewSession()
{ {
if (Connection == null) if (Connection == null)
return null; return null;
@ -389,99 +389,6 @@ namespace XenAdmin.Actions
BestEffort(ref caught, Connection != null && Connection.ExpectDisruption, func); BestEffort(ref caught, Connection != null && Connection.ExpectDisruption, func);
} }
/// <summary>
/// Overload for use by actions, using any sudo credentials on the retry.
/// Try and run the delegate.
/// If it fails with a web exception or invalid session, try again.
/// Only retry 60 times.
/// </summary>
/// <param name="session"></param>
/// <param name="f"></param>
/// <param name="p"></param>
/// <returns></returns>
public object DoWithSessionRetry(ref Session session, Delegate f, params object[] p)
{
int retries = 60;
while (true)
{
try
{
object[] ps = new object[p.Length + 1];
ps[0] = session;
for (int i = 0; i < p.Length; i++)
{
ps[i + 1] = p[i];
}
try
{
return f.DynamicInvoke(ps);
}
catch (TargetInvocationException exn)
{
throw exn.InnerException;
}
}
catch (XmlRpcNullParameterException xmlExcept)
{
log.ErrorFormat("XmlRpcNullParameterException in DoWithSessionRetry, retry {0}", retries);
log.Error(xmlExcept, xmlExcept);
throw new Exception(Messages.INVALID_SESSION);
}
catch (WebException we)
{
log.ErrorFormat("WebException in DoWithSessionRetry, retry {0}", retries);
log.Error(we, we);
if (retries <= 0)
throw;
}
catch (Failure failure)
{
log.ErrorFormat("Failure in DoWithSessionRetry, retry {0}", retries);
log.Error(failure, failure);
if (retries <= 0)
throw;
if (failure.ErrorDescription.Count < 1 || failure.ErrorDescription[0] != XenAPI.Failure.SESSION_INVALID)
throw;
}
Session newSession;
try
{
// try to create a new TCP stream to use, as the other one has failed us
newSession = NewSession();
session = newSession;
}
catch (DisconnectionException e)
{
if (!Connection.ExpectDisruption)
{
//this was not expected, throw the d/c exception
throw e;
}
// We are expecting disruption on this connection. We need to wait for the hearbeat to recover.
// Though after 60 retries we will give up in the previous try catch block
}
catch
{
// do nothing
}
retries--;
Thread.Sleep(Connection.ExpectDisruption ? 500 : 100);
}
}
protected void AddCommonAPIMethodsToRoleCheck() protected void AddCommonAPIMethodsToRoleCheck()
{ {
ApiMethodsToRoleCheck.AddRange(Role.CommonTaskApiList); ApiMethodsToRoleCheck.AddRange(Role.CommonTaskApiList);

View File

@ -33,6 +33,10 @@ using System;
using System.Threading; using System.Threading;
using XenAdmin.Network; using XenAdmin.Network;
using XenAPI; using XenAPI;
using System.Collections.Generic;
using System.Net;
using CookComputing.XmlRpc;
using System.Reflection;
namespace XenAdmin.Actions namespace XenAdmin.Actions
{ {
@ -92,6 +96,11 @@ namespace XenAdmin.Actions
set { _session = value; } set { _session = value; }
} }
private delegate void SetXenCenterUUIDDelegate(Session session, string _task, string uuid);
private delegate void SetAppliesToDelegate(Session session, string _task, List<string> applies_to);
private delegate void SetMeddlingActionTitleDelegate(Session session, string task, string title);
/// <summary> /// <summary>
/// The XenAPI.Task object (if any) that corresponds to this action. /// The XenAPI.Task object (if any) that corresponds to this action.
/// </summary> /// </summary>
@ -104,9 +113,9 @@ namespace XenAdmin.Actions
_relatedTask = value; _relatedTask = value;
if (_relatedTask != null && _session != null) if (_relatedTask != null && _session != null)
{ {
Task.SetXenCenterUUID(_session, _relatedTask.opaque_ref, XenAdminConfigManager.Provider.XenCenterUUID); DoWithSessionRetry(ref _session, (SetXenCenterUUIDDelegate)Task.SetXenCenterUUID, _relatedTask.opaque_ref, XenAdminConfigManager.Provider.XenCenterUUID);
Task.SetAppliesTo(_session, _relatedTask.opaque_ref, AppliesTo); DoWithSessionRetry(ref _session, (SetAppliesToDelegate)Task.SetAppliesTo, _relatedTask.opaque_ref, AppliesTo);
Task.SetMeddlingActionTitle(_session, _relatedTask.opaque_ref, Title); DoWithSessionRetry(ref _session, (SetMeddlingActionTitleDelegate)Task.SetMeddlingActionTitle, _relatedTask.opaque_ref, Title);
RecomputeCanCancel(); RecomputeCanCancel();
} }
} }
@ -364,5 +373,104 @@ namespace XenAdmin.Actions
/// </summary> /// </summary>
protected virtual void Clean() { } protected virtual void Clean() { }
public virtual Session NewSession()
{
if (Connection == null)
return null;
return Connection.DuplicateSession();
}
/// <summary>
/// Overload for use by actions, using elevated credentials on the retry, if implemented in NewSession().
/// Try and run the delegate.
/// If it fails with a web exception or invalid session, try again.
/// Only retry 60 times.
/// </summary>
/// <param name="session"></param>
/// <param name="f"></param>
/// <param name="p"></param>
/// <returns></returns>
public object DoWithSessionRetry(ref Session session, Delegate f, params object[] p)
{
int retries = 60;
while (true)
{
try
{
object[] ps = new object[p.Length + 1];
ps[0] = session;
for (int i = 0; i < p.Length; i++)
{
ps[i + 1] = p[i];
}
try
{
return f.DynamicInvoke(ps);
}
catch (TargetInvocationException exn)
{
throw exn.InnerException;
}
}
catch (XmlRpcNullParameterException xmlExcept)
{
log.ErrorFormat("XmlRpcNullParameterException in DoWithSessionRetry, retry {0}", retries);
log.Error(xmlExcept, xmlExcept);
throw new Exception(Messages.INVALID_SESSION);
}
catch (WebException we)
{
log.ErrorFormat("WebException in DoWithSessionRetry, retry {0}", retries);
log.Error(we, we);
if (retries <= 0)
throw;
}
catch (Failure failure)
{
log.ErrorFormat("Failure in DoWithSessionRetry, retry {0}", retries);
log.Error(failure, failure);
if (retries <= 0)
throw;
if (failure.ErrorDescription.Count < 1 || failure.ErrorDescription[0] != XenAPI.Failure.SESSION_INVALID)
throw;
}
Session newSession;
try
{
// try to create a new TCP stream to use, as the other one has failed us
newSession = NewSession();
session = newSession;
}
catch (DisconnectionException e)
{
if (!Connection.ExpectDisruption)
{
//this was not expected, throw the d/c exception
throw e;
}
// We are expecting disruption on this connection. We need to wait for the hearbeat to recover.
// Though after 60 retries we will give up in the previous try catch block
}
catch
{
// do nothing
}
retries--;
Thread.Sleep(Connection.ExpectDisruption ? 500 : 100);
}
}
} }
} }