diff options
author | John Garbutt <john@johngarbutt.com> | 2020-03-10 10:25:42 +0000 |
---|---|---|
committer | melanie witt <melwittt@gmail.com> | 2022-02-24 16:21:02 +0000 |
commit | 4207493829a1b1877f643c4a49cd2e079f23859d (patch) | |
tree | 548c2128e594b957caa62a295a0d770d371cc8a7 /nova/compute/api.py | |
parent | 3b69f959a848bad257f186f491111658b25f24c7 (diff) | |
download | nova-4207493829a1b1877f643c4a49cd2e079f23859d.tar.gz |
Enforce api and db limits
When using unified limits, we add enforcement of those limits on all
related API calls. Note: we do not yet correctly report the configured
limits to users via the quota APIs, that is in a future patch.
Note the unified limits calls are made alongside the existing legacy
quota calls. The old quota calls will be handed by the quota engine
driver, that is basically a no-op. This is to make it easier to remove
the legacy code paths in the future.
Note, over quota exceptions raised with unified limits use the standard
(improved) exception message as those raised by oslo.limit. They
however do use the existing exception code to ease integration. The
user of the API will see the same return codes, no matter which code is
enabled to enforce the limits.
Finally, this also adds test coverage where it was missing. Coverage
for "quota recheck" behavior in KeypairAPI is added where all other
KeypairAPI testing is located. Duplicate coverage is removed from
nova/api/openstack/compute/test_keypairs.py at the same time.
blueprint unified-limits-nova
Change-Id: I36e82a17579158063396d7e55b495ccff4959ceb
Diffstat (limited to 'nova/compute/api.py')
-rw-r--r-- | nova/compute/api.py | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py index b25af9f3e1..173ac2f382 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -59,6 +59,7 @@ from nova import exception from nova import exception_wrapper from nova.i18n import _ from nova.image import glance +from nova.limit import local as local_limit from nova.network import constants from nova.network import model as network_model from nova.network import neutron @@ -409,6 +410,10 @@ class API: try: objects.Quotas.limit_check(context, injected_files=len(injected_files)) + local_limit.enforce_api_limit(local_limit.INJECTED_FILES, + len(injected_files)) + except exception.OnsetFileLimitExceeded: + raise except exception.OverQuota: raise exception.OnsetFileLimitExceeded() @@ -424,6 +429,16 @@ class API: objects.Quotas.limit_check(context, injected_file_path_bytes=max_path, injected_file_content_bytes=max_content) + # TODO(johngarbutt) we can simplify the except clause when + # the above legacy quota check is removed. + local_limit.enforce_api_limit( + local_limit.INJECTED_FILES_PATH, max_path) + local_limit.enforce_api_limit( + local_limit.INJECTED_FILES_CONTENT, max_content) + except exception.OnsetFilePathLimitExceeded: + raise + except exception.OnsetFileContentLimitExceeded: + raise except exception.OverQuota as exc: # Favor path limit over content limit for reporting # purposes @@ -444,6 +459,10 @@ class API: num_metadata = len(metadata) try: objects.Quotas.limit_check(context, metadata_items=num_metadata) + local_limit.enforce_api_limit( + local_limit.SERVER_METADATA_ITEMS, num_metadata) + except exception.MetadataLimitExceeded: + raise except exception.OverQuota as exc: quota_metadata = exc.kwargs['quotas']['metadata_items'] raise exception.MetadataLimitExceeded(allowed=quota_metadata) @@ -1451,6 +1470,11 @@ class API: objects.Quotas.check_deltas( context, {'server_group_members': 1}, instance_group, context.user_id) + local_limit.enforce_db_limit( + context, local_limit.SERVER_GROUP_MEMBERS, + entity_scope=instance_group.uuid, delta=1) + except exception.GroupMemberLimitExceeded: + raise except exception.OverQuota: msg = _("Quota exceeded, too many servers in " "group") @@ -1469,6 +1493,19 @@ class API: objects.Quotas.check_deltas( context, {'server_group_members': 0}, instance_group, context.user_id) + # TODO(johngarbutt): decide if we need this check + # The quota rechecking of limits is really just to + # protect against denial of service attacks that + # aim to fill up the database. Its usefulness could + # be debated. + local_limit.enforce_db_limit( + context, local_limit.SERVER_GROUP_MEMBERS, + entity_scope=instance_group.uuid, delta=0) + except exception.GroupMemberLimitExceeded: + with excutils.save_and_reraise_exception(): + objects.InstanceGroup._remove_members_in_db( + context, instance_group.id, + [instance.uuid]) except exception.OverQuota: objects.InstanceGroup._remove_members_in_db( context, instance_group.id, [instance.uuid]) @@ -6551,6 +6588,10 @@ class KeypairAPI: '1 and 255 characters long')) try: objects.Quotas.check_deltas(context, {'key_pairs': 1}, user_id) + local_limit.enforce_db_limit(context, local_limit.KEY_PAIRS, + entity_scope=user_id, delta=1) + except exception.KeypairLimitExceeded: + raise except exception.OverQuota: raise exception.KeypairLimitExceeded() @@ -6623,6 +6664,15 @@ class KeypairAPI: if CONF.quota.recheck_quota: try: objects.Quotas.check_deltas(context, {'key_pairs': 0}, user_id) + # TODO(johngarbutt) do we really need this recheck? + # The quota rechecking of limits is really just to protect + # against denial of service attacks that aim to fill up the + # database. Its usefulness could be debated. + local_limit.enforce_db_limit(context, local_limit.KEY_PAIRS, + entity_scope=user_id, delta=0) + except exception.KeypairLimitExceeded: + with excutils.save_and_reraise_exception(): + keypair.destroy() except exception.OverQuota: keypair.destroy() raise exception.KeypairLimitExceeded() |