# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. import gettext import xmlrpclib import httplib import socket translation = gettext.translation('xen-xm', fallback = True) API_VERSION_1_1 = '1.1' API_VERSION_1_2 = '1.2' class Failure(Exception): def __init__(self, details): self.details = details def __str__(self): try: return str(self.details) except Exception, exn: import sys print >>sys.stderr, exn return "Xen-API failure: %s" % str(self.details) def _details_map(self): return dict([(str(i), self.details[i]) for i in range(len(self.details))]) _RECONNECT_AND_RETRY = (lambda _ : ()) class UDSHTTPConnection(httplib.HTTPConnection): """HTTPConnection subclass to allow HTTP over Unix domain sockets. """ def connect(self): path = self.host.replace("_", "/") self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.connect(path) class UDSHTTP(httplib.HTTP): _connection_class = UDSHTTPConnection class UDSTransport(xmlrpclib.Transport): def __init__(self, use_datetime=0): self._use_datetime = use_datetime self._extra_headers=[] def add_extra_header(self, key, value): self._extra_headers += [ (key,value) ] def make_connection(self, host): return UDSHTTP(host) def send_request(self, connection, handler, request_body): connection.putrequest("POST", handler) for key, value in self._extra_headers: connection.putheader(key, value) class Session(xmlrpclib.ServerProxy): """A server proxy and session manager for communicating with xapi using the Xen-API. Example: session = Session('http://localhost/') session.login_with_password('me', 'mypassword') session.xenapi.VM.start(vm_uuid) session.xenapi.session.logout() """ def __init__(self, uri, transport=None, encoding=None, verbose=0, allow_none=1): xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, verbose, allow_none) self.transport = transport self._session = None self.last_login_method = None self.last_login_params = None self.API_version = API_VERSION_1_1 def xenapi_request(self, methodname, params): if methodname.startswith('login'): self._login(methodname, params) return None elif methodname == 'logout' or methodname == 'session.logout': self._logout() return None else: retry_count = 0 while retry_count < 3: full_params = (self._session,) + params result = _parse_result(getattr(self, methodname)(*full_params)) if result == _RECONNECT_AND_RETRY: retry_count += 1 if self.last_login_method: self._login(self.last_login_method, self.last_login_params) else: raise xmlrpclib.Fault(401, 'You must log in') else: return result raise xmlrpclib.Fault( 500, 'Tried 3 times to get a valid session, but failed') def _login(self, method, params): result = _parse_result(getattr(self, 'session.%s' % method)(*params)) if result == _RECONNECT_AND_RETRY: raise xmlrpclib.Fault( 500, 'Received SESSION_INVALID when logging in') self._session = result self.last_login_method = method self.last_login_params = params self.API_version = self._get_api_version() def _logout(self): try: if self.last_login_method.startswith("slave_local"): return _parse_result(self.session.local_logout(self._session)) else: return _parse_result(self.session.logout(self._session)) finally: self._session = None self.last_login_method = None self.last_login_params = None self.API_version = API_VERSION_1_1 def _get_api_version(self): pool = self.xenapi.pool.get_all()[0] host = self.xenapi.pool.get_master(pool) major = self.xenapi.host.get_API_version_major(host) minor = self.xenapi.host.get_API_version_minor(host) return "%s.%s"%(major,minor) def __getattr__(self, name): if name == 'handle': return self._session elif name == 'xenapi': return _Dispatcher(self.API_version, self.xenapi_request, None) elif name.startswith('login') or name.startswith('slave_local'): return lambda *params: self._login(name, params) else: return xmlrpclib.ServerProxy.__getattr__(self, name) def xapi_local(): return Session("http://_var_xapi_xapi/", transport=UDSTransport()) def _parse_result(result): if type(result) != dict or 'Status' not in result: raise xmlrpclib.Fault(500, 'Missing Status in response from server' + result) if result['Status'] == 'Success': if 'Value' in result: return result['Value'] else: raise xmlrpclib.Fault(500, 'Missing Value in response from server') else: if 'ErrorDescription' in result: if result['ErrorDescription'][0] == 'SESSION_INVALID': return _RECONNECT_AND_RETRY else: raise Failure(result['ErrorDescription']) else: raise xmlrpclib.Fault( 500, 'Missing ErrorDescription in response from server') # Based upon _Method from xmlrpclib. class _Dispatcher: def __init__(self, API_version, send, name): self.__API_version = API_version self.__send = send self.__name = name def __repr__(self): if self.__name: return '' % self.__name else: return '' def __getattr__(self, name): if self.__name is None: return _Dispatcher(self.__API_version, self.__send, name) else: return _Dispatcher(self.__API_version, self.__send, "%s.%s" % (self.__name, name)) def __call__(self, *args): return self.__send(self.__name, args)