[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
This commit is contained in:
Pavel Kholkin 2016-12-01 18:37:28 +03:00
parent 6a6f40e991
commit df6e2d37f2
21 changed files with 263 additions and 13 deletions

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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),

View File

@ -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": []
}
}

View File

@ -19,7 +19,7 @@
}
],
"status": "CURRENT",
"version": "2.38",
"version": "2.39",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}

View File

@ -22,7 +22,7 @@
}
],
"status": "CURRENT",
"version": "2.38",
"version": "2.39",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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": []
}
}

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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.