[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:
parent
6a6f40e991
commit
df6e2d37f2
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
|
20
doc/api_samples/limits/v2.39/limit-get-resp.json
Normal file
20
doc/api_samples/limits/v2.39/limit-get-resp.json
Normal 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": []
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.38",
|
||||
"version": "2.39",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.38",
|
||||
"version": "2.39",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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": []
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user