ia64/xen-unstable

view tools/python/xen/xm/XenAPI.py @ 17352:5dfdf95db75c

xm: Make xm's printout of security errors look better

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Mar 31 10:37:19 2008 +0100 (2008-03-31)
parents 66e935c09530
children
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-2007 XenSource Inc.
16 #============================================================================
17 #
18 # Parts of this file are based upon xmlrpclib.py, the XML-RPC client
19 # interface included in the Python distribution.
20 #
21 # Copyright (c) 1999-2002 by Secret Labs AB
22 # Copyright (c) 1999-2002 by Fredrik Lundh
23 #
24 # By obtaining, using, and/or copying this software and/or its
25 # associated documentation, you agree that you have read, understood,
26 # and will comply with the following terms and conditions:
27 #
28 # Permission to use, copy, modify, and distribute this software and
29 # its associated documentation for any purpose and without fee is
30 # hereby granted, provided that the above copyright notice appears in
31 # all copies, and that both that copyright notice and this permission
32 # notice appear in supporting documentation, and that the name of
33 # Secret Labs AB or the author not be used in advertising or publicity
34 # pertaining to distribution of the software without specific, written
35 # prior permission.
36 #
37 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
38 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
39 # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
40 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
41 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
43 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
44 # OF THIS SOFTWARE.
45 # --------------------------------------------------------------------
47 import gettext
48 import xmlrpclib
50 import xen.util.xmlrpcclient as xmlrpcclient
52 def gettext_noop(str):
53 return str
55 N_ = gettext_noop
57 errormap = {
58 "INTERNAL_ERROR": N_("Internal error: %(1)s."),
59 "MAP_DUPLICATE_KEY": N_("This map already contains %(1)s -> %(2)s."),
60 "MESSAGE_METHOD_UNKNOWN": N_("The method %(1)s is unsupported."),
61 "MESSAGE_PARAMETER_COUNT_MISMATCH": N_("The method %(1)s takes %(2)s argument(s) (%(3)s given)."),
62 "SESSION_AUTHENTICATION_FAILED": N_("Permission denied."),
63 "VALUE_NOT_SUPPORTED": N_("Value \"%(2)s\" for %(1)s is not supported by this server. The server said \"%(3)s\"."),
64 "HANDLE_INVALID": N_("The %(1)s handle %(2)s is invalid."),
65 "OPERATION_NOT_ALLOWED": N_("You attempted an operation that was not allowed."),
66 "NETWORK_ALREADY_CONNECTED": N_("The network you specified already has a PIF attached to it, and so another one may not be attached."),
67 "SECURITY_ERROR": N_("%(2)s"),
68 }
70 translation = gettext.translation('xen-xm', fallback = True)
72 class Failure(Exception):
73 def __init__(self, details):
74 try:
75 # If this failure is MESSAGE_PARAMETER_COUNT_MISMATCH, then we
76 # correct the return values here, to account for the fact that we
77 # transparently add the session handle as the first argument.
78 if details[0] == 'MESSAGE_PARAMETER_COUNT_MISMATCH':
79 details[2] = str(int(details[2]) - 1)
80 details[3] = str(int(details[3]) - 1)
82 self.details = details
83 except Exception, exn:
84 self.details = ['INTERNAL_ERROR', 'Client-side: ' + str(exn)]
86 def __str__(self):
87 try:
88 return translation.ugettext(errormap[self.details[0]]) % self._details_map()
89 except TypeError, exn:
90 return "Message database broken: %s.\nXen-API failure: %s" % \
91 (exn, str(self.details))
92 except Exception, exn:
93 import sys
94 print >>sys.stderr, exn
95 return "Xen-API failure: %s" % str(self.details)
97 def _details_map(self):
98 return dict([(str(i), self.details[i])
99 for i in range(len(self.details))])
102 _RECONNECT_AND_RETRY = (lambda _ : ())
105 class Session(xmlrpcclient.ServerProxy):
106 """A server proxy and session manager for communicating with Xend using
107 the Xen-API.
109 Example:
111 session = Session('http://localhost:9363/')
112 session.login_with_password('me', 'mypassword')
113 session.xenapi.VM.start(vm_uuid)
114 session.xenapi.session.logout()
116 For now, this class also supports the legacy XML-RPC API, using
117 session.xend.domain('Domain-0') and similar. This support will disappear
118 once there is a working Xen-API replacement for every call in the legacy
119 API.
120 """
122 def __init__(self, uri, transport=None, encoding=None, verbose=0,
123 allow_none=1):
124 xmlrpcclient.ServerProxy.__init__(self, uri, transport, encoding,
125 verbose, allow_none)
126 self._session = None
127 self.last_login_method = None
128 self.last_login_params = None
131 def getSession(self):
132 return self._session
134 def xenapi_request(self, methodname, params):
135 if methodname.startswith('login'):
136 self._login(methodname, params)
137 return None
138 else:
139 retry_count = 0
140 while retry_count < 3:
141 full_params = (self._session,) + params
142 result = _parse_result(getattr(self, methodname)(*full_params))
143 if result == _RECONNECT_AND_RETRY:
144 retry_count += 1
145 if self.last_login_method:
146 self._login(self.last_login_method,
147 self.last_login_params)
148 else:
149 raise xmlrpclib.Fault(401, 'You must log in')
150 else:
151 return result
152 raise xmlrpclib.Fault(
153 500, 'Tried 3 times to get a valid session, but failed')
156 def _login(self, method, params):
157 result = _parse_result(getattr(self, 'session.%s' % method)(*params))
158 if result == _RECONNECT_AND_RETRY:
159 raise xmlrpclib.Fault(
160 500, 'Received SESSION_INVALID when logging in')
161 self._session = result
162 self.last_login_method = method
163 self.last_login_params = params
166 def __getattr__(self, name):
167 if name == 'xenapi':
168 return _Dispatcher(self.xenapi_request, None)
169 elif name.startswith('login'):
170 return lambda *params: self._login(name, params)
171 else:
172 return xmlrpcclient.ServerProxy.__getattr__(self, name)
175 def _parse_result(result):
176 if type(result) != dict or 'Status' not in result:
177 raise xmlrpclib.Fault(500, 'Missing Status in response from server: ' + str(result))
178 if result['Status'] == 'Success':
179 if 'Value' in result:
180 return result['Value']
181 else:
182 raise xmlrpclib.Fault(500,
183 'Missing Value in response from server')
184 else:
185 if 'ErrorDescription' in result:
186 if result['ErrorDescription'][0] == 'SESSION_INVALID':
187 return _RECONNECT_AND_RETRY
188 else:
189 raise Failure(result['ErrorDescription'])
190 else:
191 raise xmlrpclib.Fault(
192 500, 'Missing ErrorDescription in response from server')
195 # Based upon _Method from xmlrpclib.
196 class _Dispatcher:
197 def __init__(self, send, name):
198 self.__send = send
199 self.__name = name
201 def __repr__(self):
202 if self.__name:
203 return '<XenAPI._Dispatcher for %s>' % self.__name
204 else:
205 return '<XenAPI._Dispatcher>'
207 def __getattr__(self, name):
208 if self.__name is None:
209 return _Dispatcher(self.__send, name)
210 else:
211 return _Dispatcher(self.__send, "%s.%s" % (self.__name, name))
213 def __call__(self, *args):
214 return self.__send(self.__name, args)