2013-06-24 13:41:48 +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:
|
|
|
|
*
|
|
|
|
* 1) Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* 2) 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;
|
2020-03-04 13:47:22 +01:00
|
|
|
using System.Linq;
|
2013-06-24 13:41:48 +02:00
|
|
|
using System.Resources;
|
2020-03-04 13:47:22 +01:00
|
|
|
using System.Runtime.Serialization;
|
2013-06-24 13:41:48 +02:00
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
using System.Xml;
|
2020-03-04 13:47:22 +01:00
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
namespace XenAPI
|
|
|
|
{
|
2014-07-03 11:00:05 +02:00
|
|
|
[Serializable]
|
2013-06-24 13:41:48 +02:00
|
|
|
public partial class Failure : Exception
|
|
|
|
{
|
|
|
|
public const string INTERNAL_ERROR = "INTERNAL_ERROR";
|
|
|
|
public const string MESSAGE_PARAMETER_COUNT_MISMATCH = "MESSAGE_PARAMETER_COUNT_MISMATCH";
|
|
|
|
|
2020-03-04 13:47:22 +01:00
|
|
|
private static ResourceManager errorDescriptions = FriendlyErrorNames.ResourceManager;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
2020-05-05 16:42:38 +02:00
|
|
|
private readonly List<string> errorDescription = new List<string>();
|
2013-06-24 13:41:48 +02:00
|
|
|
private string errorText;
|
|
|
|
private string shortError;
|
|
|
|
|
2020-05-28 14:58:59 +02:00
|
|
|
public List<string> ErrorDescription
|
2013-06-24 13:41:48 +02:00
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
get { return errorDescription; }
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public string ShortMessage
|
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
get { return shortError; }
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public override string Message
|
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
get { return errorText; }
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
|
|
|
|
2020-03-04 13:47:22 +01:00
|
|
|
#region Constructors
|
|
|
|
|
2020-05-05 16:42:38 +02:00
|
|
|
public Failure()
|
|
|
|
{}
|
2014-07-03 11:00:05 +02:00
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
public Failure(params string[] err)
|
|
|
|
: this(new List<string>(err))
|
|
|
|
{}
|
|
|
|
|
2020-03-04 13:47:22 +01:00
|
|
|
public Failure(List<string> errDescription)
|
|
|
|
{
|
|
|
|
errorDescription = errDescription;
|
|
|
|
ParseExceptionMessage();
|
|
|
|
}
|
|
|
|
|
2014-07-03 11:00:05 +02:00
|
|
|
public Failure(string message, Exception exception)
|
|
|
|
: base(message, exception)
|
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
errorDescription = new List<string> {message};
|
|
|
|
ParseExceptionMessage();
|
2014-07-03 11:00:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected Failure(SerializationInfo info, StreamingContext context)
|
|
|
|
: base(info, context)
|
|
|
|
{
|
|
|
|
errorDescription = (List<string>)info.GetValue("errorDescription", typeof(List<string>));
|
|
|
|
errorText = info.GetString("errorText");
|
|
|
|
shortError = info.GetString("shortError");
|
|
|
|
}
|
|
|
|
|
2020-03-04 13:47:22 +01:00
|
|
|
#endregion
|
2013-06-24 13:41:48 +02:00
|
|
|
|
2020-03-04 13:47:22 +01:00
|
|
|
private void ParseExceptionMessage()
|
2013-06-24 13:41:48 +02:00
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
if (ErrorDescription.Count <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
try
|
2013-06-24 13:41:48 +02:00
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
string formatString;
|
2013-06-24 13:41:48 +02:00
|
|
|
try
|
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
formatString = errorDescriptions.GetString(ErrorDescription[0]);
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
2020-03-04 13:47:22 +01:00
|
|
|
catch
|
2013-06-24 13:41:48 +02:00
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
formatString = null;
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
|
|
|
|
2020-03-04 13:47:22 +01:00
|
|
|
if (formatString == null)
|
2013-06-24 13:41:48 +02:00
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
// If we don't have a translation, just combine all the error results from the server
|
|
|
|
// Only show non-empty bits of ErrorDescription.
|
|
|
|
// Also, trim the bits because the server occasionally sends spurious newlines.
|
|
|
|
|
|
|
|
var cleanBits = (from string s in ErrorDescription
|
|
|
|
let trimmed = s.Trim()
|
|
|
|
where trimmed.Length > 0
|
|
|
|
select trimmed).ToArray();
|
|
|
|
|
|
|
|
errorText = string.Join(" - ", cleanBits);
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
2020-03-04 13:47:22 +01:00
|
|
|
else
|
2013-06-24 13:41:48 +02:00
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
// We need a string array to pass to String.Format, and it must not contain the 0th element
|
2020-05-09 02:53:47 +02:00
|
|
|
errorText = string.Format(formatString, ErrorDescription.Skip(1).Cast<object>().ToArray());
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
2020-03-04 13:47:22 +01:00
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
errorText = ErrorDescription[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
//call these before setting the shortError because they modify the errorText
|
|
|
|
ParseSmapiV3Failures();
|
|
|
|
ParseCslgFailures();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
2020-03-04 13:47:22 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
shortError = errorDescriptions.GetString(ErrorDescription[0] + "-SHORT") ?? errorText;
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
shortError = errorText;
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-04 13:47:22 +01:00
|
|
|
/// The ErrorDescription[2] of SmapiV3 failures contains embedded json.
|
|
|
|
/// This method parses it and copies the user friendly part to errorText.
|
2013-06-24 13:41:48 +02:00
|
|
|
/// </summary>
|
2020-03-04 13:47:22 +01:00
|
|
|
private void ParseSmapiV3Failures()
|
2013-06-24 13:41:48 +02:00
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
/* Example ErrorDescription:
|
|
|
|
* [
|
|
|
|
* "SR_BACKEND_FAILURE",
|
|
|
|
* "TransportException",
|
|
|
|
* "{\"error\": \"Unable to connect to iSCSI service on target\"}"
|
|
|
|
* ]
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ErrorDescription.Count < 3 || string.IsNullOrEmpty(ErrorDescription[0]) || !ErrorDescription[0].StartsWith("SR_BACKEND_FAILURE"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var obj = JObject.Parse(ErrorDescription[2]); //will throw exception if ErrorDescription[2] is a simple string
|
|
|
|
errorText = (string)obj.SelectToken("error") ?? errorText;
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
//ignore
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The ErrorDescription[2] of Cslg failures contains embedded xml.
|
|
|
|
/// This method parses it and copies the user friendly part to errorText.
|
|
|
|
/// </summary>
|
|
|
|
private void ParseCslgFailures()
|
|
|
|
{
|
|
|
|
/* ErrorDescription[2] example:
|
|
|
|
|
|
|
|
<StorageLinkServiceError>
|
|
|
|
<Fault>Host ivory has not yet been added to the service. [err=Object was not found]</Fault>
|
|
|
|
<Detail>
|
|
|
|
<errorCode>6</errorCode>
|
|
|
|
<messageId></messageId>
|
|
|
|
<defaultMessage>Host ivory has not yet been added to the service. [err=Object was not found]</defaultMessage>
|
|
|
|
<severity>2</severity>
|
|
|
|
<errorFunction>CXSSHostUtil::getHost</errorFunction>
|
|
|
|
<errorLine>113</errorLine>
|
|
|
|
<errorFile>.\\xss_util_host.cpp</errorFile>
|
|
|
|
</Detail>
|
|
|
|
</StorageLinkServiceError>
|
|
|
|
*/
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
if (ErrorDescription.Count > 2 && ErrorDescription[2] != null && ErrorDescription[0] != null && ErrorDescription[0].StartsWith("SR_BACKEND_FAILURE"))
|
|
|
|
{
|
|
|
|
Match m = Regex.Match(ErrorDescription[2], @"<StorageLinkServiceError>.*</StorageLinkServiceError>", RegexOptions.Singleline);
|
|
|
|
|
|
|
|
if (m.Success)
|
|
|
|
{
|
|
|
|
XmlDocument doc = new XmlDocument();
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
doc.LoadXml(m.Value);
|
|
|
|
}
|
|
|
|
catch (XmlException)
|
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
return;
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
XmlNodeList nodes = doc.SelectNodes("/StorageLinkServiceError/Fault");
|
|
|
|
|
|
|
|
if (nodes != null && nodes.Count > 0 && !string.IsNullOrEmpty(nodes[0].InnerText))
|
|
|
|
{
|
2020-03-04 13:47:22 +01:00
|
|
|
errorText = string.IsNullOrEmpty(errorText)
|
|
|
|
? nodes[0].InnerText
|
|
|
|
: string.Format("{0} ({1})", errorText, nodes[0].InnerText);
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-04 13:47:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[Obsolete("This method is used internally and should not be called directly from the implementing code. "+
|
|
|
|
"If you need to modify this instance's fields, construct a new instance instead.")]
|
|
|
|
public void Setup()
|
|
|
|
{
|
|
|
|
ParseExceptionMessage();
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
{
|
|
|
|
return Message;
|
|
|
|
}
|
2014-07-03 11:00:05 +02:00
|
|
|
|
|
|
|
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
|
|
|
{
|
|
|
|
if (info == null)
|
|
|
|
throw new ArgumentNullException("info");
|
|
|
|
|
|
|
|
info.AddValue("errorDescription", errorDescription, typeof(List<string>));
|
|
|
|
info.AddValue("errorText", errorText);
|
|
|
|
info.AddValue("shortError", shortError);
|
|
|
|
|
|
|
|
base.GetObjectData(info, context);
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
}
|
2020-05-28 14:58:59 +02:00
|
|
|
}
|