ia64/xen-unstable

view tools/python/xen/xend/server/XMLRPCServer.py @ 18036:a762b4aed1a8

xend: Ignore errors from dying domains in RPC server

When a domain is in the process of shutting down there is a small
window when the domain is known to XenD, but it will be unable to form
an SXPR for it due it being in the middle of device hot-unplug. This
causes the 'xm list' command to totally fail with an error like

# xm list
Error: Device 0 not connected
Usage: xm list [options] [Domain, ...]

The 'xm list' command calls into the 'domains' method of
XMLRPCServer.py in XenD. This method just iterates over the list of
domains, fetching the sxpr for each in turn, but with no exception
handling. So if a single domain fails to generate an sxpr, no data is
returned even for other domains which are still functional. This
patch simply makes XenD ignore and skip over domains which throw an
exception, logging the problematic domain.

NB, this problem only hits 'xm list' if it is configured to use the
legay XMLRPC server instead of XenAPI.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jul 11 12:47:50 2008 +0100 (2008-07-11)
parents 5255eac35270
children 2ef77b4bdf58
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2006 Anthony Liguori <aliguori@us.ibm.com>
16 # Copyright (C) 2006 XenSource Ltd.
17 #============================================================================
19 import errno
20 import socket
21 import types
22 import xmlrpclib
23 from xen.util.xmlrpclib2 import UnixXMLRPCServer, TCPXMLRPCServer
24 try:
25 from SSLXMLRPCServer import SSLXMLRPCServer
26 ssl_enabled = True
27 except ImportError:
28 ssl_enabled = False
30 from xen.xend import XendAPI, XendDomain, XendDomainInfo, XendNode
31 from xen.xend import XendLogging, XendDmesg
32 from xen.xend.XendClient import XML_RPC_SOCKET
33 from xen.xend.XendConstants import DOM_STATE_RUNNING
34 from xen.xend.XendLogging import log
35 from xen.xend.XendError import XendInvalidDomain
37 # vcpu_avail is a long and is not needed by the clients. It's far easier
38 # to just remove it then to try and marshal the long.
39 def fixup_sxpr(sexpr):
40 ret = []
41 for k in sexpr:
42 if type(k) in (list, tuple):
43 if len(k) != 2 or k[0] != 'vcpu_avail':
44 ret.append(fixup_sxpr(k))
45 else:
46 ret.append(k)
47 return ret
49 def lookup(domid):
50 info = XendDomain.instance().domain_lookup(domid)
51 return info
53 def dispatch(domid, fn, args):
54 info = lookup(domid)
55 return getattr(info, fn)(*args)
57 def domain(domid, full = 0):
58 info = lookup(domid)
59 return fixup_sxpr(info.sxpr(not full))
61 def domains(detail = True, full = False):
62 return domains_with_state(detail, DOM_STATE_RUNNING, full)
64 def domains_with_state(detail, state, full):
65 if detail:
66 domains = XendDomain.instance().list_sorted(state)
67 ret = []
68 for dom in domains:
69 try:
70 ret.append(fixup_sxpr(dom.sxpr(not full)))
71 except:
72 log.warn("Failed to query SXPR for domain %s" % str(dom))
73 pass
74 return ret
75 else:
76 return XendDomain.instance().list_names(state)
78 def domain_create(config):
79 info = XendDomain.instance().domain_create(config)
80 return fixup_sxpr(info.sxpr())
82 def domain_restore(src, paused=False):
83 info = XendDomain.instance().domain_restore(src, paused)
84 return fixup_sxpr(info.sxpr())
86 def get_log():
87 f = open(XendLogging.getLogFilename(), 'r')
88 try:
89 return f.read()
90 finally:
91 f.close()
93 methods = ['device_create', 'device_configure',
94 'destroyDevice','getDeviceSxprs',
95 'setMemoryTarget', 'setName', 'setVCpuCount', 'shutdown',
96 'send_sysrq', 'getVCPUInfo', 'waitForDevices',
97 'getRestartCount', 'getBlockDeviceClass']
99 exclude = ['domain_create', 'domain_restore']
101 class XMLRPCServer:
102 def __init__(self, auth, use_xenapi, use_tcp = False,
103 ssl_key_file = None, ssl_cert_file = None,
104 host = "localhost", port = 8006, path = XML_RPC_SOCKET,
105 hosts_allowed = None):
107 self.use_tcp = use_tcp
108 self.port = port
109 self.host = host
110 self.path = path
111 self.hosts_allowed = hosts_allowed
113 self.ssl_key_file = ssl_key_file
114 self.ssl_cert_file = ssl_cert_file
116 self.ready = False
117 self.running = True
118 self.auth = auth
119 self.xenapi = use_xenapi and XendAPI.XendAPI(auth) or None
121 def run(self):
122 authmsg = (self.auth == XendAPI.AUTH_NONE and
123 "; authentication has been disabled for this server." or
124 ".")
126 try:
127 if self.use_tcp:
128 using_ssl = self.ssl_key_file and self.ssl_cert_file
130 log.info("Opening %s XML-RPC server on %s%d%s",
131 using_ssl and 'HTTPS' or 'TCP',
132 self.host and '%s:' % self.host or
133 'all interfaces, port ',
134 self.port, authmsg)
136 if using_ssl:
137 if not ssl_enabled:
138 raise ValueError("pyOpenSSL not installed. "
139 "Unable to start HTTPS XML-RPC server")
140 self.server = SSLXMLRPCServer(
141 (self.host, self.port),
142 self.hosts_allowed,
143 self.xenapi is not None,
144 logRequests = False,
145 ssl_key_file = self.ssl_key_file,
146 ssl_cert_file = self.ssl_cert_file)
147 else:
148 self.server = TCPXMLRPCServer(
149 (self.host, self.port),
150 self.hosts_allowed,
151 self.xenapi is not None,
152 logRequests = False)
154 else:
155 log.info("Opening Unix domain socket XML-RPC server on %s%s",
156 self.path, authmsg)
157 self.server = UnixXMLRPCServer(self.path, self.hosts_allowed,
158 self.xenapi is not None,
159 logRequests = False)
160 except socket.error, exn:
161 log.error('Cannot start server: %s!', exn.args[1])
162 ready = True
163 running = False
164 return
165 except Exception, e:
166 log.exception('Cannot start server: %s!', e)
167 ready = True
168 running = False
169 return
171 # Register Xen API Functions
172 # -------------------------------------------------------------------
173 # exportable functions are ones that do not begin with '_'
174 # and has the 'api' attribute.
176 for meth_name in dir(self.xenapi):
177 if meth_name[0] != '_':
178 meth = getattr(self.xenapi, meth_name)
179 if callable(meth) and hasattr(meth, 'api'):
180 self.server.register_function(meth, getattr(meth, 'api'))
182 self.server.register_instance(XendAPI.XendAPIAsyncProxy(self.xenapi))
184 # Legacy deprecated xm xmlrpc api
185 # --------------------------------------------------------------------
187 # Functions in XendDomainInfo
188 for name in methods:
189 fn = eval("lambda domid, *args: dispatch(domid, '%s', args)"%name)
190 self.server.register_function(fn, "xend.domain.%s" % name)
192 inst = XendDomain.instance()
194 for name in dir(inst):
195 fn = getattr(inst, name)
196 if name.startswith("domain_") and callable(fn):
197 if name not in exclude:
198 self.server.register_function(fn, "xend.domain.%s" % name[7:])
200 # Functions in XendNode and XendDmesg
201 for type, lst, n in [(XendNode, ['info', 'send_debug_keys'], 'node'),
202 (XendDmesg, ['info', 'clear'], 'node.dmesg')]:
203 inst = type.instance()
204 for name in lst:
205 self.server.register_function(getattr(inst, name),
206 "xend.%s.%s" % (n, name))
208 # A few special cases
209 self.server.register_function(domain, 'xend.domain')
210 self.server.register_function(domains, 'xend.domains')
211 self.server.register_function(domains_with_state,
212 'xend.domains_with_state')
213 self.server.register_function(get_log, 'xend.node.log')
214 self.server.register_function(domain_create, 'xend.domain.create')
215 self.server.register_function(domain_restore, 'xend.domain.restore')
217 # A couple of the security functions
218 from xen.util.xsm import xsm as security
219 for name in security.xmlrpc_exports:
220 fn = getattr(security, name)
221 self.server.register_function(fn, "xend.security.%s" % name)
223 self.server.register_introspection_functions()
224 self.ready = True
226 # Custom runloop so we can cleanup when exiting.
227 # -----------------------------------------------------------------
228 try:
229 while self.running:
230 self.server.handle_request()
231 finally:
232 self.shutdown()
234 def cleanup(self):
235 log.debug('XMLRPCServer.cleanup()')
236 if hasattr(self, 'server'):
237 try:
238 # This is here to make sure the socket is actually
239 # cleaned up when close() is called. Otherwise
240 # SO_REUSEADDR doesn't take effect. To replicate,
241 # try 'xend reload' and look for EADDRINUSE.
242 #
243 # May be caued by us calling close() outside of
244 # the listen()ing thread.
245 self.server.socket.shutdown(2)
246 except socket.error, e:
247 pass # ignore any socket errors
248 try:
249 self.server.socket.close()
250 except socket.error, e:
251 pass
253 def shutdown(self):
254 self.running = False
255 if self.ready:
256 self.ready = False
257 self.cleanup()