- limits: limits
- absolute: limits_absolutes
- - maxImageMeta: metadata_items
+ - maxImageMeta: image_metadata_items
- maxPersonality: injected_files
- maxPersonalitySize: injected_file_content_bytes
- maxSecurityGroupRules: security_group_rules
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.
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.
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),
--- /dev/null
+{
+ "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": []
+ }
+}
}
],
"status": "CURRENT",
- "version": "2.38",
+ "version": "2.39",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}
}
],
"status": "CURRENT",
- "version": "2.38",
+ "version": "2.39",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}
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
# 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
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
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)
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
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."""
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']
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):
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):
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):
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):
# 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
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)
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()
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)
"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": {
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.
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
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.
--- /dev/null
+{
+ "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": []
+ }
+}
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)
# License for the specific language governing permissions and limitations
# under the License.
+import mock
import webob
from nova.api.openstack import common
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()
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)
},
}
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)
"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()
--- /dev/null
+---
+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.