diff --git a/glance/api/v2/images.py b/glance/api/v2/images.py index 6975e63ebc..cb1476c0b7 100644 --- a/glance/api/v2/images.py +++ b/glance/api/v2/images.py @@ -258,8 +258,8 @@ class ResponseSerializer(wsgi.JSONResponseSerializer): def _format_image(self, image): _image = image['properties'] - for key in ['id', 'name', 'created_at', 'updated_at', 'tags', 'owner', - 'checksum']: + for key in ['id', 'name', 'created_at', 'updated_at', 'tags', 'size', + 'owner', 'checksum']: _image[key] = image[key] _image['visibility'] = 'public' if image['is_public'] else 'private' _image = self.schema.filter(_image) @@ -333,6 +333,10 @@ _BASE_PROPERTIES = { 'type': 'string', 'maxLength': 32, }, + 'size': { + 'type': 'integer', + 'description': 'Size of image file in bytes', + }, 'created_at': { 'type': 'string', 'description': 'Date and time of image registration', diff --git a/glance/tests/functional/v2/test_images.py b/glance/tests/functional/v2/test_images.py index 631d534814..289a25c27b 100644 --- a/glance/tests/functional/v2/test_images.py +++ b/glance/tests/functional/v2/test_images.py @@ -85,6 +85,7 @@ class TestImages(functional.FunctionalTest): image = json.loads(response.text)['image'] self.assertEqual(image_id, image['id']) self.assertEqual(None, image['checksum']) + self.assertEqual(None, image['size']) self.assertEqual('bar', image['foo']) self.assertTrue(image['created_at']) self.assertTrue(image['updated_at']) @@ -147,6 +148,13 @@ class TestImages(functional.FunctionalTest): response = requests.put(path, headers=headers, data='XXX') self.assertEqual(409, response.status_code) + # Ensure the size is updated to reflect the data uploaded + path = self._url('/v2/images/%s' % image_id) + headers = self._headers() + response = requests.get(path, headers=headers) + self.assertEqual(200, response.status_code) + self.assertEqual(5, json.loads(response.text)['image']['size']) + # Deletion should work path = self._url('/v2/images/%s' % image_id) response = requests.delete(path, headers=self._headers()) diff --git a/glance/tests/functional/v2/test_schemas.py b/glance/tests/functional/v2/test_schemas.py index 836d4c6430..d686fd7489 100644 --- a/glance/tests/functional/v2/test_schemas.py +++ b/glance/tests/functional/v2/test_schemas.py @@ -55,6 +55,7 @@ class TestSchemas(functional.FunctionalTest): 'created_at', 'updated_at', 'tags', + 'size', 'type', 'format', 'self', diff --git a/glance/tests/unit/v2/test_images_resource.py b/glance/tests/unit/v2/test_images_resource.py index 57daec5d4c..a4e1731b36 100644 --- a/glance/tests/unit/v2/test_images_resource.py +++ b/glance/tests/unit/v2/test_images_resource.py @@ -703,6 +703,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': ['one', 'two'], + 'size': 1024, }, { 'id': unit_test_utils.UUID2, @@ -714,6 +715,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': [], + 'size': None, }, ] expected = { @@ -727,6 +729,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': ['one', 'two'], + 'size': 1024, 'self': '/v2/images/%s' % unit_test_utils.UUID1, 'file': '/v2/images/%s/file' % unit_test_utils.UUID1, 'access': '/v2/images/%s/access' % unit_test_utils.UUID1, @@ -741,6 +744,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': [], + 'size': None, 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, 'access': '/v2/images/%s/access' % unit_test_utils.UUID2, @@ -768,6 +772,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': ['one', 'two'], + 'size': 1024, }, { 'id': unit_test_utils.UUID2, @@ -779,6 +784,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': [], + 'size': None, }, ] expected = { @@ -792,6 +798,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': ['one', 'two'], + 'size': 1024, 'self': '/v2/images/%s' % unit_test_utils.UUID1, 'file': '/v2/images/%s/file' % unit_test_utils.UUID1, 'access': '/v2/images/%s/access' % unit_test_utils.UUID1, @@ -806,6 +813,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': [], + 'size': None, 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, 'access': '/v2/images/%s/access' % unit_test_utils.UUID2, @@ -835,6 +843,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': ['one', 'two'], + 'size': 1024, }, { 'id': unit_test_utils.UUID2, @@ -846,6 +855,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': [], + 'size': None, }, ] expected = { @@ -859,6 +869,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': ['one', 'two'], + 'size': 1024, 'self': '/v2/images/%s' % unit_test_utils.UUID1, 'file': '/v2/images/%s/file' % unit_test_utils.UUID1, 'access': '/v2/images/%s/access' % unit_test_utils.UUID1, @@ -873,6 +884,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': [], + 'size': None, 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, 'access': '/v2/images/%s/access' % unit_test_utils.UUID2, @@ -901,6 +913,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': ['one', 'two'], + 'size': 1024, }, { 'id': unit_test_utils.UUID2, @@ -912,6 +925,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': [], + 'size': None, }, ] expected = { @@ -925,6 +939,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': ['one', 'two'], + 'size': 1024, 'self': '/v2/images/%s' % unit_test_utils.UUID1, 'file': '/v2/images/%s/file' % unit_test_utils.UUID1, 'access': '/v2/images/%s/access' % unit_test_utils.UUID1, @@ -939,6 +954,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': [], + 'size': None, 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, 'access': '/v2/images/%s/access' % unit_test_utils.UUID2, @@ -969,6 +985,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': ['three', 'four'], + 'size': 1024, } expected = { 'image': { @@ -980,6 +997,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': ['three', 'four'], + 'size': 1024, 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, 'access': '/v2/images/%s/access' % unit_test_utils.UUID2, @@ -1001,6 +1019,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': [], + 'size': 1024, } self_link = '/v2/images/%s' % unit_test_utils.UUID2 expected = { @@ -1013,6 +1032,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': [], + 'size': 1024, 'self': self_link, 'file': '%s/file' % self_link, 'access': '%s/access' % self_link, @@ -1035,6 +1055,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': ['five'], + 'size': 1024, } self_link = '/v2/images/%s' % unit_test_utils.UUID2 expected = { @@ -1047,6 +1068,7 @@ class TestImagesSerializer(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': ['five'], + 'size': 1024, 'self': self_link, 'file': '%s/file' % self_link, 'access': '%s/access' % self_link, @@ -1082,6 +1104,7 @@ class TestImagesSerializerWithExtendedSchema(test_utils.BaseTestCase): 'created_at': DATETIME, 'updated_at': DATETIME, 'tags': [], + 'size': 1024, 'properties': {'color': 'green', 'mood': 'grouchy'}, } @@ -1096,6 +1119,7 @@ class TestImagesSerializerWithExtendedSchema(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': [], + 'size': 1024, 'color': 'green', 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, @@ -1119,6 +1143,7 @@ class TestImagesSerializerWithExtendedSchema(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': [], + 'size': 1024, 'color': 'invalid', 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, @@ -1148,6 +1173,7 @@ class TestImagesSerializerWithAdditionalProperties(test_utils.BaseTestCase): 'marx': 'groucho', }, 'tags': [], + 'size': 1024, } def test_show(self): @@ -1163,6 +1189,7 @@ class TestImagesSerializerWithAdditionalProperties(test_utils.BaseTestCase): 'updated_at': ISOTIME, 'marx': 'groucho', 'tags': [], + 'size': 1024, 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, 'access': '/v2/images/%s/access' % unit_test_utils.UUID2, @@ -1190,6 +1217,7 @@ class TestImagesSerializerWithAdditionalProperties(test_utils.BaseTestCase): 'updated_at': ISOTIME, 'marx': 123, 'tags': [], + 'size': 1024, 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, 'access': '/v2/images/%s/access' % unit_test_utils.UUID2, @@ -1213,6 +1241,7 @@ class TestImagesSerializerWithAdditionalProperties(test_utils.BaseTestCase): 'created_at': ISOTIME, 'updated_at': ISOTIME, 'tags': [], + 'size': 1024, 'self': '/v2/images/%s' % unit_test_utils.UUID2, 'file': '/v2/images/%s/file' % unit_test_utils.UUID2, 'access': '/v2/images/%s/access' % unit_test_utils.UUID2,