From: Pavel Kholkin Date: Thu, 1 Dec 2016 15:37:28 +0000 (+0300) Subject: [proxy-api] microversion 2.39 deprecates image-metadata proxy API X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=df6e2d37f2c4b4c4dcccf870bc236ca5adc7770e;p=osstest%2Fopenstack-nova.git [proxy-api] microversion 2.39 deprecates image-metadata proxy API Almost all proxy APIs were deprecated in microversion 2.36. But the sub-resource image-metadata of image was forgotten to deprecate. This patch deprecates the image-metdata API from 2.39. Implements blueprint deprecate-image-meta-proxy-api Closes-bug: #1614578 Change-Id: I5507337ab6fe4a377f66dec3fe275d75618cd7b4 --- diff --git a/api-ref/source/limits.inc b/api-ref/source/limits.inc index 71b47b3902..fcf5e6ada0 100644 --- a/api-ref/source/limits.inc +++ b/api-ref/source/limits.inc @@ -24,7 +24,7 @@ Response - limits: limits - absolute: limits_absolutes - - maxImageMeta: metadata_items + - maxImageMeta: image_metadata_items - maxPersonality: injected_files - maxPersonalitySize: injected_file_content_bytes - maxSecurityGroupRules: security_group_rules diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 7813770a19..917254980a 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -2361,6 +2361,15 @@ image_id_body: in: body required: true type: string +image_metadata_items: + description: | + The number of allowed metadata items for each image. Starting from + version 2.39 this field is dropped from 'os-limits' response, because + 'image-metadata' proxy API was deprecated. + in: body + required: true + type: integer + max_version: 2.38 image_name: description: | The display name of an Image. diff --git a/api-ref/source/servers-actions.inc b/api-ref/source/servers-actions.inc index 18f1d48b55..ce22f10eef 100644 --- a/api-ref/source/servers-actions.inc +++ b/api-ref/source/servers-actions.inc @@ -219,6 +219,10 @@ If the operation succeeds, the created image has a status of ``active`` and the server status returns to the original status. You can also see the new image in the image back end that OpenStack Image service manages. +.. note:: + Starting from version 2.39 the image quota enforcement with Nova `metadata` + is removed and quota checks should be performed using Glance API directly. + **Preconditions** The server must exist. diff --git a/api-ref/source/servers-admin-action.inc b/api-ref/source/servers-admin-action.inc index 5a8550d88f..f3508fee73 100644 --- a/api-ref/source/servers-admin-action.inc +++ b/api-ref/source/servers-admin-action.inc @@ -26,6 +26,10 @@ Policy defaults enable only users with the administrative role or the owner of the server to perform this operation. Cloud providers can change these permissions through the ``policy.json`` file. +.. note:: + Starting from version 2.39 the image quota enforcement with Nova `metadata` + is removed and quota checks should be performed using Glance API directly. + Normal response codes: 202 Error response codes: badRequest(400), unauthorized(401), forbidden(403), diff --git a/doc/api_samples/limits/v2.39/limit-get-resp.json b/doc/api_samples/limits/v2.39/limit-get-resp.json new file mode 100644 index 0000000000..68b29eacd6 --- /dev/null +++ b/doc/api_samples/limits/v2.39/limit-get-resp.json @@ -0,0 +1,20 @@ +{ + "limits": { + "absolute": { + "maxPersonality": 5, + "maxPersonalitySize": 10240, + "maxServerMeta": 128, + "maxTotalCores": 20, + "maxTotalInstances": 10, + "maxTotalKeypairs": 100, + "maxTotalRAMSize": 51200, + "maxServerGroups": 10, + "maxServerGroupMembers": 10, + "totalCoresUsed": 0, + "totalInstancesUsed": 0, + "totalRAMUsed": 0, + "totalServerGroupsUsed": 0 + }, + "rate": [] + } +} diff --git a/doc/api_samples/versions/v21-version-get-resp.json b/doc/api_samples/versions/v21-version-get-resp.json index 79a37afb0c..e46112e48d 100644 --- a/doc/api_samples/versions/v21-version-get-resp.json +++ b/doc/api_samples/versions/v21-version-get-resp.json @@ -19,7 +19,7 @@ } ], "status": "CURRENT", - "version": "2.38", + "version": "2.39", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/doc/api_samples/versions/versions-get-resp.json b/doc/api_samples/versions/versions-get-resp.json index c672f1e6e8..6922ff85e7 100644 --- a/doc/api_samples/versions/versions-get-resp.json +++ b/doc/api_samples/versions/versions-get-resp.json @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.38", + "version": "2.39", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/api/openstack/api_version_request.py b/nova/api/openstack/api_version_request.py index 6a3865f967..d80f710b41 100644 --- a/nova/api/openstack/api_version_request.py +++ b/nova/api/openstack/api_version_request.py @@ -95,6 +95,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: UUID format. * 2.38 - Add a condition to return HTTPBadRequest if invalid status is provided for listing servers. + * 2.39 - Deprecates image-metadata proxy API """ # The minimum and maximum versions of the API supported @@ -103,14 +104,18 @@ REST_API_VERSION_HISTORY = """REST API Version History: # Note(cyeoh): This only applies for the v2.1 API once microversions # support is fully merged. It does not affect the V2 API. _MIN_API_VERSION = "2.1" -_MAX_API_VERSION = "2.38" +_MAX_API_VERSION = "2.39" DEFAULT_API_VERSION = _MIN_API_VERSION -# All the proxy APIs which related network, images and baremetal +# Almost all proxy APIs which related to network, images and baremetal # were deprecated from 2.36. MAX_PROXY_API_SUPPORT_VERSION = '2.35' MIN_WITHOUT_PROXY_API_SUPPORT_VERSION = '2.36' +# Starting from microversion 2.39 also image-metadata proxy API is deprecated. +MAX_IMAGE_META_PROXY_API_VERSION = '2.38' +MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION = '2.39' + # NOTE(cyeoh): min and max versions declared as functions so we can # mock them for unittests. Do not use the constants directly anywhere diff --git a/nova/api/openstack/compute/create_backup.py b/nova/api/openstack/compute/create_backup.py index 38368e95bc..fbe950f7c3 100644 --- a/nova/api/openstack/compute/create_backup.py +++ b/nova/api/openstack/compute/create_backup.py @@ -15,6 +15,7 @@ import webob +from nova.api.openstack import api_version_request from nova.api.openstack import common from nova.api.openstack.compute.schemas import create_backup from nova.api.openstack import extensions @@ -57,7 +58,11 @@ class CreateBackupController(wsgi.Controller): props = {} metadata = entity.get('metadata', {}) - common.check_img_metadata_properties_quota(context, metadata) + # Starting from microversion 2.39 we don't check quotas on createBackup + if api_version_request.is_supported( + req, max_version= + api_version_request.MAX_IMAGE_META_PROXY_API_VERSION): + common.check_img_metadata_properties_quota(context, metadata) props.update(metadata) instance = common.get_instance(self.compute_api, context, id) diff --git a/nova/api/openstack/compute/image_metadata.py b/nova/api/openstack/compute/image_metadata.py index 8c50687894..ab28fe48f8 100644 --- a/nova/api/openstack/compute/image_metadata.py +++ b/nova/api/openstack/compute/image_metadata.py @@ -16,6 +16,8 @@ import six from webob import exc +from nova.api.openstack.api_version_request import \ + MAX_IMAGE_META_PROXY_API_VERSION from nova.api.openstack import common from nova.api.openstack.compute.schemas import image_metadata from nova.api.openstack import extensions @@ -43,6 +45,7 @@ class ImageMetadataController(wsgi.Controller): msg = _("Image not found.") raise exc.HTTPNotFound(explanation=msg) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((403, 404)) def index(self, req, image_id): """Returns the list of metadata for a given instance.""" @@ -50,6 +53,7 @@ class ImageMetadataController(wsgi.Controller): metadata = self._get_image(context, image_id)['properties'] return dict(metadata=metadata) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((403, 404)) def show(self, req, image_id, id): context = req.environ['nova.context'] @@ -59,6 +63,7 @@ class ImageMetadataController(wsgi.Controller): else: raise exc.HTTPNotFound() + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((400, 403, 404)) @validation.schema(image_metadata.create) def create(self, req, image_id, body): @@ -75,6 +80,7 @@ class ImageMetadataController(wsgi.Controller): raise exc.HTTPForbidden(explanation=e.format_message()) return dict(metadata=image['properties']) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((400, 403, 404)) @validation.schema(image_metadata.update) def update(self, req, image_id, id, body): @@ -97,6 +103,7 @@ class ImageMetadataController(wsgi.Controller): raise exc.HTTPForbidden(explanation=e.format_message()) return dict(meta=meta) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((400, 403, 404)) @validation.schema(image_metadata.update_all) def update_all(self, req, image_id, body): @@ -112,6 +119,7 @@ class ImageMetadataController(wsgi.Controller): raise exc.HTTPForbidden(explanation=e.format_message()) return dict(metadata=metadata) + @wsgi.Controller.api_version("2.1", MAX_IMAGE_META_PROXY_API_VERSION) @extensions.expected_errors((403, 404)) @wsgi.response(204) def delete(self, req, image_id, id): diff --git a/nova/api/openstack/compute/limits.py b/nova/api/openstack/compute/limits.py index 6027d9ed74..3d77ad7510 100644 --- a/nova/api/openstack/compute/limits.py +++ b/nova/api/openstack/compute/limits.py @@ -13,8 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. +from nova.api.openstack.api_version_request \ + import MAX_IMAGE_META_PROXY_API_VERSION from nova.api.openstack.api_version_request \ import MAX_PROXY_API_SUPPORT_VERSION +from nova.api.openstack.api_version_request \ + import MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION from nova.api.openstack.api_version_request \ import MIN_WITHOUT_PROXY_API_SUPPORT_VERSION from nova.api.openstack.compute.views import limits as limits_views @@ -36,12 +40,19 @@ class LimitsController(wsgi.Controller): def index(self, req): return self._index(req) - @wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa + @wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, # noqa + MAX_IMAGE_META_PROXY_API_VERSION) # noqa @extensions.expected_errors(()) def index(self, req): return self._index(req, filter_result=True) - def _index(self, req, filter_result=False): + @wsgi.Controller.api_version( # noqa + MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION) # noqa + @extensions.expected_errors(()) + def index(self, req): + return self._index(req, filter_result=True, max_image_meta=False) + + def _index(self, req, filter_result=False, max_image_meta=True): """Return all global limit information.""" context = req.environ['nova.context'] context.can(limits_policies.BASE_POLICY_NAME) @@ -51,7 +62,8 @@ class LimitsController(wsgi.Controller): abs_limits = {k: v['limit'] for k, v in quotas.items()} builder = self._get_view_builder(req) - return builder.build(abs_limits, filter_result=filter_result) + return builder.build(abs_limits, filter_result=filter_result, + max_image_meta=max_image_meta) def _get_view_builder(self, req): return limits_views.ViewBuilderV21() diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 314e40a8aa..eaf1e6d269 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -1053,7 +1053,11 @@ class ServersController(wsgi.Controller): image_name = common.normalize_name(entity["name"]) metadata = entity.get('metadata', {}) - common.check_img_metadata_properties_quota(context, metadata) + # Starting from microversion 2.39 we don't check quotas on createImage + if api_version_request.is_supported( + req, max_version= + api_version_request.MAX_IMAGE_META_PROXY_API_VERSION): + common.check_img_metadata_properties_quota(context, metadata) instance = self._get_server(context, req, id) diff --git a/nova/api/openstack/compute/views/limits.py b/nova/api/openstack/compute/views/limits.py index 79610677f3..3955032905 100644 --- a/nova/api/openstack/compute/views/limits.py +++ b/nova/api/openstack/compute/views/limits.py @@ -41,9 +41,10 @@ class ViewBuilder(object): "security_group_rules": ["maxSecurityGroupRules"], } - def build(self, absolute_limits, filter_result=False): + def build(self, absolute_limits, filter_result=False, max_image_meta=True): absolute_limits = self._build_absolute_limits( - absolute_limits, filter_result=filter_result) + absolute_limits, filter_result=filter_result, + max_image_meta=max_image_meta) output = { "limits": { @@ -54,7 +55,8 @@ class ViewBuilder(object): return output - def _build_absolute_limits(self, absolute_limits, filter_result=False): + def _build_absolute_limits(self, absolute_limits, filter_result=False, + max_image_meta=True): """Builder for absolute limits absolute_limits should be given as a dict of limits. @@ -69,6 +71,8 @@ class ViewBuilder(object): if (name in self.limit_names and value is not None and name not in filtered_limits): for limit_name in self.limit_names[name]: + if not max_image_meta and limit_name == "maxImageMeta": + continue limits[limit_name] = value return limits diff --git a/nova/api/openstack/rest_api_version_history.rst b/nova/api/openstack/rest_api_version_history.rst index 3b42bce13e..5cbafd1988 100644 --- a/nova/api/openstack/rest_api_version_history.rst +++ b/nova/api/openstack/rest_api_version_history.rst @@ -415,3 +415,14 @@ user documentation. should not be accepted. From this version of the API admin as well as non admin user will get 400 HTTPBadRequest if invalid status is passed to nova list command. + +2.39 +---- + + Deprecates image-metadata proxy API that is just a proxy for Glance API + to operate the image metadata. Also removes the extra quota enforcement with + Nova `metadata` quota (quota checks for 'createImage' and 'createBackup' + actions in Nova were removed). After this version Glance configuration + option `image_property_quota` should be used to control the quota of + image metadatas. Also, removes the `maxImageMeta` field from `os-limits` + API response. diff --git a/nova/tests/functional/api_sample_tests/api_samples/limits/v2.39/limit-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/limits/v2.39/limit-get-resp.json.tpl new file mode 100644 index 0000000000..68b29eacd6 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/limits/v2.39/limit-get-resp.json.tpl @@ -0,0 +1,20 @@ +{ + "limits": { + "absolute": { + "maxPersonality": 5, + "maxPersonalitySize": 10240, + "maxServerMeta": 128, + "maxTotalCores": 20, + "maxTotalInstances": 10, + "maxTotalKeypairs": 100, + "maxTotalRAMSize": 51200, + "maxServerGroups": 10, + "maxServerGroupMembers": 10, + "totalCoresUsed": 0, + "totalInstancesUsed": 0, + "totalRAMUsed": 0, + "totalServerGroupsUsed": 0 + }, + "rate": [] + } +} diff --git a/nova/tests/functional/api_sample_tests/test_limits.py b/nova/tests/functional/api_sample_tests/test_limits.py index ab20aa7bd8..939b79603e 100644 --- a/nova/tests/functional/api_sample_tests/test_limits.py +++ b/nova/tests/functional/api_sample_tests/test_limits.py @@ -48,3 +48,20 @@ class LimitsV236Test(api_sample_base.ApiSampleTestBaseV21): self.api.microversion = self.microversion response = self._do_get('limits') self._verify_response('limit-get-resp', {}, response, 200) + + +class LimitsV239Test(api_sample_base.ApiSampleTestBaseV21): + """Test limits don't return 'maxImageMeta' field after 2.39. + + We dropped the image-metadata proxy API in 2.39, which also means that we + shouldn't be returning 'maxImageMeta' field in 'os-limits' response. + + """ + sample_dir = "limits" + microversion = '2.39' + scenarios = [('v2_39', {'api_major_version': 'v2.1'})] + + def test_limits_get(self): + self.api.microversion = self.microversion + response = self._do_get('limits') + self._verify_response('limit-get-resp', {}, response, 200) diff --git a/nova/tests/unit/api/openstack/compute/test_create_backup.py b/nova/tests/unit/api/openstack/compute/test_create_backup.py index d3fca0d285..fe5a3d57c0 100644 --- a/nova/tests/unit/api/openstack/compute/test_create_backup.py +++ b/nova/tests/unit/api/openstack/compute/test_create_backup.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import mock import webob from nova.api.openstack import common @@ -352,3 +353,34 @@ class CreateBackupPolicyEnforcementv21(test.NoDBTestCase): self.assertEqual( "Policy doesn't allow %s to be performed." % rule_name, exc.format_message()) + + +class CreateBackupTestsV239(test.NoDBTestCase): + + def setUp(self): + super(CreateBackupTestsV239, self).setUp() + self.controller = create_backup_v21.CreateBackupController() + self.req = fakes.HTTPRequest.blank('', version='2.39') + + @mock.patch.object(common, 'check_img_metadata_properties_quota') + @mock.patch.object(common, 'get_instance') + def test_create_backup_no_quota_checks(self, mock_get_instance, + mock_check_quotas): + # 'mock_get_instance' helps to skip the whole logic of the action, + # but to make the test + mock_get_instance.side_effect = webob.exc.HTTPNotFound + metadata = {'123': 'asdf'} + body = { + 'createBackup': { + 'name': 'Backup 1', + 'backup_type': 'daily', + 'rotation': 1, + 'metadata': metadata, + }, + } + self.assertRaises(webob.exc.HTTPNotFound, + self.controller._create_backup, self.req, + fakes.FAKE_UUID, body=body) + # starting from version 2.39 no quota checks on Nova side are performed + # for 'createBackup' action after removing 'image-metadata' proxy API + mock_check_quotas.assert_not_called() diff --git a/nova/tests/unit/api/openstack/compute/test_image_metadata.py b/nova/tests/unit/api/openstack/compute/test_image_metadata.py index 4a482ef15c..fb2903a4e2 100644 --- a/nova/tests/unit/api/openstack/compute/test_image_metadata.py +++ b/nova/tests/unit/api/openstack/compute/test_image_metadata.py @@ -348,3 +348,28 @@ class ImageMetaDataTestV21(test.NoDBTestCase): self.assertRaises(webob.exc.HTTPForbidden, self.controller.create, req, image_id, body=body) + + +class ImageMetadataControllerV239(test.NoDBTestCase): + + def setUp(self): + super(ImageMetadataControllerV239, self).setUp() + self.controller = image_metadata_v21.ImageMetadataController() + self.req = fakes.HTTPRequest.blank('', version='2.39') + + def test_not_found_for_all_image_metadata_api(self): + self.assertRaises(exception.VersionNotFoundForAPIMethod, + self.controller.index, self.req) + self.assertRaises(exception.VersionNotFoundForAPIMethod, + self.controller.show, self.req, fakes.FAKE_UUID) + self.assertRaises(exception.VersionNotFoundForAPIMethod, + self.controller.create, self.req, + fakes.FAKE_UUID, {'metadata': {}}) + self.assertRaises(exception.VersionNotFoundForAPIMethod, + self.controller.update, self.req, + fakes.FAKE_UUID, 'id', {'metadata': {}}) + self.assertRaises(exception.VersionNotFoundForAPIMethod, + self.controller.update_all, self.req, + fakes.FAKE_UUID, {'metadata': {}}) + self.assertRaises(exception.VersionNotFoundForAPIMethod, + self.controller.delete, self.req, fakes.FAKE_UUID) diff --git a/nova/tests/unit/api/openstack/compute/test_limits.py b/nova/tests/unit/api/openstack/compute/test_limits.py index e588060f60..46190d95a1 100644 --- a/nova/tests/unit/api/openstack/compute/test_limits.py +++ b/nova/tests/unit/api/openstack/compute/test_limits.py @@ -275,3 +275,36 @@ class LimitsControllerTestV236(BaseLimitTestSuite): }, } self.assertEqual(expected_response, response) + + +class LimitsControllerTestV239(BaseLimitTestSuite): + + def setUp(self): + super(LimitsControllerTestV239, self).setUp() + self.controller = limits_v21.LimitsController() + self.req = fakes.HTTPRequest.blank("/?tenant_id=faketenant", + version='2.39') + + def test_index_filtered_no_max_image_meta(self): + absolute_limits = { + "metadata_items": 1, + } + + def _get_project_quotas(context, project_id, usages=True): + return {k: dict(limit=v) for k, v in absolute_limits.items()} + + with mock.patch('nova.quota.QUOTAS.get_project_quotas') as \ + get_project_quotas: + get_project_quotas.side_effect = _get_project_quotas + response = self.controller.index(self.req) + # staring from version 2.39 there is no 'maxImageMeta' field + # in response after removing 'image-metadata' proxy API + expected_response = { + "limits": { + "rate": [], + "absolute": { + "maxServerMeta": 1, + }, + }, + } + self.assertEqual(expected_response, response) diff --git a/nova/tests/unit/api/openstack/compute/test_serversV21.py b/nova/tests/unit/api/openstack/compute/test_serversV21.py index aa96d4687b..945120e040 100644 --- a/nova/tests/unit/api/openstack/compute/test_serversV21.py +++ b/nova/tests/unit/api/openstack/compute/test_serversV21.py @@ -4832,3 +4832,31 @@ class ServersPolicyEnforcementV21(test.NoDBTestCase): "os_compute_api:servers:create:attach_volume": "@", rule_name: "project:non_fake"} self._create_policy_check(rules, rule_name) + + +class ServersActionsJsonTestV239(test.NoDBTestCase): + + def setUp(self): + super(ServersActionsJsonTestV239, self).setUp() + ext_info = extension_info.LoadedExtensionInfo() + self.controller = servers.ServersController(extension_info=ext_info) + self.req = fakes.HTTPRequest.blank('', version='2.39') + + @mock.patch.object(common, 'check_img_metadata_properties_quota') + @mock.patch.object(common, 'get_instance') + def test_server_create_image_no_quota_checks(self, mock_get_instance, + mock_check_quotas): + # 'mock_get_instance' helps to skip the whole logic of the action, + # but to make the test + mock_get_instance.side_effect = webob.exc.HTTPNotFound + body = { + 'createImage': { + 'name': 'Snapshot 1', + }, + } + self.assertRaises(webob.exc.HTTPNotFound, + self.controller._action_create_image, self.req, + FAKE_UUID, body=body) + # starting from version 2.39 no quota checks on Nova side are performed + # for 'createImage' action after removing 'image-metadata' proxy API + mock_check_quotas.assert_not_called() diff --git a/releasenotes/notes/bp-deprecate-image-meta-proxy-api-7f21e1e6a94944ee.yaml b/releasenotes/notes/bp-deprecate-image-meta-proxy-api-7f21e1e6a94944ee.yaml new file mode 100644 index 0000000000..1e357d1c12 --- /dev/null +++ b/releasenotes/notes/bp-deprecate-image-meta-proxy-api-7f21e1e6a94944ee.yaml @@ -0,0 +1,9 @@ +--- +deprecations: + - Implemented microversion v2.39 that deprecates `image-metadata` proxy API, + removes image metadata quota checks for 'createImage' and 'createBackup' + actions. + + After this version Glance configuration option `image_property_quota` + should be used to control the quota of image metadatas. Also, removes the + `maxImageMeta` field from `os-limits` API response.