diff -Nru cinder-2014.1.5/debian/changelog cinder-2014.1.5/debian/changelog --- cinder-2014.1.5/debian/changelog 2015-08-31 14:51:13.000000000 +0000 +++ cinder-2014.1.5/debian/changelog 2016-11-14 09:50:18.000000000 +0000 @@ -1,3 +1,12 @@ +cinder (1:2014.1.5-0ubuntu3) trusty; urgency=medium + + * Include boot properties from glance v2 images (LP: #1439371): + - d/p/0001-Include-boot-properties-from-glance-v2-images.patch + * Fix extract properties from image with glance api v2 (LP: #1323660): + - d/p/0002-Fix-properties-extracting-from-image-with-glance-api.patch + + -- Seyeong Kim Fri, 11 Nov 2016 11:23:53 +0900 + cinder (1:2014.1.5-0ubuntu2) trusty; urgency=medium * Enable iscsi_write_cache option for tgtadm backends (LP: #1336568): diff -Nru cinder-2014.1.5/debian/patches/0001-Include-boot-properties-from-glance-v2-images.patch cinder-2014.1.5/debian/patches/0001-Include-boot-properties-from-glance-v2-images.patch --- cinder-2014.1.5/debian/patches/0001-Include-boot-properties-from-glance-v2-images.patch 1970-01-01 00:00:00.000000000 +0000 +++ cinder-2014.1.5/debian/patches/0001-Include-boot-properties-from-glance-v2-images.patch 2016-11-14 09:49:27.000000000 +0000 @@ -0,0 +1,117 @@ +From 4dfc33be4f64442cf0605797a8ff53cdccd1899c Mon Sep 17 00:00:00 2001 +From: Jon Bernard +Date: Tue, 7 Apr 2015 13:57:36 -0400 +Subject: [PATCH 1/2] Include boot properties from glance v2 images + +In order for users to take advantage of COW volumes created from +a glance image, Cinder must be configured to use Glance API version +2 (default is 1). In version 2, the required boot metadata (kernel_id +and ramdisk_id) are no long stored in the 'properties' dict, but as +standalone fields in the GET response from glance. The existing cinder +parser for the glance request is not aware of this and the volume +created form a v2 image will lack this required metadata. + +This was causing the recent Ceph CI gate failures for +test_volume_boot_pattern. + +Change-Id: I688898b3841691369d73887f7eabdceb05155db1 +Closes-Bug: #1439371 +(cherry picked from commit ea109b5f24dca93fd6f660bc436a685d6101bcea) +--- + cinder/image/glance.py | 11 ++++++++ + cinder/tests/image/test_glance.py | 57 +++++++++++++++++++++++++++++++++++++++ + 2 files changed, 68 insertions(+) + +diff --git a/cinder/image/glance.py b/cinder/image/glance.py +index e0e6ab2..6d79b28 100644 +--- a/cinder/image/glance.py ++++ b/cinder/image/glance.py +@@ -435,6 +435,17 @@ def _extract_attributes(image): + + output['properties'] = getattr(image, 'properties', {}) + ++ # NOTE(jbernard): Update image properties for API version 2. For UEC ++ # images stored in glance, the necessary boot information is stored in the ++ # properties dict in version 1 so there is nothing more to do. However, in ++ # version 2 these are standalone fields in the GET response. This bit of ++ # code moves them back into the properties dict as the caller expects, thus ++ # producing a volume with correct metadata for booting. ++ for attr in ('kernel_id', 'ramdisk_id'): ++ value = getattr(image, attr, None) ++ if value: ++ output['properties'][attr] = value ++ + return output + + +diff --git a/cinder/tests/image/test_glance.py b/cinder/tests/image/test_glance.py +index 03ef260..1333f77 100644 +--- a/cinder/tests/image/test_glance.py ++++ b/cinder/tests/image/test_glance.py +@@ -585,6 +585,63 @@ class TestGlanceImageService(test.TestCase): + } + self.assertEqual(actual, expected) + ++ @mock.patch('cinder.image.glance.CONF') ++ def test_extracting_v2_boot_properties(self, config): ++ ++ config.glance_api_version = 2 ++ ++ attributes = ['size', 'disk_format', 'owner', 'container_format', ++ 'checksum', 'id', 'name', 'created_at', 'updated_at', ++ 'deleted', 'status', 'min_disk', 'min_ram', 'is_public'] ++ ++ metadata = { ++ 'id': 1, ++ 'size': 2, ++ 'min_disk': 2, ++ 'min_ram': 2, ++ 'kernel_id': 'foo', ++ 'ramdisk_id': 'bar', ++ } ++ ++ class FakeSchema(object): ++ ++ def __init__(self, base): ++ self.base = base ++ ++ def is_base_property(self, key): ++ if key in self.base: ++ return True ++ else: ++ return False ++ ++ image = glance_stubs.FakeImage(metadata) ++ client = glance_stubs.StubGlanceClient() ++ ++ service = self._create_image_service(client) ++ service._image_schema = FakeSchema(attributes) ++ actual = service._translate_from_glance(image) ++ expected = { ++ 'id': 1, ++ 'name': None, ++ 'is_public': None, ++ 'size': 2, ++ 'min_disk': 2, ++ 'min_ram': 2, ++ 'disk_format': None, ++ 'container_format': None, ++ 'checksum': None, ++ 'deleted': None, ++ 'deleted_at': None, ++ 'status': None, ++ 'properties': {'kernel_id': 'foo', ++ 'ramdisk_id': 'bar'}, ++ 'owner': None, ++ 'created_at': None, ++ 'updated_at': None ++ } ++ ++ self.assertEqual(expected, actual) ++ + + class TestGlanceClientVersion(test.TestCase): + """Tests the version of the glance client generated.""" +-- +2.9.3 + diff -Nru cinder-2014.1.5/debian/patches/0002-Fix-properties-extracting-from-image-with-glance-api.patch cinder-2014.1.5/debian/patches/0002-Fix-properties-extracting-from-image-with-glance-api.patch --- cinder-2014.1.5/debian/patches/0002-Fix-properties-extracting-from-image-with-glance-api.patch 1970-01-01 00:00:00.000000000 +0000 +++ cinder-2014.1.5/debian/patches/0002-Fix-properties-extracting-from-image-with-glance-api.patch 2016-11-14 09:49:38.000000000 +0000 @@ -0,0 +1,248 @@ +From aa036a66f32ba137895852d9dc59086634483b92 Mon Sep 17 00:00:00 2001 +From: Anton Arefiev +Date: Tue, 31 Mar 2015 19:44:57 +0300 +Subject: [PATCH 2/2] Fix properties extracting from image with glance api v2 + +When a volume is created using the glance v1 API, the non based +properties places in 'properties' dict, and nova is expected +to find them here. Otherwise api's v2 return custom image properties +as base properties, so nova can't find them in volume 'image +properties'. + +This change add new glance call to get image schema, it allows +extract custom properties from image. + +Closes-Bug: 1323660 + +Change-Id: Ib54bb6759b27334294fb2c6d2c0bfe4eae3d0920 +(cherry picked from commit f20643ca44e139e3384b9dea638b54c2235b1169) +--- + cinder/image/glance.py | 59 +++++++++++++++++++++++++++++---------- + cinder/tests/glance/stubs.py | 23 +++++++++++---- + cinder/tests/image/test_glance.py | 21 ++------------ + 3 files changed, 65 insertions(+), 38 deletions(-) + +diff --git a/cinder/image/glance.py b/cinder/image/glance.py +index 6d79b28..e7a48c1 100644 +--- a/cinder/image/glance.py ++++ b/cinder/image/glance.py +@@ -142,9 +142,7 @@ class GlanceClientWrapper(object): + If we get a connection error, + retry the request according to CONF.glance_num_retries. + """ +- version = self.version +- if version in kwargs: +- version = kwargs['version'] ++ version = kwargs.pop('version', self.version) + + retry_excs = (glanceclient.exc.ServiceUnavailable, + glanceclient.exc.InvalidEndpoint, +@@ -155,7 +153,9 @@ class GlanceClientWrapper(object): + client = self.client or self._create_onetime_client(context, + version) + try: +- return getattr(client.images, method)(*args, **kwargs) ++ controller = getattr(client, ++ kwargs.pop('controller', 'images')) ++ return getattr(controller, method)(*args, **kwargs) + except retry_excs as e: + netloc = self.netloc + extra = "retrying" +@@ -184,6 +184,7 @@ class GlanceImageService(object): + + def __init__(self, client=None): + self._client = client or GlanceClientWrapper() ++ self._image_schema = None + + def detail(self, context, **kwargs): + """Calls out to Glance for a list of detailed image information.""" +@@ -196,7 +197,7 @@ class GlanceImageService(object): + _images = [] + for image in images: + if self._is_image_available(context, image): +- _images.append(self._translate_from_glance(image)) ++ _images.append(self._translate_from_glance(context, image)) + + return _images + +@@ -225,7 +226,7 @@ class GlanceImageService(object): + if not self._is_image_available(context, image): + raise exception.ImageNotFound(image_id=image_id) + +- base_image_meta = self._translate_from_glance(image) ++ base_image_meta = self._translate_from_glance(context, image) + return base_image_meta + + def get_location(self, context, image_id): +@@ -285,7 +286,7 @@ class GlanceImageService(object): + recv_service_image_meta = self._client.call(context, 'create', + **sent_service_image_meta) + +- return self._translate_from_glance(recv_service_image_meta) ++ return self._translate_from_glance(context, recv_service_image_meta) + + def update(self, context, image_id, + image_meta, data=None, purge_props=True): +@@ -310,7 +311,7 @@ class GlanceImageService(object): + except Exception: + _reraise_translated_image_exception(image_id) + else: +- return self._translate_from_glance(image_meta) ++ return self._translate_from_glance(context, image_meta) + + def delete(self, context, image_id): + """Delete the given image. +@@ -325,6 +326,41 @@ class GlanceImageService(object): + raise exception.ImageNotFound(image_id=image_id) + return True + ++ def _translate_from_glance(self, context, image): ++ """Get image metadata from glance image. ++ ++ Extract metadata from image and convert it's properties ++ to type cinder expected. ++ ++ :param image: glance image object ++ :return: image metadata dictionary ++ """ ++ if CONF.glance_api_version == 2: ++ if self._image_schema is None: ++ self._image_schema = self._client.call(context, 'get', ++ controller='schemas', ++ schema_name='image', ++ version=2) ++ # NOTE(aarefiev): get base image property, store image 'schema' ++ # is redundant, so ignore it. ++ image_meta = {key: getattr(image, key) ++ for key in image.keys() ++ if self._image_schema.is_base_property(key) is True ++ and key != 'schema'} ++ ++ # NOTE(aarefiev): nova is expected that all image properties ++ # (custom or defined in schema-image.json) stores in ++ # 'properties' key. ++ image_meta['properties'] = { ++ key: getattr(image, key) for key in image.keys() ++ if self._image_schema.is_base_property(key) is False} ++ else: ++ image_meta = _extract_attributes(image) ++ ++ image_meta = _convert_timestamps_to_datetimes(image_meta) ++ image_meta = _convert_from_string(image_meta) ++ return image_meta ++ + @staticmethod + def _translate_to_glance(image_meta): + image_meta = _convert_to_string(image_meta) +@@ -332,13 +368,6 @@ class GlanceImageService(object): + return image_meta + + @staticmethod +- def _translate_from_glance(image): +- image_meta = _extract_attributes(image) +- image_meta = _convert_timestamps_to_datetimes(image_meta) +- image_meta = _convert_from_string(image_meta) +- return image_meta +- +- @staticmethod + def _is_image_available(context, image): + """Check image availability. + +diff --git a/cinder/tests/glance/stubs.py b/cinder/tests/glance/stubs.py +index 9a1bbdd..7ad45d0 100644 +--- a/cinder/tests/glance/stubs.py ++++ b/cinder/tests/glance/stubs.py +@@ -18,6 +18,13 @@ import glanceclient.exc + NOW_GLANCE_FORMAT = "2010-10-11T10:30:22" + + ++IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner', ++ 'container_format', 'checksum', 'id', ++ 'name', 'created_at', 'updated_at', ++ 'deleted', 'status', ++ 'min_disk', 'min_ram', 'is_public'] ++ ++ + class StubGlanceClient(object): + + def __init__(self, images=None): +@@ -88,11 +95,6 @@ class StubGlanceClient(object): + + class FakeImage(object): + def __init__(self, metadata): +- IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner', +- 'container_format', 'checksum', 'id', +- 'name', 'created_at', 'updated_at', +- 'deleted', 'status', +- 'min_disk', 'min_ram', 'is_public'] + raw = dict.fromkeys(IMAGE_ATTRIBUTES) + raw.update(metadata) + self.__dict__['raw'] = raw +@@ -108,3 +110,14 @@ class FakeImage(object): + self.__dict__['raw'][key] = value + except KeyError: + raise AttributeError(key) ++ ++ def keys(self): ++ return self.__dict__['raw'].keys() ++ ++ ++class FakeSchema(object): ++ def is_base_property(self, key): ++ if key in IMAGE_ATTRIBUTES: ++ return True ++ else: ++ return False +diff --git a/cinder/tests/image/test_glance.py b/cinder/tests/image/test_glance.py +index 1333f77..7817b90 100644 +--- a/cinder/tests/image/test_glance.py ++++ b/cinder/tests/image/test_glance.py +@@ -590,10 +590,6 @@ class TestGlanceImageService(test.TestCase): + + config.glance_api_version = 2 + +- attributes = ['size', 'disk_format', 'owner', 'container_format', +- 'checksum', 'id', 'name', 'created_at', 'updated_at', +- 'deleted', 'status', 'min_disk', 'min_ram', 'is_public'] +- + metadata = { + 'id': 1, + 'size': 2, +@@ -603,23 +599,13 @@ class TestGlanceImageService(test.TestCase): + 'ramdisk_id': 'bar', + } + +- class FakeSchema(object): +- +- def __init__(self, base): +- self.base = base +- +- def is_base_property(self, key): +- if key in self.base: +- return True +- else: +- return False +- + image = glance_stubs.FakeImage(metadata) + client = glance_stubs.StubGlanceClient() + + service = self._create_image_service(client) +- service._image_schema = FakeSchema(attributes) +- actual = service._translate_from_glance(image) ++ service._image_schema = glance_stubs.FakeSchema() ++ ++ actual = service._translate_from_glance('fake_context', image) + expected = { + 'id': 1, + 'name': None, +@@ -631,7 +617,6 @@ class TestGlanceImageService(test.TestCase): + 'container_format': None, + 'checksum': None, + 'deleted': None, +- 'deleted_at': None, + 'status': None, + 'properties': {'kernel_id': 'foo', + 'ramdisk_id': 'bar'}, +-- +2.9.3 + diff -Nru cinder-2014.1.5/debian/patches/series cinder-2014.1.5/debian/patches/series --- cinder-2014.1.5/debian/patches/series 2015-08-31 14:51:13.000000000 +0000 +++ cinder-2014.1.5/debian/patches/series 2016-11-14 09:49:38.000000000 +0000 @@ -1,2 +1,4 @@ fix-requirements.patch tgtadmin-iscsi-write-cache-config.patch +0001-Include-boot-properties-from-glance-v2-images.patch +0002-Fix-properties-extracting-from-image-with-glance-api.patch