From 47a20a9f44f94fd77ef877778d7055ebaadd3072 Mon Sep 17 00:00:00 2001 From: Jared Culp Date: Tue, 23 Jul 2013 15:25:25 -0400 Subject: [PATCH] xenapi: send identity headers from glance plugin Send identity headers while uploading/downloading images from glance. Related to: 1) Add identity headers while calling glanceclient from nova Ife02059abbdce7920f1f408b71e9745d777fa770 2) Pass identity headers received in glanceclient to nova Ifbef582aa4e64a2e7a46db43a9cc6cf8c3531dbd 3) Pass identity headers received by glance api to glance registry Ie5f07ed6dfeaa8428de4f79c4d40d182328e6ab4 NOTE: since this is a change to a Dom0 plugin API, it requires the lock-step upgrade of both the compute-manager and the dom0 plugin. DocImpact Change-Id: I6d5e3448d2c0acc392d18e0b88cec25cf313da5b --- nova/image/glance.py | 21 +++++---- nova/tests/virt/xenapi/image/test_glance.py | 46 ++++++++++--------- nova/tests/virt/xenapi/test_vm_utils.py | 14 +++++- nova/virt/xenapi/image/glance.py | 2 +- .../xenapi/etc/xapi.d/plugins/glance | 23 ++++------ 5 files changed, 58 insertions(+), 48 deletions(-) diff --git a/nova/image/glance.py b/nova/image/glance.py index dae52aa560e5..9c5c0abcb042 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -104,6 +104,17 @@ def _parse_image_ref(image_href): return (image_id, host, port, use_ssl) +def generate_identity_headers(context, status='Confirmed'): + return { + 'X-Auth-Token': getattr(context, 'auth_token', None), + 'X-User-Id': getattr(context, 'user', None), + 'X-Tenant-Id': getattr(context, 'tenant', None), + 'X-Roles': ','.join(context.roles), + 'X-Identity-Status': status, + 'X-Service-Catalog': json.dumps(context.service_catalog), + } + + def _create_glance_client(context, host, port, use_ssl, version=1): """Instantiate a new glanceclient.Client object.""" params = {} @@ -120,15 +131,7 @@ def _create_glance_client(context, host, port, use_ssl, version=1): # keyword 'token', but later versions accept both the # header 'X-Auth-Token' and 'token' params['token'] = context.auth_token - identity_headers = { - 'X-Auth-Token': context.auth_token, - 'X-User-Id': context.user, - 'X-Tenant-Id': context.tenant, - 'X-Roles': ','.join(context.roles), - 'X-Identity-Status': 'Confirmed', - 'X-Service-Catalog': json.dumps(context.service_catalog), - } - params['identity_headers'] = identity_headers + params['identity_headers'] = generate_identity_headers(context) endpoint = '%s://%s:%s' % (scheme, host, port) return glanceclient.Client(str(version), endpoint, **params) diff --git a/nova/tests/virt/xenapi/image/test_glance.py b/nova/tests/virt/xenapi/image/test_glance.py index 7459b2216cef..5a329bc8a65d 100644 --- a/nova/tests/virt/xenapi/image/test_glance.py +++ b/nova/tests/virt/xenapi/image/test_glance.py @@ -16,8 +16,6 @@ # under the License. -import mox - from nova import context from nova import exception from nova.tests.virt.xenapi import stubs @@ -31,7 +29,6 @@ class TestGlanceStore(stubs.XenAPITestBase): def setUp(self): super(TestGlanceStore, self).setUp() self.store = glance.GlanceStore() - self.mox = mox.Mox() self.flags(glance_host='1.1.1.1', glance_port=123, @@ -56,14 +53,26 @@ class TestGlanceStore(stubs.XenAPITestBase): 'os_type': 'default', 'xenapi_use_agent': 'true'} + def _get_params(self): + return {'image_id': 'fake_image_uuid', + 'glance_host': '1.1.1.1', + 'glance_port': 123, + 'glance_use_ssl': False, + 'sr_path': '/fake/sr/path', + 'extra_headers': {'X-Service-Catalog': '[]', + 'X-Auth-Token': 'foobar', + 'X-Roles': '', + 'X-Tenant-Id': 'project', + 'X-User-Id': 'user', + 'X-Identity-Status': 'Confirmed'}} + + def _get_download_params(self): + params = self._get_params() + params['uuid_stack'] = ['uuid1'] + return params + def test_download_image(self): - params = {'image_id': 'fake_image_uuid', - 'glance_host': '1.1.1.1', - 'glance_port': 123, - 'glance_use_ssl': False, - 'sr_path': '/fake/sr/path', - 'auth_token': 'foobar', - 'uuid_stack': ['uuid1']} + params = self._get_download_params() self.stubs.Set(vm_utils, '_make_uuid_stack', lambda *a, **kw: ['uuid1']) @@ -78,15 +87,10 @@ class TestGlanceStore(stubs.XenAPITestBase): self.mox.VerifyAll() def _get_upload_params(self): - params = {'vdi_uuids': ['fake_vdi_uuid'], - 'image_id': 'fake_image_uuid', - 'glance_host': '1.1.1.1', - 'glance_port': 123, - 'glance_use_ssl': False, - 'sr_path': '/fake/sr/path', - 'auth_token': 'foobar', - 'properties': {'auto_disk_config': True, - 'os_type': 'default'}} + params = self._get_params() + params['vdi_uuids'] = ['fake_vdi_uuid'] + params['properties'] = {'auto_disk_config': True, + 'os_type': 'default'} return params def test_upload_image(self): @@ -105,10 +109,10 @@ class TestGlanceStore(stubs.XenAPITestBase): self.mox.StubOutWithMock(self.session, 'call_plugin_serialized') self.session.call_plugin_serialized('glance', 'upload_vhd', - **params).AndRaise(Exception) + **params).AndRaise(RuntimeError) self.mox.ReplayAll() - self.assertRaises(Exception, self.store.upload_image, + self.assertRaises(RuntimeError, self.store.upload_image, self.context, self.session, self.instance, ['fake_vdi_uuid'], 'fake_image_uuid') self.mox.VerifyAll() diff --git a/nova/tests/virt/xenapi/test_vm_utils.py b/nova/tests/virt/xenapi/test_vm_utils.py index dc2527526321..6f9b9b676718 100644 --- a/nova/tests/virt/xenapi/test_vm_utils.py +++ b/nova/tests/virt/xenapi/test_vm_utils.py @@ -243,7 +243,12 @@ class FetchVhdImageTestCase(test.TestCase): self.session, 'call_plugin_serialized_with_retry') self.session.call_plugin_serialized_with_retry( 'glance', 'download_vhd', 0, mox.IgnoreArg(), - auth_token='auth_token', + extra_headers={'X-Service-Catalog': '[]', + 'X-Auth-Token': 'auth_token', + 'X-Roles': '', + 'X-Tenant-Id': None, + 'X-User-Id': None, + 'X-Identity-Status': 'Confirmed'}, image_id='image_id', uuid_stack=["uuid_stack"], sr_path='sr_path').AndReturn({'root': {'uuid': 'vdi'}}) @@ -316,7 +321,12 @@ class FetchVhdImageTestCase(test.TestCase): self.session, 'call_plugin_serialized_with_retry') self.session.call_plugin_serialized_with_retry( 'glance', 'download_vhd', 0, mox.IgnoreArg(), - auth_token='auth_token', + extra_headers={'X-Service-Catalog': '[]', + 'X-Auth-Token': 'auth_token', + 'X-Roles': '', + 'X-Tenant-Id': None, + 'X-User-Id': None, + 'X-Identity-Status': 'Confirmed'}, image_id='image_id', uuid_stack=["uuid_stack"], sr_path='sr_path').AndReturn({'root': {'uuid': 'vdi'}}) diff --git a/nova/virt/xenapi/image/glance.py b/nova/virt/xenapi/image/glance.py index 59930a69e53a..b6ec77a84ebd 100644 --- a/nova/virt/xenapi/image/glance.py +++ b/nova/virt/xenapi/image/glance.py @@ -39,7 +39,7 @@ class GlanceStore(object): def _make_params(self, context, session, image_id): return {'image_id': image_id, 'sr_path': vm_utils.get_sr_path(session), - 'auth_token': getattr(context, 'auth_token', None)} + 'extra_headers': glance.generate_identity_headers(context)} def download_image(self, context, session, instance, image_id): params = self._make_params(context, session, image_id) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 0413c78c3438..dad9acb4a40d 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -89,15 +89,10 @@ def _download_tarball_and_verify(request, staging_path): def _download_tarball(sr_path, staging_path, image_id, glance_host, - glance_port, glance_use_ssl, auth_token): + glance_port, glance_use_ssl, extra_headers): """Download the tarball image from Glance and extract it into the staging area. Retry if there is any failure. """ - # Build request headers - headers = {} - if auth_token: - headers['x-auth-token'] = auth_token - if glance_use_ssl: scheme = 'https' else: @@ -107,7 +102,7 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host, "%(image_id)s" % locals()) logging.info("Downloading %s" % url) - request = urllib2.Request(url, headers=headers) + request = urllib2.Request(url, headers=extra_headers) try: _download_tarball_and_verify(request, staging_path) except Exception: @@ -116,7 +111,7 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host, def _upload_tarball(staging_path, image_id, glance_host, glance_port, - glance_use_ssl, auth_token, properties): + glance_use_ssl, extra_headers, properties): """ Create a tarball of the image and then stream that into Glance using chunked-transfer-encoded HTTP. @@ -166,9 +161,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port, 'x-image-meta-container-format': 'ovf', 'x-glance-registry-purge-props': 'False'} - # If we have an auth_token, set an x-auth-token header - if auth_token: - headers['x-auth-token'] = auth_token + headers.update(**extra_headers) for key, value in properties.iteritems(): header_key = "x-image-meta-property-%s" % key.replace('_', '-') @@ -229,7 +222,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port, def download_vhd(session, image_id, glance_host, glance_port, glance_use_ssl, - uuid_stack, sr_path, auth_token): + uuid_stack, sr_path, extra_headers): """Download an image from Glance, unbundle it, and then deposit the VHDs into the storage repository """ @@ -238,7 +231,7 @@ def download_vhd(session, image_id, glance_host, glance_port, glance_use_ssl, # Download tarball into staging area and extract it _download_tarball( sr_path, staging_path, image_id, glance_host, glance_port, - glance_use_ssl, auth_token) + glance_use_ssl, extra_headers) # Move the VHDs from the staging area into the storage repository return utils.import_vhds(sr_path, staging_path, uuid_stack) @@ -247,14 +240,14 @@ def download_vhd(session, image_id, glance_host, glance_port, glance_use_ssl, def upload_vhd(session, vdi_uuids, image_id, glance_host, glance_port, - glance_use_ssl, sr_path, auth_token, properties): + glance_use_ssl, 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(staging_path, image_id, glance_host, glance_port, - glance_use_ssl, auth_token, properties) + glance_use_ssl, extra_headers, properties) finally: utils.cleanup_staging_area(staging_path)