summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmando Migliaccio <armando.migliaccio@citrix.com>2010-12-10 18:24:59 +0000
committerArmando Migliaccio <armando.migliaccio@citrix.com>2010-12-10 18:24:59 +0000
commit83cd4057e358e597e0d1f13392e000cbb58f096a (patch)
tree49ae85ea0ed4f1d9f71178292751a98b7a3e585a
parent9420d99dc2fb75a8f6b518f55b6580027af582c3 (diff)
parent1a759c3722610d720dd8dabf816db146c1063937 (diff)
* pylint fixes
* code clean-up * first cut for xenapi unit tests
-rw-r--r--Authors2
-rw-r--r--nova/api/ec2/cloud.py1
-rw-r--r--nova/api/openstack/__init__.py8
-rw-r--r--nova/compute/api.py17
-rw-r--r--nova/tests/api/openstack/test_adminapi.py61
-rw-r--r--nova/tests/virt_unittest.py46
-rw-r--r--nova/virt/xenapi/fake.py59
-rw-r--r--nova/virt/xenapi/vm_utils.py68
-rw-r--r--nova/virt/xenapi/vmops.py22
-rw-r--r--nova/virt/xenapi/volume_utils.py29
-rw-r--r--nova/virt/xenapi/volumeops.py10
-rw-r--r--nova/virt/xenapi_conn.py62
-rw-r--r--tools/pip-requires1
13 files changed, 312 insertions, 74 deletions
diff --git a/Authors b/Authors
index 62f0c49d5..c9bf3b67c 100644
--- a/Authors
+++ b/Authors
@@ -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