3030
3131using System ;
3232using System . Collections . Generic ;
33- using System . Text ;
33+ using System . Linq ;
3434using System . Resources ;
35- using System . Collections ;
35+ using System . Runtime . Serialization ;
3636using System . Text . RegularExpressions ;
3737using System . Xml ;
38- using System . Runtime . Serialization ;
38+ using Newtonsoft . Json . Linq ;
39+
3940
4041namespace XenAPI
4142{
@@ -45,47 +46,46 @@ public partial class Failure : Exception
4546 public const string INTERNAL_ERROR = "INTERNAL_ERROR" ;
4647 public const string MESSAGE_PARAMETER_COUNT_MISMATCH = "MESSAGE_PARAMETER_COUNT_MISMATCH" ;
4748
48- private static ResourceManager errorDescriptions = XenAPI . FriendlyErrorNames . ResourceManager ;
49+ private static ResourceManager errorDescriptions = FriendlyErrorNames . ResourceManager ;
4950
5051 private readonly List < string > errorDescription ;
5152 private string errorText ;
5253 private string shortError ;
5354
54- public List < string > ErrorDescription
55+ public List < string > ErrorDescription
5556 {
56- get
57- {
58- return errorDescription ;
59- }
57+ get { return errorDescription ; }
6058 }
6159
6260 public string ShortMessage
6361 {
64- get
65- {
66- return shortError ;
67- }
62+ get { return shortError ; }
6863 }
6964
7065 public override string Message
7166 {
72- get
73- {
74- return errorText ;
75- }
67+ get { return errorText ; }
7668 }
7769
70+ #region Constructors
71+
7872 public Failure ( ) : base ( ) { }
7973
8074 public Failure ( params string [ ] err )
8175 : this ( new List < string > ( err ) )
8276 { }
8377
78+ public Failure ( List < string > errDescription )
79+ {
80+ errorDescription = errDescription ;
81+ ParseExceptionMessage ( ) ;
82+ }
83+
8484 public Failure ( string message , Exception exception )
8585 : base ( message , exception )
8686 {
87- errorDescription = new List < string > ( ) { message } ;
88- Setup ( ) ;
87+ errorDescription = new List < string > { message } ;
88+ ParseExceptionMessage ( ) ;
8989 }
9090
9191 protected Failure ( SerializationInfo info , StreamingContext context )
@@ -96,95 +96,112 @@ protected Failure(SerializationInfo info, StreamingContext context)
9696 shortError = info . GetString ( "shortError" ) ;
9797 }
9898
99- public Failure ( List < string > errDescription )
100- {
101- errorDescription = errDescription ;
102- Setup ( ) ;
103- }
99+ #endregion
104100
105- public void Setup ( )
101+ private void ParseExceptionMessage ( )
106102 {
107- if ( ErrorDescription . Count > 0 )
103+ if ( ErrorDescription . Count <= 0 )
104+ return ;
105+
106+ try
108107 {
108+ string formatString ;
109109 try
110110 {
111- string formatString ;
112- try
113- {
114- formatString = errorDescriptions . GetString ( ErrorDescription [ 0 ] ) ;
115- }
116- catch
117- {
118- formatString = null ;
119- }
120-
121- if ( formatString == null )
122- {
123- // If we don't have a translation, just combine all the error results from the server
124- List < string > cleanBits = new List < string > ( ) ;
125- foreach ( string s in ErrorDescription )
126- {
127- // Only show non-empty bits of ErrorDescription.
128- // Also, trim the bits, since the server occasionally sends spurious newlines.
129- if ( s . Trim ( ) . Length > 0 )
130- {
131- cleanBits . Add ( s . Trim ( ) ) ;
132- }
133- }
134-
135- this . errorText = string . Join ( " - " , cleanBits . ToArray ( ) ) ;
136- }
137- else
138- {
139-
140- // We need a string array to pass to String.Format, and it must not contain the 0th element.
141-
142- string [ ] objects = new string [ ErrorDescription . Count - 1 ] ;
143-
144- for ( int i = 1 ; i < ErrorDescription . Count ; i ++ )
145- objects [ i - 1 ] = ErrorDescription [ i ] ;
146-
147- this . errorText = String . Format ( formatString , objects ) ;
148- }
111+ formatString = errorDescriptions . GetString ( ErrorDescription [ 0 ] ) ;
149112 }
150- catch ( Exception )
113+ catch
151114 {
152- this . errorText = ErrorDescription [ 0 ] ;
115+ formatString = null ;
153116 }
154117
155- try
118+ if ( formatString == null )
156119 {
157- shortError = errorDescriptions . GetString ( ErrorDescription [ 0 ] + "-SHORT" ) ?? errorText ;
120+ // If we don't have a translation, just combine all the error results from the server
121+ // Only show non-empty bits of ErrorDescription.
122+ // Also, trim the bits because the server occasionally sends spurious newlines.
123+
124+ var cleanBits = ( from string s in ErrorDescription
125+ let trimmed = s . Trim ( )
126+ where trimmed . Length > 0
127+ select trimmed ) . ToArray ( ) ;
128+
129+ errorText = string . Join ( " - " , cleanBits ) ;
158130 }
159- catch ( Exception )
131+ else
160132 {
161- shortError = this . errorText ;
133+ // We need a string array to pass to String.Format, and it must not contain the 0th element
134+ errorText = string . Format ( formatString , ErrorDescription . Skip ( 1 ) ) ;
162135 }
136+ }
137+ catch ( Exception )
138+ {
139+ errorText = ErrorDescription [ 0 ] ;
140+ }
141+
142+ //call these before setting the shortError because they modify the errorText
143+ ParseSmapiV3Failures ( ) ;
144+ ParseCslgFailures ( ) ;
163145
164- // now try and parse CSLG failures (these have embedded xml)
165- TryParseCslg ( ) ;
146+ try
147+ {
148+ shortError = errorDescriptions . GetString ( ErrorDescription [ 0 ] + "-SHORT" ) ?? errorText ;
149+ }
150+ catch ( Exception )
151+ {
152+ shortError = errorText ;
166153 }
167154 }
168155
169156 /// <summary>
170- /// Tries the parse CSLG failures. These have embedded xml. The useful part (from the user's perspective) is copied to errorText.
157+ /// The ErrorDescription[2] of SmapiV3 failures contains embedded json.
158+ /// This method parses it and copies the user friendly part to errorText.
171159 /// </summary>
172- /// <returns>A value specifying whether a CSLG error was found.</returns>
173- private bool TryParseCslg ( )
160+ private void ParseSmapiV3Failures ( )
174161 {
175- //failure.ErrorDescription[2]:
176- //<StorageLinkServiceError>
177- // <Fault>Host ivory has not yet been added to the service. [err=Object was not found]</Fault>
178- // <Detail>
179- // <errorCode>6</errorCode>
180- // <messageId></messageId>
181- // <defaultMessage>Host ivory has not yet been added to the service. [err=Object was not found]</defaultMessage>
182- // <severity>2</severity>
183- // <errorFunction>CXSSHostUtil::getHost</errorFunction>
184- // <errorLine>113</errorLine>
185- // <errorFile>.\\xss_util_host.cpp</errorFile>
186- // </Detail>
187- // </StorageLinkServiceError>
162+ /* Example ErrorDescription:
163+ * [
164+ * "SR_BACKEND_FAILURE",
165+ * "TransportException",
166+ * "{\"error\": \"Unable to connect to iSCSI service on target\"}"
167+ * ]
168+ */
169+
170+ if ( ErrorDescription . Count < 3 || string . IsNullOrEmpty ( ErrorDescription [ 0 ] ) || ! ErrorDescription [ 0 ] . StartsWith ( "SR_BACKEND_FAILURE" ) )
171+ return ;
172+
173+ try
174+ {
175+ var obj = JObject . Parse ( ErrorDescription [ 2 ] ) ; //will throw exception if ErrorDescription[2] is a simple string
176+ errorText = ( string ) obj . SelectToken ( "error" ) ?? errorText ;
177+ }
178+ catch
179+ {
180+ //ignore
181+ }
182+ }
183+
184+ /// <summary>
185+ /// The ErrorDescription[2] of Cslg failures contains embedded xml.
186+ /// This method parses it and copies the user friendly part to errorText.
187+ /// </summary>
188+ private void ParseCslgFailures ( )
189+ {
190+ /* ErrorDescription[2] example:
191+
192+ <StorageLinkServiceError>
193+ <Fault>Host ivory has not yet been added to the service. [err=Object was not found]</Fault>
194+ <Detail>
195+ <errorCode>6</errorCode>
196+ <messageId></messageId>
197+ <defaultMessage>Host ivory has not yet been added to the service. [err=Object was not found]</defaultMessage>
198+ <severity>2</severity>
199+ <errorFunction>CXSSHostUtil::getHost</errorFunction>
200+ <errorLine>113</errorLine>
201+ <errorFile>.\\xss_util_host.cpp</errorFile>
202+ </Detail>
203+ </StorageLinkServiceError>
204+ */
188205
189206 if ( ErrorDescription . Count > 2 && ErrorDescription [ 2 ] != null && ErrorDescription [ 0 ] != null && ErrorDescription [ 0 ] . StartsWith ( "SR_BACKEND_FAILURE" ) )
190207 {
@@ -200,26 +217,26 @@ private bool TryParseCslg()
200217 }
201218 catch ( XmlException )
202219 {
203- return false ;
220+ return ;
204221 }
205222
206223 XmlNodeList nodes = doc . SelectNodes ( "/StorageLinkServiceError/Fault" ) ;
207224
208225 if ( nodes != null && nodes . Count > 0 && ! string . IsNullOrEmpty ( nodes [ 0 ] . InnerText ) )
209226 {
210- if ( string . IsNullOrEmpty ( errorText ) )
211- {
212- errorText = nodes [ 0 ] . InnerText ;
213- }
214- else
215- {
216- errorText = string . Format ( "{0} ({1})" , errorText , nodes [ 0 ] . InnerText ) ;
217- }
218- return true ;
227+ errorText = string . IsNullOrEmpty ( errorText )
228+ ? nodes [ 0 ] . InnerText
229+ : string . Format ( "{0} ({1})" , errorText , nodes [ 0 ] . InnerText ) ;
219230 }
220231 }
221232 }
222- return false ;
233+ }
234+
235+ [ Obsolete ( "This method is used internally and should not be called directly from the implementing code. " +
236+ "If you need to modify this instance's fields, construct a new instance instead." ) ]
237+ public void Setup ( )
238+ {
239+ ParseExceptionMessage ( ) ;
223240 }
224241
225242 public override string ToString ( )
@@ -230,9 +247,7 @@ public override string ToString()
230247 public override void GetObjectData ( SerializationInfo info , StreamingContext context )
231248 {
232249 if ( info == null )
233- {
234250 throw new ArgumentNullException ( "info" ) ;
235- }
236251
237252 info . AddValue ( "errorDescription" , errorDescription , typeof ( List < string > ) ) ;
238253 info . AddValue ( "errorText" , errorText ) ;
@@ -241,4 +256,4 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont
241256 base . GetObjectData ( info , context ) ;
242257 }
243258 }
244- }
259+ }
0 commit comments