From 89de4ed5e94620f7ca3716a888d7e26a0d23e98f Mon Sep 17 00:00:00 2001 From: Sean Dague Date: Tue, 8 Dec 2015 09:17:06 -0500 Subject: [PATCH] Add upload/download vhd2 interfaces This adds interfaces to the glance plugin for uploading / downloading vhd files by glance url instead of with a host / port / use_ssl tuple. This makes it possible to have glance hosted on a non root url and still work with xenserver. This bumps the plugin version to 1.3 as these are new functions exposed. Convert xenapi driver to using up/down vhd2 All unit test converted to use this new functionality. Change-Id: I73f60de0b1644827238961e1659b0119b80bf82f --- .../unit/virt/xenapi/image/test_glance.py | 40 +++++++++---------- nova/tests/unit/virt/xenapi/stubs.py | 2 +- nova/tests/unit/virt/xenapi/test_vm_utils.py | 2 +- nova/virt/xenapi/client/session.py | 2 +- nova/virt/xenapi/fake.py | 3 +- nova/virt/xenapi/image/glance.py | 13 +++--- .../xenapi/etc/xapi.d/plugins/glance | 34 +++++++++++++++- .../etc/xapi.d/plugins/nova_plugin_version | 3 +- ...er-glance-plugin-1.3-11c3b70b8c928263.yaml | 6 +++ 9 files changed, 72 insertions(+), 33 deletions(-) create mode 100644 releasenotes/notes/xenserver-glance-plugin-1.3-11c3b70b8c928263.yaml diff --git a/nova/tests/unit/virt/xenapi/image/test_glance.py b/nova/tests/unit/virt/xenapi/image/test_glance.py index 044e413213e0..d3c13682cf0d 100644 --- a/nova/tests/unit/virt/xenapi/image/test_glance.py +++ b/nova/tests/unit/virt/xenapi/image/test_glance.py @@ -61,9 +61,7 @@ class TestGlanceStore(stubs.XenAPITestBaseNoDB): def _get_params(self): return {'image_id': 'fake_image_uuid', - 'glance_host': '1.1.1.1', - 'glance_port': 123, - 'glance_use_ssl': False, + 'endpoint': 'http://1.1.1.1:123', 'sr_path': '/fake/sr/path', 'extra_headers': {'X-Auth-Token': 'foobar', 'X-Roles': '', @@ -83,7 +81,8 @@ class TestGlanceStore(stubs.XenAPITestBaseNoDB): lambda *a, **kw: ['uuid1']) self.mox.StubOutWithMock(self.session, 'call_plugin_serialized') - self.session.call_plugin_serialized('glance', 'download_vhd', **params) + self.session.call_plugin_serialized('glance', 'download_vhd2', + **params) self.mox.ReplayAll() self.store.download_image(self.context, self.session, @@ -102,19 +101,20 @@ class TestGlanceStore(stubs.XenAPITestBaseNoDB): params = self._get_download_params() self.flags(num_retries=2, group='glance') - params.pop("glance_port") - params.pop("glance_host") - calls = [mock.call('glance', 'download_vhd', glance_port=9292, - glance_host='10.0.1.1', **params), - mock.call('glance', 'download_vhd', glance_port=9293, - glance_host='10.0.0.1', **params)] + params.pop("endpoint") + calls = [mock.call('glance', 'download_vhd2', + endpoint='http://10.0.1.1:9292', + **params), + mock.call('glance', 'download_vhd2', + endpoint='http://10.0.0.1:9293', + **params)] log_calls = [mock.call(mock.ANY, {'callback_result': '10.0.1.1', 'attempts': 3, 'attempt': 1, - 'fn': 'download_vhd', + 'fn': 'download_vhd2', 'plugin': 'glance'}), mock.call(mock.ANY, {'callback_result': '10.0.0.1', 'attempts': 3, 'attempt': 2, - 'fn': 'download_vhd', + 'fn': 'download_vhd2', 'plugin': 'glance'})] glance_api_servers = ['10.0.1.1:9292', @@ -147,7 +147,7 @@ class TestGlanceStore(stubs.XenAPITestBaseNoDB): params = self._get_upload_params(auto_disk_config, expected_os_type) self.mox.StubOutWithMock(self.session, 'call_plugin_serialized') - self.session.call_plugin_serialized('glance', 'upload_vhd', **params) + self.session.call_plugin_serialized('glance', 'upload_vhd2', **params) self.mox.ReplayAll() self.store.upload_image(self.context, self.session, self.instance, @@ -174,7 +174,7 @@ class TestGlanceStore(stubs.XenAPITestBaseNoDB): params = self._get_upload_params() self.mox.StubOutWithMock(self.session, 'call_plugin_serialized') - self.session.call_plugin_serialized('glance', 'upload_vhd', + self.session.call_plugin_serialized('glance', 'upload_vhd2', **params).AndRaise(RuntimeError) self.mox.ReplayAll() @@ -192,21 +192,21 @@ class TestGlanceStore(stubs.XenAPITestBaseNoDB): self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') error_details = ["", "", "RetryableError", ""] error = self.session.XenAPI.Failure(details=error_details) - self.session.call_plugin_serialized('glance', 'upload_vhd', + self.session.call_plugin_serialized('glance', 'upload_vhd2', **params).AndRaise(error) compute_utils.add_instance_fault_from_exc(self.context, self.instance, error, (fake.Failure, error, mox.IgnoreArg())) time.sleep(0.5) - self.session.call_plugin_serialized('glance', 'upload_vhd', + self.session.call_plugin_serialized('glance', 'upload_vhd2', **params).AndRaise(error) compute_utils.add_instance_fault_from_exc(self.context, self.instance, error, (fake.Failure, error, mox.IgnoreArg())) time.sleep(1) - self.session.call_plugin_serialized('glance', 'upload_vhd', + self.session.call_plugin_serialized('glance', 'upload_vhd2', **params).AndRaise(error) compute_utils.add_instance_fault_from_exc(self.context, self.instance, error, (fake.Failure, @@ -229,7 +229,7 @@ class TestGlanceStore(stubs.XenAPITestBaseNoDB): self.mox.StubOutWithMock(compute_utils, 'add_instance_fault_from_exc') error_details = ["", "task signaled", "", ""] error = self.session.XenAPI.Failure(details=error_details) - self.session.call_plugin_serialized('glance', 'upload_vhd', + self.session.call_plugin_serialized('glance', 'upload_vhd2', **params).AndRaise(error) compute_utils.add_instance_fault_from_exc(self.context, self.instance, error, (fake.Failure, @@ -239,14 +239,14 @@ class TestGlanceStore(stubs.XenAPITestBaseNoDB): # Note(johngarbutt) XenServer 6.1 and later has this error error_details = ["", "signal: SIGTERM", "", ""] error = self.session.XenAPI.Failure(details=error_details) - self.session.call_plugin_serialized('glance', 'upload_vhd', + self.session.call_plugin_serialized('glance', 'upload_vhd2', **params).AndRaise(error) compute_utils.add_instance_fault_from_exc(self.context, self.instance, error, (fake.Failure, error, mox.IgnoreArg())) time.sleep(1) - self.session.call_plugin_serialized('glance', 'upload_vhd', + self.session.call_plugin_serialized('glance', 'upload_vhd2', **params) self.mox.ReplayAll() diff --git a/nova/tests/unit/virt/xenapi/stubs.py b/nova/tests/unit/virt/xenapi/stubs.py index 5d515b16e501..af4ada335b6a 100644 --- a/nova/tests/unit/virt/xenapi/stubs.py +++ b/nova/tests/unit/virt/xenapi/stubs.py @@ -168,7 +168,7 @@ class FakeSessionForVMTests(fake.SessionBase): "# Completed on Sun Nov 6 22:49:02 2011\n") def host_call_plugin(self, _1, _2, plugin, method, _5): - if (plugin, method) == ('glance', 'download_vhd'): + if plugin == 'glance' and method in ('download_vhd', 'download_vhd2'): root_uuid = _make_fake_vdi() return pickle.dumps(dict(root=dict(uuid=root_uuid))) elif (plugin, method) == ("xenhost", "iptables_config"): diff --git a/nova/tests/unit/virt/xenapi/test_vm_utils.py b/nova/tests/unit/virt/xenapi/test_vm_utils.py index 6f722bd79f0d..436158b1bd7c 100644 --- a/nova/tests/unit/virt/xenapi/test_vm_utils.py +++ b/nova/tests/unit/virt/xenapi/test_vm_utils.py @@ -283,7 +283,7 @@ class FetchVhdImageTestCase(VMUtilsTestBase): self.mox.StubOutWithMock( self.session, 'call_plugin_serialized_with_retry') func = self.session.call_plugin_serialized_with_retry( - 'glance', 'download_vhd', 0, mox.IgnoreArg(), mox.IgnoreArg(), + 'glance', 'download_vhd2', 0, mox.IgnoreArg(), mox.IgnoreArg(), extra_headers={'X-Auth-Token': 'auth_token', 'X-Roles': '', 'X-Tenant-Id': None, diff --git a/nova/virt/xenapi/client/session.py b/nova/virt/xenapi/client/session.py index 2af764d0e40c..ca533e9fe63e 100644 --- a/nova/virt/xenapi/client/session.py +++ b/nova/virt/xenapi/client/session.py @@ -81,7 +81,7 @@ class XenAPISession(object): # changed in development environments. # MAJOR VERSION: Incompatible changes with the plugins # MINOR VERSION: Compatible changes, new plguins, etc - PLUGIN_REQUIRED_VERSION = '1.2' + PLUGIN_REQUIRED_VERSION = '1.3' def __init__(self, url, user, pw): version_string = version.version_string_with_package() diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 6437126ff371..083601511302 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -665,6 +665,7 @@ class SessionBase(object): return pickle.dumps(None) _plugin_glance_upload_vhd = _plugin_pickle_noop + _plugin_glance_upload_vhd2 = _plugin_pickle_noop _plugin_kernel_copy_vdi = _plugin_noop _plugin_kernel_create_kernel_ramdisk = _plugin_noop _plugin_kernel_remove_kernel_ramdisk = _plugin_noop @@ -761,7 +762,7 @@ class SessionBase(object): return base64.b64encode(zlib.compress("dom_id: %s" % dom_id)) def _plugin_nova_plugin_version_get_version(self, method, args): - return pickle.dumps("1.2") + return pickle.dumps("1.3") def _plugin_xenhost_query_gc(self, method, args): return pickle.dumps("False") diff --git a/nova/virt/xenapi/image/glance.py b/nova/virt/xenapi/image/glance.py index 557c69b791aa..3e8c5bce7573 100644 --- a/nova/virt/xenapi/image/glance.py +++ b/nova/virt/xenapi/image/glance.py @@ -36,11 +36,10 @@ class GlanceStore(object): glance_api_servers = glance.get_api_servers() def pick_glance(kwargs): - g_host, g_port, g_use_ssl = next(glance_api_servers).as_tuple() - kwargs['glance_host'] = g_host - kwargs['glance_port'] = g_port - kwargs['glance_use_ssl'] = g_use_ssl - return g_host + server = next(glance_api_servers) + kwargs['endpoint'] = server.url + # NOTE(sdague): is the return significant here at all? + return server.host def retry_cb(context, instance, exc=None): if exc: @@ -65,7 +64,7 @@ class GlanceStore(object): try: vdis = self._call_glance_plugin(context, instance, session, - 'download_vhd', params) + 'download_vhd2', params) except exception.PluginRetriesExceeded: raise exception.CouldNotFetchImage(image_id=image_id) @@ -90,6 +89,6 @@ class GlanceStore(object): try: self._call_glance_plugin(context, instance, session, - 'upload_vhd', params) + 'upload_vhd2', params) except exception.PluginRetriesExceeded: raise exception.CouldNotUploadImage(image_id=image_id) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 622e054112f8..f86584b38eb7 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -375,6 +375,24 @@ def validate_image_status_before_upload(conn, url, extra_headers): 'image_status': image_status}) +def download_vhd2(session, image_id, endpoint, + uuid_stack, sr_path, extra_headers): + """Download an image from Glance, unbundle it, and then deposit the VHDs + into the storage repository + """ + staging_path = utils.make_staging_area(sr_path) + try: + # Download tarball into staging area and extract it + _download_tarball_by_url( + sr_path, staging_path, image_id, + endpoint, extra_headers) + + # Move the VHDs from the staging area into the storage repository + return utils.import_vhds(sr_path, staging_path, uuid_stack) + finally: + utils.cleanup_staging_area(staging_path) + + def download_vhd(session, image_id, glance_host, glance_port, glance_use_ssl, uuid_stack, sr_path, extra_headers): """Download an image from Glance, unbundle it, and then deposit the VHDs @@ -393,6 +411,19 @@ def download_vhd(session, image_id, glance_host, glance_port, glance_use_ssl, utils.cleanup_staging_area(staging_path) +def upload_vhd2(session, vdi_uuids, image_id, + endpoint, sr_path, extra_headers, properties): + """Bundle the VHDs comprising an image and then stream them into Glance. + """ + staging_path = utils.make_staging_area(sr_path) + try: + utils.prepare_staging_area(sr_path, staging_path, vdi_uuids) + _upload_tarball_by_url(staging_path, image_id, + endpoint, extra_headers, properties) + finally: + utils.cleanup_staging_area(staging_path) + + def upload_vhd(session, vdi_uuids, image_id, glance_host, glance_port, glance_use_ssl, sr_path, extra_headers, properties): """Bundle the VHDs comprising an image and then stream them into Glance. @@ -407,4 +438,5 @@ def upload_vhd(session, vdi_uuids, image_id, glance_host, glance_port, if __name__ == '__main__': - utils.register_plugin_calls(download_vhd, upload_vhd) + utils.register_plugin_calls(download_vhd, upload_vhd, + download_vhd2, upload_vhd2) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version b/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version index b67c84be524d..74e3c4c936e5 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/nova_plugin_version @@ -28,7 +28,8 @@ import utils # 1.0 - Initial version. # 1.1 - New call to check GC status # 1.2 - Added support for pci passthrough devices -PLUGIN_VERSION = "1.2" +# 1.3 - Add vhd2 functions for doing glance operations by url +PLUGIN_VERSION = "1.3" def get_version(session): return PLUGIN_VERSION diff --git a/releasenotes/notes/xenserver-glance-plugin-1.3-11c3b70b8c928263.yaml b/releasenotes/notes/xenserver-glance-plugin-1.3-11c3b70b8c928263.yaml new file mode 100644 index 000000000000..0a452d4c14d9 --- /dev/null +++ b/releasenotes/notes/xenserver-glance-plugin-1.3-11c3b70b8c928263.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - The glance xenserver plugin has been bumped to version 1.3 which + includes new interfaces for referencing glance servers by url. All + dom0 will need to be upgraded with this plugin before upgrading + the nova code.