diff options
| author | Armando Migliaccio <armando.migliaccio@citrix.com> | 2010-12-10 18:24:59 +0000 |
|---|---|---|
| committer | Armando Migliaccio <armando.migliaccio@citrix.com> | 2010-12-10 18:24:59 +0000 |
| commit | 83cd4057e358e597e0d1f13392e000cbb58f096a (patch) | |
| tree | 49ae85ea0ed4f1d9f71178292751a98b7a3e585a | |
| parent | 9420d99dc2fb75a8f6b518f55b6580027af582c3 (diff) | |
| parent | 1a759c3722610d720dd8dabf816db146c1063937 (diff) | |
* pylint fixes
* code clean-up
* first cut for xenapi unit tests
| -rw-r--r-- | Authors | 2 | ||||
| -rw-r--r-- | nova/api/ec2/cloud.py | 1 | ||||
| -rw-r--r-- | nova/api/openstack/__init__.py | 8 | ||||
| -rw-r--r-- | nova/compute/api.py | 17 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_adminapi.py | 61 | ||||
| -rw-r--r-- | nova/tests/virt_unittest.py | 46 | ||||
| -rw-r--r-- | nova/virt/xenapi/fake.py | 59 | ||||
| -rw-r--r-- | nova/virt/xenapi/vm_utils.py | 68 | ||||
| -rw-r--r-- | nova/virt/xenapi/vmops.py | 22 | ||||
| -rw-r--r-- | nova/virt/xenapi/volume_utils.py | 29 | ||||
| -rw-r--r-- | nova/virt/xenapi/volumeops.py | 10 | ||||
| -rw-r--r-- | nova/virt/xenapi_conn.py | 62 | ||||
| -rw-r--r-- | tools/pip-requires | 1 |
13 files changed, 312 insertions, 74 deletions
@@ -21,8 +21,10 @@ Monty Taylor <mordred@inaugust.com> Paul Voccio <paul@openstack.org> Rick Clark <rick@openstack.org> Ryan Lucio <rlucio@internap.com> +Sandy Walsh <sandy.walsh@rackspace.com> Soren Hansen <soren.hansen@rackspace.com> Todd Willey <todd@ansolabs.com> +Trey Morris <trey.morris@rackspace.com> Vishvananda Ishaya <vishvananda@gmail.com> Youcef Laribi <Youcef.Laribi@eu.citrix.com> Zhixue Wu <Zhixue.Wu@citrix.com> diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4eef5e1ef..05f8c3d0b 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -753,7 +753,6 @@ class CloudController(object): ramdisk_id=kwargs.get('ramdisk_id'), display_name=kwargs.get('display_name'), description=kwargs.get('display_description'), - user_data=kwargs.get('user_data', ''), key_name=kwargs.get('key_name'), security_group=kwargs.get('security_group'), generate_hostname=internal_id_to_ec2_id) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 4ca108c4e..c9efe5222 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -48,6 +48,10 @@ flags.DEFINE_string('nova_api_auth', 'nova.api.openstack.auth.BasicApiAuthManager', 'The auth mechanism to use for the OpenStack API implemenation') +flags.DEFINE_bool('allow_admin_api', + False, + 'When True, this API service will accept admin operations.') + class API(wsgi.Middleware): """WSGI entry point for all OpenStack API requests.""" @@ -183,6 +187,10 @@ class APIRouter(wsgi.Router): mapper.resource("sharedipgroup", "sharedipgroups", controller=sharedipgroups.Controller()) + if FLAGS.allow_admin_api: + logging.debug("Including admin operations in API.") + # TODO: Place routes for admin operations here. + super(APIRouter, self).__init__(mapper) diff --git a/nova/compute/api.py b/nova/compute/api.py index cb23dae55..8e0efa4cc 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -55,9 +55,8 @@ class ComputeAPI(base.Base): def create_instances(self, context, instance_type, image_id, min_count=1, max_count=1, kernel_id=None, ramdisk_id=None, - display_name='', description='', user_data='', - key_name=None, key_data=None, - security_group='default', + display_name='', description='', key_name=None, + key_data=None, security_group='default', generate_hostname=generate_default_hostname): """Create the number of instances requested if quote and other arguments check out ok.""" @@ -158,8 +157,8 @@ class ComputeAPI(base.Base): {"method": "setup_fixed_ip", "args": {"address": address}}) - logging.debug("Casting to scheduler for %s/%s's instance %s" % - (context.project_id, context.user_id, instance_id)) + logging.debug("Casting to scheduler for %s/%s's instance %s", + context.project_id, context.user_id, instance_id) rpc.cast(context, FLAGS.scheduler_topic, {"method": "run_instance", @@ -169,6 +168,12 @@ class ComputeAPI(base.Base): return instances def ensure_default_security_group(self, context): + """ Create security group for the security context if it + does not already exist + + :param context: the security context + + """ try: db.security_group_get_by_name(context, context.project_id, 'default') @@ -177,7 +182,7 @@ class ComputeAPI(base.Base): 'description': 'default', 'user_id': context.user_id, 'project_id': context.project_id} - group = db.security_group_create(context, values) + db.security_group_create(context, values) def update_instance(self, context, instance_id, **kwargs): """Updates the instance in the datastore. diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py new file mode 100644 index 000000000..1b2e1654d --- /dev/null +++ b/nova/tests/api/openstack/test_adminapi.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# All Rights Reserved. +# +# Licensed 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 unittest + +import stubout +import webob + +import nova.api +from nova import flags +from nova.tests.api.openstack import fakes + +FLAGS = flags.FLAGS + + +class AdminAPITest(unittest.TestCase): + def setUp(self): + self.stubs = stubout.StubOutForTesting() + fakes.FakeAuthManager.auth_data = {} + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + self.allow_admin = FLAGS.allow_admin_api + + def tearDown(self): + self.stubs.UnsetAll() + FLAGS.allow_admin_api = self.allow_admin + + def test_admin_enabled(self): + FLAGS.allow_admin_api = True + # We should still be able to access public operations. + req = webob.Request.blank('/v1.0/flavors') + res = req.get_response(nova.api.API('os')) + self.assertEqual(res.status_int, 200) + # TODO: Confirm admin operations are available. + + def test_admin_disabled(self): + FLAGS.allow_admin_api = False + # We should still be able to access public operations. + req = webob.Request.blank('/v1.0/flavors') + res = req.get_response(nova.api.API('os')) + self.assertEqual(res.status_int, 200) + # TODO: Confirm admin operations are unavailable. + +if __name__ == '__main__': + unittest.main() diff --git a/nova/tests/virt_unittest.py b/nova/tests/virt_unittest.py index d49383fb7..ba3fba83b 100644 --- a/nova/tests/virt_unittest.py +++ b/nova/tests/virt_unittest.py @@ -25,6 +25,8 @@ from nova import utils from nova.api.ec2 import cloud from nova.auth import manager from nova.virt import libvirt_conn +from nova.virt.xenapi import fake +from nova.virt.xenapi import volume_utils FLAGS = flags.FLAGS flags.DECLARE('instances_path', 'nova.compute.manager') @@ -257,3 +259,47 @@ class NWFilterTestCase(test.TrialTestCase): d.addCallback(lambda _: self.teardown_security_group()) return d + + +class XenAPIVolumeTestCase(test.TrialTestCase): + + def setUp(self): + super(XenAPIVolumeTestCase, self).setUp() + self.flags(xenapi_use_fake_session=True) + self.session = fake.FakeXenAPISession() + self.helper = volume_utils.VolumeHelper + self.helper.late_import() + + def test_create_iscsi_storage_raise_no_exception(self): + info = self.helper.parse_volume_info(None, None) + label = 'SR-' + description = '' + self.helper.create_iscsi_storage_blocking(self.session, + info, + label, + description) + + def test_create_iscsi_storage_raise_unable_to_create_sr_exception(self): + info = self.helper.parse_volume_info(None, None) + label = None + description = None + self.assertFailure(self.helper.create_iscsi_storage_blocking(self.session, + info, + label, + description), + StorageError) + + def test_find_sr_from_vbd_raise_no_exception(self): + pass + + def test_destroy_iscsi_storage_raise_no_exception(self): + pass + + def test_introduce_vdi_raise_no_exception(self): + pass + + def test_introduce_vdi_raise_unable_get_vdi_record_exception(self): + pass + + def tearDown(self): + super(XenAPIVolumeTestCase, self).tearDown() diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py new file mode 100644 index 000000000..75ff587e1 --- /dev/null +++ b/nova/virt/xenapi/fake.py @@ -0,0 +1,59 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2010 Citrix Systems, Inc. +# +# Licensed 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. + +""" +A fake XenAPI SDK. + +Allows for xenapi helper classes testing. +""" + + +class Failure(Exception): + def __init__(self, message=None): + super(Failure, self).__init__(message) + self.details = [] + + def __str__(self): + return 'Fake XenAPI Exception' + + +class FakeXenAPISession(object): + """ The session to invoke XenAPI SDK calls """ + def __init__(self): + pass + + def get_xenapi(self): + """ Return the xenapi object """ + raise NotImplementedError() + + def get_xenapi_host(self): + """ Return the xenapi host """ + raise NotImplementedError() + + def call_xenapi(self, method, *args): + """Call the specified XenAPI method on a background thread. Returns + a Deferred for the result.""" + raise NotImplementedError() + + def async_call_plugin(self, plugin, fn, args): + """Call Async.host.call_plugin on a background thread. Returns a + Deferred with the task reference.""" + raise NotImplementedError() + + def wait_for_task(self, task): + """Return a Deferred that will give the result of the given task. + The task is polled until it completes.""" + raise NotImplementedError() diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 0549dc9fb..89831fe5d 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -20,16 +20,22 @@ their attributes like VDIs, VIFs, as well as their lookup functions. """ import logging +import urllib from twisted.internet import defer +from xml.dom import minidom +from nova import flags from nova import utils + from nova.auth.manager import AuthManager from nova.compute import instance_types from nova.compute import power_state from nova.virt import images from nova.virt.xenapi.volume_utils import StorageError +FLAGS = flags.FLAGS + XENAPI_POWER_STATE = { 'Halted': power_state.SHUTDOWN, 'Running': power_state.RUNNING, @@ -37,13 +43,14 @@ XENAPI_POWER_STATE = { 'Suspended': power_state.SHUTDOWN, # FIXME 'Crashed': power_state.CRASHED} -XenAPI = None - class VMHelper(): """ The class that wraps the helper methods together. """ + + XenAPI = None + def __init__(self): return @@ -52,9 +59,13 @@ class VMHelper(): """ Load XenAPI module in for helper class """ - global XenAPI - if XenAPI is None: - XenAPI = __import__('XenAPI') + xenapi_module = \ + FLAGS.xenapi_use_fake_session and 'nova.virt.xenapi.fake' or 'XenAPI' + from_list = \ + FLAGS.xenapi_use_fake_session and ['fake'] or [] + if cls.XenAPI is None: + cls.XenAPI = __import__(xenapi_module, + globals(), locals(), from_list, -1) @classmethod @defer.inlineCallbacks @@ -140,9 +151,9 @@ class VMHelper(): vbd_rec = session.get_xenapi().VBD.get_record(vbd) if vbd_rec['userdevice'] == str(number): return vbd - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn(exc) - raise StorageError('VBD not found in instance %s' % vm_ref) + raise StorageError('VBD not found in instance %s' % vm_ref) @classmethod @defer.inlineCallbacks @@ -150,7 +161,7 @@ class VMHelper(): """ Unplug VBD from VM """ try: vbd_ref = yield session.call_xenapi('VBD.unplug', vbd_ref) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn(exc) if exc.details[0] != 'DEVICE_ALREADY_DETACHED': raise StorageError('Unable to unplug VBD %s' % vbd_ref) @@ -162,7 +173,7 @@ class VMHelper(): try: task = yield session.call_xenapi('Async.VBD.destroy', vbd_ref) yield session.wait_for_task(task) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn(exc) raise StorageError('Unable to destroy VBD %s' % vbd_ref) @@ -248,7 +259,7 @@ class VMHelper(): # Test valid VDI record = session.get_xenapi().VDI.get_record(vdi) logging.debug('VDI %s is still available', record['uuid']) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn(exc) else: vdis.append(vdi) @@ -265,3 +276,40 @@ class VMHelper(): 'mem': long(record['memory_dynamic_max']) >> 10, 'num_cpu': record['VCPUs_max'], 'cpu_time': 0} + + @classmethod + def compile_diagnostics(cls, session, record): + """Compile VM diagnostics data""" + try: + host = session.get_xenapi_host() + host_ip = session.get_xenapi().host.get_record(host)["address"] + metrics = session.get_xenapi().VM_guest_metrics.get_record( + record["guest_metrics"]) + diags = { + "Kernel": metrics["os_version"]["uname"], + "Distro": metrics["os_version"]["name"]} + xml = get_rrd(host_ip, record["uuid"]) + if xml: + rrd = minidom.parseString(xml) + for i, node in enumerate(rrd.firstChild.childNodes): + # We don't want all of the extra garbage + if i >= 3 and i <= 11: + ref = node.childNodes + # Name and Value + diags[ref[0].firstChild.data] = ref[6].firstChild.data + return diags + except cls.XenAPI.Failure as e: + return {"Unable to retrieve diagnostics": e} + + +def get_rrd(host, uuid): + """Return the VM RRD XML as a string""" + try: + xml = urllib.urlopen("http://%s:%s@%s/vm_rrd?uuid=%s" % ( + FLAGS.xenapi_connection_username, + FLAGS.xenapi_connection_password, + host, + uuid)) + return xml.read() + except IOError: + return None diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7e54e1a8c..a5d923a3b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -24,21 +24,18 @@ from twisted.internet import defer from nova import db from nova import context + from nova.auth.manager import AuthManager from nova.virt.xenapi.network_utils import NetworkHelper from nova.virt.xenapi.vm_utils import VMHelper -XenAPI = None - class VMOps(object): """ Management class for VM-related tasks """ def __init__(self, session): - global XenAPI - if XenAPI is None: - XenAPI = __import__('XenAPI') + self.XenAPI = __import__('XenAPI') self._session = session # Load XenAPI module in the helper class VMHelper.late_import() @@ -105,7 +102,7 @@ class VMOps(object): task = yield self._session.call_xenapi('Async.VM.hard_shutdown', vm) yield self._session.wait_for_task(task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) # Disk clean-up if vdis: @@ -114,12 +111,12 @@ class VMOps(object): task = yield self._session.call_xenapi('Async.VDI.destroy', vdi) yield self._session.wait_for_task(task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) try: task = yield self._session.call_xenapi('Async.VM.destroy', vm) yield self._session.wait_for_task(task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) def get_info(self, instance_id): @@ -130,6 +127,15 @@ class VMOps(object): rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_info(rec) + @defer.inlineCallbacks + def get_diagnostics(self, instance_id): + """Return data about VM diagnostics""" + vm = yield VMHelper.lookup(self._session, instance_id) + if vm is None: + raise Exception("instance not present %s" % instance_id) + rec = yield self._session.get_xenapi().VM.get_record(vm) + defer.returnValue(VMHelper.compile_diagnostics(self._session, rec)) + def get_console_output(self, instance): """ Return snapshot of console """ # TODO: implement this to fix pylint! diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index d247066aa..05143ed91 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -31,8 +31,8 @@ from nova import flags from nova import process from nova import utils + FLAGS = flags.FLAGS -XenAPI = None class StorageError(Exception): @@ -45,6 +45,9 @@ class VolumeHelper(): """ The class that wraps the helper methods together. """ + + XenAPI = None + def __init__(self): return @@ -53,9 +56,13 @@ class VolumeHelper(): """ Load XenAPI module in for helper class """ - global XenAPI - if XenAPI is None: - XenAPI = __import__('XenAPI') + xenapi_module = \ + FLAGS.xenapi_use_fake_session and 'nova.virt.xenapi.fake' or 'XenAPI' + from_list = \ + FLAGS.xenapi_use_fake_session and ['fake'] or [] + if cls.XenAPI is None: + cls.XenAPI = __import__(xenapi_module, + globals(), locals(), from_list, -1) @classmethod @utils.deferredToThread @@ -94,7 +101,7 @@ class VolumeHelper(): '0', label, description, 'iscsi', '', False, {}) logging.debug('Introduced %s as %s.', label, sr_ref) return sr_ref - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn(exc) raise StorageError('Unable to create Storage Repository') else: @@ -107,7 +114,7 @@ class VolumeHelper(): try: vdi_ref = yield session.get_xenapi().VBD.get_VDI(vbd_ref) sr_ref = yield session.get_xenapi().VDI.get_SR(vdi_ref) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn(exc) raise StorageError('Unable to find SR from VBD %s' % vbd_ref) defer.returnValue(sr_ref) @@ -125,19 +132,19 @@ class VolumeHelper(): pbds = [] try: pbds = session.get_xenapi().SR.get_PBDs(sr_ref) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn('Ignoring exception %s when getting PBDs for %s', exc, sr_ref) for pbd in pbds: try: session.get_xenapi().PBD.unplug(pbd) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn('Ignoring exception %s when unplugging PBD %s', exc, pbd) try: session.get_xenapi().SR.forget(sr_ref) logging.debug("Forgetting SR %s done.", sr_ref) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn('Ignoring exception %s when forgetting SR %s', exc, sr_ref) @@ -152,12 +159,12 @@ class VolumeHelper(): """ Synchronous introduce_vdi """ try: vdis = session.get_xenapi().SR.get_VDIs(sr_ref) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn(exc) raise StorageError('Unable to introduce VDI on SR %s' % sr_ref) try: vdi_rec = session.get_xenapi().VDI.get_record(vdis[0]) - except XenAPI.Failure, exc: + except cls.XenAPI.Failure, exc: logging.warn(exc) raise StorageError('Unable to get record of VDI %s on' % vdis[0]) else: diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py index 16363df58..b1afdc811 100644 --- a/nova/virt/xenapi/volumeops.py +++ b/nova/virt/xenapi/volumeops.py @@ -25,17 +25,13 @@ from nova.virt.xenapi.vm_utils import VMHelper from nova.virt.xenapi.volume_utils import VolumeHelper from nova.virt.xenapi.volume_utils import StorageError -XenAPI = None - class VolumeOps(object): """ Management class for Volume-related tasks """ def __init__(self, session): - global XenAPI - if XenAPI is None: - XenAPI = __import__('XenAPI') + self.XenAPI = __import__('XenAPI') self._session = session # Load XenAPI module in the helper classes respectively VolumeHelper.late_import() @@ -76,7 +72,7 @@ class VolumeOps(object): vm_ref, vdi_ref, vol_rec['deviceNumber'], False) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) yield VolumeHelper.destroy_iscsi_storage(self._session, sr_ref) raise Exception('Unable to use SR %s for instance %s' @@ -87,7 +83,7 @@ class VolumeOps(object): task = yield self._session.call_xenapi('Async.VBD.plug', vbd_ref) yield self._session.wait_for_task(task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) yield VolumeHelper.destroy_iscsi_storage(self._session, sr_ref) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 4caf44be0..b82862764 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -36,6 +36,7 @@ reactor thread if the VM.get_by_name_label or VM.get_record calls block. **Related Flags** +:xenapi_use_fake_session: To be set for unit testing :xenapi_connection_url: URL for connection to XenServer/Xen Cloud Platform. :xenapi_connection_username: Username for connection to XenServer/Xen Cloud Platform (default: root). @@ -62,6 +63,10 @@ from nova.virt.xenapi.vmops import VMOps from nova.virt.xenapi.volumeops import VolumeOps FLAGS = flags.FLAGS + +flags.DEFINE_boolean('xenapi_use_fake_session', + True, + 'Set to true in order to use the fake XenAPI SDK') flags.DEFINE_string('xenapi_connection_url', None, 'URL for connection to XenServer/Xen Cloud Platform.' @@ -89,17 +94,10 @@ flags.DEFINE_string('iqn_prefix', 'iqn.2010-10.org.openstack', 'IQN Prefix') -XenAPI = None - def get_connection(_): """Note that XenAPI doesn't have a read-only connection mode, so the read_only parameter is ignored.""" - # This is loaded late so that there's no need to install this - # library when not using XenAPI. - global XenAPI - if XenAPI is None: - XenAPI = __import__('XenAPI') url = FLAGS.xenapi_connection_url username = FLAGS.xenapi_connection_username password = FLAGS.xenapi_connection_password @@ -156,7 +154,10 @@ class XenAPIConnection(object): class XenAPISession(object): """ The session to invoke XenAPI SDK calls """ def __init__(self, url, user, pw): - self._session = XenAPI.Session(url) + # This is loaded late so that there's no need to install this + # library when not using XenAPI. + self.XenAPI = __import__('XenAPI') + self._session = self.XenAPI.Session(url) self._session.login_with_password(user, pw) def get_xenapi(self): @@ -180,7 +181,7 @@ class XenAPISession(object): def async_call_plugin(self, plugin, fn, args): """Call Async.host.call_plugin on a background thread. Returns a Deferred with the task reference.""" - return _unwrap_plugin_exceptions( + return self._unwrap_plugin_exceptions( self._session.xenapi.Async.host.call_plugin, self.get_xenapi_host(), plugin, fn, args) @@ -209,33 +210,32 @@ class XenAPISession(object): error_info = self._session.xenapi.task.get_error_info(task) logging.warn('Task %s status: %s. %s', task, status, error_info) - deferred.errback(XenAPI.Failure(error_info)) + deferred.errback(self.XenAPI.Failure(error_info)) #logging.debug('Polling task %s done.', task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) deferred.errback(exc) - -def _unwrap_plugin_exceptions(func, *args, **kwargs): - """ Parse exception details """ - try: - return func(*args, **kwargs) - except XenAPI.Failure, exc: - logging.debug("Got exception: %s", exc) - if (len(exc.details) == 4 and - exc.details[0] == 'XENAPI_PLUGIN_EXCEPTION' and - exc.details[2] == 'Failure'): - params = None - try: - params = eval(exc.details[3]) - except: - raise exc - raise XenAPI.Failure(params) - else: + def _unwrap_plugin_exceptions(self, func, *args, **kwargs): + """ Parse exception details """ + try: + return func(*args, **kwargs) + except self.XenAPI.Failure, exc: + logging.debug("Got exception: %s", exc) + if (len(exc.details) == 4 and + exc.details[0] == 'XENAPI_PLUGIN_EXCEPTION' and + exc.details[2] == 'Failure'): + params = None + try: + params = eval(exc.details[3]) + except: + raise exc + raise self.XenAPI.Failure(params) + else: + raise + except xmlrpclib.ProtocolError, exc: + logging.debug("Got exception: %s", exc) raise - except xmlrpclib.ProtocolError, exc: - logging.debug("Got exception: %s", exc) - raise def _parse_xmlrpc_value(val): diff --git a/tools/pip-requires b/tools/pip-requires index 548073326..17a1a4c5c 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -20,3 +20,4 @@ mox==0.5.0 -f http://pymox.googlecode.com/files/mox-0.5.0.tar.gz greenlet==0.3.1 nose +bzr |
