Skip to content

Commit 1981e5b

Browse files
committed
xenserver: test code to fix duplicated name in XMLRPC response
CustomerMapParser is copied from org.apache.xmlrpc.parser.MapParser:3.1.3 just removed ``` if (this.map.containsKey(this.nameObject)) { throw new SAXParseException("Duplicate name: " + this.nameObject, this.getDocumentLocator()); } ``` ConnectionNew is copied from com.xensource.xenapi.Connection:8.1.0
1 parent e68ff7e commit 1981e5b

File tree

2 files changed

+338
-3
lines changed

2 files changed

+338
-3
lines changed

plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/XenServerConnectionPool.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.cloud.utils.exception.CloudRuntimeException;
2222
import com.xensource.xenapi.APIVersion;
2323
import com.xensource.xenapi.Connection;
24+
import com.xensource.xenapi.ConnectionNew;
2425
import com.xensource.xenapi.Host;
2526
import com.xensource.xenapi.Pool;
2627
import com.xensource.xenapi.Session;
@@ -150,12 +151,12 @@ static void forceSleep(long sec) {
150151
}
151152

152153
public Connection getConnect(String ip, String username, Queue<String> password) {
153-
Connection conn = new Connection(getURL(ip), 10, _connWait);
154+
Connection conn = new ConnectionNew(getURL(ip), 10, _connWait);
154155
try {
155156
loginWithPassword(conn, username, password, APIVersion.latest().toString());
156157
} catch (Types.HostIsSlave e) {
157158
String maddress = e.masterIPAddress;
158-
conn = new Connection(getURL(maddress), 10, _connWait);
159+
conn = new ConnectionNew(getURL(maddress), 10, _connWait);
159160
try {
160161
loginWithPassword(conn, username, password, APIVersion.latest().toString());
161162
} catch (Exception e1) {
@@ -221,7 +222,7 @@ public Connection connect(String hostUuid, String poolUuid, String ipAddress,
221222

222223
if ( mConn == null ) {
223224
try {
224-
Connection conn = new Connection(getURL(ipAddress), 5, _connWait);
225+
Connection conn = new ConnectionNew(getURL(ipAddress), 5, _connWait);
225226
Session sess = loginWithPassword(conn, username, password, APIVersion.latest().toString());
226227
Host host = sess.getThisHost(conn);
227228
Boolean hostenabled = host.getEnabled(conn);
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package com.xensource.xenapi;
19+
20+
21+
import org.apache.ws.commons.util.NamespaceContextImpl;
22+
import org.apache.xmlrpc.XmlRpcException;
23+
import org.apache.xmlrpc.client.XmlRpcClient;
24+
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
25+
import org.apache.xmlrpc.client.XmlRpcHttpClientConfig;
26+
import org.apache.xmlrpc.common.TypeFactory;
27+
import org.apache.xmlrpc.common.TypeFactoryImpl;
28+
import org.apache.xmlrpc.common.XmlRpcStreamConfig;
29+
import org.apache.xmlrpc.parser.MapParser;
30+
import org.apache.xmlrpc.parser.TypeParser;
31+
import org.xml.sax.Attributes;
32+
import org.xml.sax.SAXException;
33+
import org.xml.sax.SAXParseException;
34+
35+
import javax.xml.namespace.QName;
36+
import java.net.URL;
37+
import java.util.HashMap;
38+
import java.util.Map;
39+
import java.util.TimeZone;
40+
41+
public class ConnectionNew extends Connection {
42+
/**
43+
* The version of the bindings that this class belongs to.
44+
*
45+
* @deprecated This field is not used any more.
46+
*/
47+
private APIVersion apiVersion;
48+
49+
50+
/**
51+
* Updated when Session.login_with_password() is called.
52+
*/
53+
@Override
54+
public APIVersion getAPIVersion()
55+
{
56+
return apiVersion;
57+
}
58+
59+
/**
60+
* The opaque reference to the session used by this connection
61+
*/
62+
private String sessionReference;
63+
64+
/**
65+
* As seen by the xmlrpc library. From our point of view it's a server.
66+
*/
67+
private final XmlRpcClient client;
68+
69+
/**
70+
* Creates a connection to a particular server using a given url. This object can then be passed
71+
* in to any other API calls.
72+
*
73+
* Note this constructor does NOT call Session.loginWithPassword; the programmer is responsible for calling it,
74+
* passing the Connection as a parameter. No attempt to connect to the server is made until login is called.
75+
*
76+
* When this constructor is used, a call to dispose() will do nothing. The programmer is responsible for manually
77+
* logging out the Session.
78+
*
79+
* @param url The URL of the server to connect to
80+
* @param replyTimeout The reply timeout for xml-rpc calls in seconds
81+
* @param connTimeout The connection timeout for xml-rpc calls in seconds
82+
*/
83+
public ConnectionNew(URL url, int replyTimeout, int connTimeout)
84+
{
85+
super(url, replyTimeout, connTimeout);
86+
this.client = getClientFromURL(url, replyTimeout, connTimeout);
87+
}
88+
89+
private XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
90+
91+
@Override
92+
public XmlRpcClientConfigImpl getConfig()
93+
{
94+
return config;
95+
}
96+
97+
static class CustomerMapParser extends MapParser {
98+
99+
private int level = 0;
100+
private StringBuffer nameBuffer = new StringBuffer();
101+
private Object nameObject;
102+
private Map map;
103+
private boolean inName;
104+
private boolean inValue;
105+
private boolean doneValue;
106+
107+
public CustomerMapParser(XmlRpcStreamConfig pConfig, NamespaceContextImpl pContext, TypeFactory pFactory) {
108+
super(pConfig, pContext, pFactory);
109+
}
110+
111+
protected void addResult(Object pResult) throws SAXException {
112+
if (this.inName) {
113+
this.nameObject = pResult;
114+
} else {
115+
if (this.nameObject == null) {
116+
throw new SAXParseException("Invalid state: Expected name", this.getDocumentLocator());
117+
}
118+
119+
this.map.put(this.nameObject, pResult);
120+
}
121+
122+
}
123+
124+
public void startDocument() throws SAXException {
125+
super.startDocument();
126+
this.level = 0;
127+
this.map = new HashMap();
128+
this.inValue = this.inName = false;
129+
}
130+
131+
public void characters(char[] pChars, int pOffset, int pLength) throws SAXException {
132+
if (this.inName && !this.inValue) {
133+
this.nameBuffer.append(pChars, pOffset, pLength);
134+
} else {
135+
super.characters(pChars, pOffset, pLength);
136+
}
137+
138+
}
139+
140+
public void ignorableWhitespace(char[] pChars, int pOffset, int pLength) throws SAXException {
141+
if (this.inName) {
142+
this.characters(pChars, pOffset, pLength);
143+
} else {
144+
super.ignorableWhitespace(pChars, pOffset, pLength);
145+
}
146+
147+
}
148+
149+
public void startElement(String pURI, String pLocalName, String pQName, Attributes pAttrs) throws SAXException {
150+
switch (this.level++) {
151+
case 0:
152+
if (!"".equals(pURI) || !"struct".equals(pLocalName)) {
153+
throw new SAXParseException("Expected struct, got " + new QName(pURI, pLocalName), this.getDocumentLocator());
154+
}
155+
break;
156+
case 1:
157+
if (!"".equals(pURI) || !"member".equals(pLocalName)) {
158+
throw new SAXParseException("Expected member, got " + new QName(pURI, pLocalName), this.getDocumentLocator());
159+
}
160+
161+
this.doneValue = this.inName = this.inValue = false;
162+
this.nameObject = null;
163+
this.nameBuffer.setLength(0);
164+
break;
165+
case 2:
166+
if (this.doneValue) {
167+
throw new SAXParseException("Expected /member, got " + new QName(pURI, pLocalName), this.getDocumentLocator());
168+
}
169+
170+
if ("".equals(pURI) && "name".equals(pLocalName)) {
171+
if (this.nameObject != null) {
172+
throw new SAXParseException("Expected value, got " + new QName(pURI, pLocalName), this.getDocumentLocator());
173+
}
174+
175+
this.inName = true;
176+
} else if ("".equals(pURI) && "value".equals(pLocalName)) {
177+
if (this.nameObject == null) {
178+
throw new SAXParseException("Expected name, got " + new QName(pURI, pLocalName), this.getDocumentLocator());
179+
}
180+
181+
this.inValue = true;
182+
this.startValueTag();
183+
}
184+
break;
185+
case 3:
186+
if (this.inName && "".equals(pURI) && "value".equals(pLocalName)) {
187+
if (!this.cfg.isEnabledForExtensions()) {
188+
throw new SAXParseException("Expected /name, got " + new QName(pURI, pLocalName), this.getDocumentLocator());
189+
}
190+
191+
this.inValue = true;
192+
this.startValueTag();
193+
} else {
194+
super.startElement(pURI, pLocalName, pQName, pAttrs);
195+
}
196+
break;
197+
default:
198+
super.startElement(pURI, pLocalName, pQName, pAttrs);
199+
}
200+
201+
}
202+
203+
public void endElement(String pURI, String pLocalName, String pQName) throws SAXException {
204+
switch (--this.level) {
205+
case 0:
206+
this.setResult(this.map);
207+
case 1:
208+
break;
209+
case 2:
210+
if (this.inName) {
211+
this.inName = false;
212+
if (this.nameObject == null) {
213+
this.nameObject = this.nameBuffer.toString();
214+
} else {
215+
for(int i = 0; i < this.nameBuffer.length(); ++i) {
216+
if (!Character.isWhitespace(this.nameBuffer.charAt(i))) {
217+
throw new SAXParseException("Unexpected non-whitespace character in member name", this.getDocumentLocator());
218+
}
219+
}
220+
}
221+
} else if (this.inValue) {
222+
this.endValueTag();
223+
this.doneValue = true;
224+
}
225+
break;
226+
case 3:
227+
if (this.inName && this.inValue && "".equals(pURI) && "value".equals(pLocalName)) {
228+
this.endValueTag();
229+
} else {
230+
super.endElement(pURI, pLocalName, pQName);
231+
}
232+
break;
233+
default:
234+
super.endElement(pURI, pLocalName, pQName);
235+
}
236+
237+
}
238+
}
239+
240+
private XmlRpcClient getClientFromURL(URL url, int replyWait, int connWait)
241+
{
242+
config.setTimeZone(TimeZone.getTimeZone("UTC"));
243+
config.setServerURL(url);
244+
config.setReplyTimeout(replyWait * 1000);
245+
config.setConnectionTimeout(connWait * 1000);
246+
XmlRpcClient client = new XmlRpcClient();
247+
client.setConfig(config);
248+
client.setTypeFactory(new TypeFactoryImpl(client) {
249+
@Override
250+
public TypeParser getParser(XmlRpcStreamConfig pConfig, NamespaceContextImpl pContext, String pURI, String pLocalName) {
251+
TypeParser parser = super.getParser(pConfig, pContext, pURI, pLocalName);
252+
if (parser instanceof MapParser) {
253+
return new CustomerMapParser(pConfig, pContext, this);
254+
}
255+
return parser;
256+
}
257+
});
258+
return client;
259+
}
260+
261+
@Override
262+
public String getSessionReference()
263+
{
264+
return this.sessionReference;
265+
}
266+
267+
@Override
268+
protected Map dispatch(String methodCall, Object[] methodParams) throws XmlRpcException, Types.XenAPIException
269+
{
270+
Map response = (Map) client.execute(methodCall, methodParams);
271+
272+
if (methodCall.equals("session.login_with_password") &&
273+
response.get("Status").equals("Success"))
274+
{
275+
Session session = Types.toSession(response.get("Value"));
276+
sessionReference = session.ref;
277+
setAPIVersion(session);
278+
}
279+
else if (methodCall.equals("session.slave_local_login_with_password") &&
280+
response.get("Status").equals("Success"))
281+
{
282+
sessionReference = Types.toSession(response.get("Value")).ref;
283+
apiVersion = APIVersion.latest();
284+
}
285+
else if (methodCall.equals("session.logout"))
286+
{
287+
// Work around a bug in XenServer 5.0 and below.
288+
// session.login_with_password should have rejected us with
289+
// HOST_IS_SLAVE, but instead we don't find out until later.
290+
// We don't want to leak the session, so we need to log out
291+
// this session from the master instead.
292+
if (response.get("Status").equals("Failure"))
293+
{
294+
Object[] error = (Object[]) response.get("ErrorDescription");
295+
if (error.length == 2 && error[0].equals("HOST_IS_SLAVE"))
296+
{
297+
try
298+
{
299+
XmlRpcHttpClientConfig clientConfig = (XmlRpcHttpClientConfig)client.getClientConfig();
300+
URL client_url = clientConfig.getServerURL();
301+
URL masterUrl = new URL(client_url.getProtocol(), (String)error[1], client_url.getPort(), client_url.getFile());
302+
303+
Connection tmp_conn = new Connection(masterUrl, sessionReference, clientConfig.getReplyTimeout(), clientConfig.getConnectionTimeout());
304+
305+
Session.logout(tmp_conn);
306+
}
307+
catch (Exception ex)
308+
{
309+
// Ignore
310+
}
311+
}
312+
}
313+
314+
this.sessionReference = null;
315+
}
316+
317+
return Types.checkResponse(response);
318+
}
319+
320+
321+
private void setAPIVersion(Session session) throws Types.XenAPIException, XmlRpcException
322+
{
323+
try
324+
{
325+
long major = session.getThisHost(this).getAPIVersionMajor(this);
326+
long minor = session.getThisHost(this).getAPIVersionMinor(this);
327+
apiVersion = APIVersion.fromMajorMinor(major, minor);
328+
}
329+
catch (Types.BadServerResponse exn)
330+
{
331+
apiVersion = APIVersion.UNKNOWN;
332+
}
333+
}
334+
}

0 commit comments

Comments
 (0)