diff options
-rw-r--r-- | nova/tests/test_xenapi.py | 68 | ||||
-rw-r--r-- | nova/tests/virt/xenapi/imageupload/__init__.py | 0 | ||||
-rw-r--r-- | nova/tests/virt/xenapi/imageupload/test_glance.py | 74 | ||||
-rw-r--r-- | nova/virt/xenapi/imageupload/__init__.py | 0 | ||||
-rw-r--r-- | nova/virt/xenapi/imageupload/glance.py | 54 | ||||
-rw-r--r-- | nova/virt/xenapi/vm_utils.py | 29 | ||||
-rw-r--r-- | nova/virt/xenapi/vmops.py | 25 |
7 files changed, 165 insertions, 85 deletions
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 067e28a13..aa640810b 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -19,7 +19,6 @@ import ast import base64 import contextlib -import cPickle as pickle import functools import os import re @@ -48,6 +47,7 @@ from nova.virt.xenapi import agent from nova.virt.xenapi import driver as xenapi_conn from nova.virt.xenapi import fake as xenapi_fake from nova.virt.xenapi import host +from nova.virt.xenapi.imageupload import glance from nova.virt.xenapi import pool from nova.virt.xenapi import pool_states from nova.virt.xenapi import vm_utils @@ -431,15 +431,29 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): {'task_state': task_states.IMAGE_UPLOADING, 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + image_id = "my_snapshot_id" stubs.stubout_instance_snapshot(self.stubs) stubs.stubout_is_snapshot(self.stubs) # Stubbing out firewall driver as previous stub sets alters # xml rpc result parsing stubs.stubout_firewall_driver(self.stubs, self.conn) + instance = self._create_instance() - image_id = "my_snapshot_id" + self.fake_upload_called = False + + def fake_image_upload(_self, ctx, session, inst, vdi_uuids, + img_id): + self.fake_upload_called = True + self.assertEqual(ctx, self.context) + self.assertEqual(inst, instance) + self.assertTrue(isinstance(vdi_uuids, list)) + self.assertEqual(img_id, image_id) + + self.stubs.Set(glance.GlanceStore, 'upload_image', + fake_image_upload) + self.conn.snapshot(self.context, instance, image_id, func_call_matcher.call) @@ -469,6 +483,8 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): name_label = vdi_rec["name_label"] self.assert_(not name_label.endswith('snapshot')) + self.assertTrue(self.fake_upload_called) + def create_vm_record(self, conn, os_type, name): instances = conn.list_instances() self.assertEquals(instances, [name]) @@ -2574,54 +2590,6 @@ class SwapXapiHostTestCase(test.TestCase): "http://someserver", 'otherserver')) -class VmUtilsTestCase(test.TestCase): - """Unit tests for xenapi utils.""" - - def test_upload_image(self): - def fake_instance_system_metadata_get(context, uuid): - return dict(image_a=1, image_b=2, image_c='c', d='d') - - def fake_get_sr_path(session): - return "foo" - - class FakeInstance(dict): - def __init__(self): - super(FakeInstance, self).__init__({ - 'auto_disk_config': 'auto disk config', - 'os_type': 'os type'}) - - def __missing__(self, item): - return "whatever" - - class FakeSession(object): - def call_plugin(session_self, service, command, kwargs): - self.kwargs = kwargs - - def call_plugin_serialized(session_self, service, command, *args, - **kwargs): - self.kwargs = kwargs - - def fake_dumps(thing): - return thing - - self.stubs.Set(db, "instance_system_metadata_get", - fake_instance_system_metadata_get) - self.stubs.Set(vm_utils, "get_sr_path", fake_get_sr_path) - self.stubs.Set(pickle, "dumps", fake_dumps) - - ctx = context.get_admin_context() - - instance = FakeInstance() - session = FakeSession() - vm_utils.upload_image(ctx, session, instance, "vmi uuids", "image id") - - actual = self.kwargs['properties'] - # Inheritance happens in another place, now - expected = dict(auto_disk_config='auto disk config', - os_type='os type') - self.assertEquals(expected, actual) - - class XenAPILiveMigrateTestCase(stubs.XenAPITestBase): """Unit tests for live_migration.""" def setUp(self): diff --git a/nova/tests/virt/xenapi/imageupload/__init__.py b/nova/tests/virt/xenapi/imageupload/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/nova/tests/virt/xenapi/imageupload/__init__.py diff --git a/nova/tests/virt/xenapi/imageupload/test_glance.py b/nova/tests/virt/xenapi/imageupload/test_glance.py new file mode 100644 index 000000000..b0518228d --- /dev/null +++ b/nova/tests/virt/xenapi/imageupload/test_glance.py @@ -0,0 +1,74 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 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 mox + +from nova import context +from nova import test +from nova.virt.xenapi.imageupload import glance +from nova.virt.xenapi import vm_utils + + +class TestGlanceStore(test.TestCase): + def setUp(self): + super(TestGlanceStore, self).setUp() + self.store = glance.GlanceStore() + self.mox = mox.Mox() + + def tearDown(self): + super(TestGlanceStore, self).tearDown() + + def test_upload_image(self): + glance_host = '0.1.2.3' + glance_port = 8143 + glance_use_ssl = False + sr_path = '/fake/sr/path' + self.flags(glance_host=glance_host) + self.flags(glance_port=glance_port) + self.flags(glance_api_insecure=glance_use_ssl) + + def fake_get_sr_path(*_args, **_kwargs): + return sr_path + + self.stubs.Set(vm_utils, 'get_sr_path', fake_get_sr_path) + + ctx = context.RequestContext('user', 'project', auth_token='foobar') + properties = { + 'auto_disk_config': True, + 'os_type': 'default', + } + image_id = 'fake_image_uuid' + vdi_uuids = ['fake_vdi_uuid'] + instance = {'uuid': 'blah'} + instance.update(properties) + + params = {'vdi_uuids': vdi_uuids, + 'image_id': image_id, + 'glance_host': glance_host, + 'glance_port': glance_port, + 'glance_use_ssl': glance_use_ssl, + 'sr_path': sr_path, + 'auth_token': 'foobar', + 'properties': properties} + session = self.mox.CreateMockAnything() + session.call_plugin_serialized('glance', 'upload_vhd', **params) + self.mox.ReplayAll() + + self.store.upload_image(ctx, session, instance, vdi_uuids, image_id) + + self.mox.VerifyAll() diff --git a/nova/virt/xenapi/imageupload/__init__.py b/nova/virt/xenapi/imageupload/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/nova/virt/xenapi/imageupload/__init__.py diff --git a/nova/virt/xenapi/imageupload/glance.py b/nova/virt/xenapi/imageupload/glance.py new file mode 100644 index 000000000..adc06f65b --- /dev/null +++ b/nova/virt/xenapi/imageupload/glance.py @@ -0,0 +1,54 @@ +# Copyright 2013 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. + +from nova.image import glance +from nova.openstack.common import cfg +import nova.openstack.common.log as logging +from nova.virt.xenapi import vm_utils + +LOG = logging.getLogger(__name__) + +CONF = cfg.CONF + + +class GlanceStore(object): + + def upload_image(self, context, session, instance, vdi_uuids, image_id): + """Requests that the Glance plugin bundle the specified VDIs and + push them into Glance using the specified human-friendly name. + """ + # NOTE(sirp): Currently we only support uploading images as VHD, there + # is no RAW equivalent (yet) + LOG.debug(_("Asking xapi to upload to glance %(vdi_uuids)s as" + " ID %(image_id)s"), locals(), instance=instance) + + glance_api_servers = glance.get_api_servers() + glance_host, glance_port, glance_use_ssl = glance_api_servers.next() + + properties = { + 'auto_disk_config': instance['auto_disk_config'], + 'os_type': instance['os_type'] or CONF.default_os_type, + } + + params = {'vdi_uuids': vdi_uuids, + 'image_id': image_id, + 'glance_host': glance_host, + 'glance_port': glance_port, + 'glance_use_ssl': glance_use_ssl, + 'sr_path': vm_utils.get_sr_path(session), + 'auth_token': getattr(context, 'auth_token', None), + 'properties': properties} + + session.call_plugin_serialized('glance', 'upload_vhd', **params) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 582a9320a..fe4ce9409 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -715,35 +715,6 @@ def _find_cached_image(session, image_id, sr_ref): return cached_images.get(image_id) -def upload_image(context, session, instance, vdi_uuids, image_id): - """Requests that the Glance plugin bundle the specified VDIs and - push them into Glance using the specified human-friendly name. - """ - # NOTE(sirp): Currently we only support uploading images as VHD, there - # is no RAW equivalent (yet) - LOG.debug(_("Asking xapi to upload %(vdi_uuids)s as" - " ID %(image_id)s"), locals(), instance=instance) - - glance_api_servers = glance.get_api_servers() - glance_host, glance_port, glance_use_ssl = glance_api_servers.next() - - properties = { - 'auto_disk_config': instance['auto_disk_config'], - 'os_type': instance['os_type'] or CONF.default_os_type, - } - - params = {'vdi_uuids': vdi_uuids, - 'image_id': image_id, - 'glance_host': glance_host, - 'glance_port': glance_port, - 'glance_use_ssl': glance_use_ssl, - 'sr_path': get_sr_path(session), - 'auth_token': getattr(context, 'auth_token', None), - 'properties': properties} - - session.call_plugin_serialized('glance', 'upload_vhd', **params) - - def resize_disk(session, instance, vdi_ref, instance_type): # Copy VDI over to something we can resize # NOTE(jerdfelt): Would be nice to just set vdi_ref to read/write diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 4a8372cda..8d3a3bed2 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -59,7 +59,10 @@ xenapi_vmops_opts = [ 'to go to running state'), cfg.StrOpt('xenapi_vif_driver', default='nova.virt.xenapi.vif.XenAPIBridgeDriver', - help='The XenAPI VIF driver using XenServer Network APIs.') + help='The XenAPI VIF driver using XenServer Network APIs.'), + cfg.StrOpt('xenapi_image_upload_handler', + default='nova.virt.xenapi.imageupload.glance.GlanceStore', + help='Object Store Driver used to handle image uploads.'), ] CONF = cfg.CONF @@ -161,6 +164,11 @@ class VMOps(object): self.vif_driver = vif_impl(xenapi_session=self._session) self.default_root_dev = '/dev/sda' + msg = _("Importing image upload handler: %s") + LOG.debug(msg % CONF.xenapi_image_upload_handler) + self.image_upload_handler = importutils.import_object( + CONF.xenapi_image_upload_handler) + @property def agent_enabled(self): return not CONF.xenapi_disable_agent @@ -661,9 +669,11 @@ class VMOps(object): coalesce together, so, we must wait for this coalescing to occur to get a stable representation of the data on disk. - 3. Push-to-glance: Once coalesced, we call a plugin on the XenServer - that will bundle the VHDs together and then push the bundle into - Glance. + 3. Push-to-data-store: Once coalesced, we call a plugin on the + XenServer that will bundle the VHDs together and then push the + bundle. Depending on the configured value of + 'xenapi_image_upload_handler', image data may be pushed to + Glance or the specified data store. """ vm_ref = self._get_vm_opaque_ref(instance) @@ -674,8 +684,11 @@ class VMOps(object): update_task_state) as vdi_uuids: update_task_state(task_state=task_states.IMAGE_UPLOADING, expected_state=task_states.IMAGE_PENDING_UPLOAD) - vm_utils.upload_image( - context, self._session, instance, vdi_uuids, image_id) + self.image_upload_handler.upload_image(context, + self._session, + instance, + vdi_uuids, + image_id) LOG.debug(_("Finished snapshot and upload for VM"), instance=instance) |