Add image_count_total quota enforcement
This makes us enforce a quota on the total number of (non-deleted) images owned by a user. Partially-implements: blueprint glance-unified-quotas Change-Id: I8af124d9307263cd8289d0701fb9a745d13b1d56
This commit is contained in:
parent
d3d6a646e3
commit
a36666e2fe
@ -94,6 +94,7 @@ class ImagesController(object):
|
|||||||
image_factory = self.gateway.get_image_factory(req.context)
|
image_factory = self.gateway.get_image_factory(req.context)
|
||||||
image_repo = self.gateway.get_repo(req.context)
|
image_repo = self.gateway.get_repo(req.context)
|
||||||
try:
|
try:
|
||||||
|
ks_quota.enforce_image_count_total(req.context, req.context.owner)
|
||||||
image = image_factory.new_image(extra_properties=extra_properties,
|
image = image_factory.new_image(extra_properties=extra_properties,
|
||||||
tags=tags, **image)
|
tags=tags, **image)
|
||||||
image_repo.add(image)
|
image_repo.add(image)
|
||||||
|
@ -29,6 +29,7 @@ limit.opts.register_opts(CONF)
|
|||||||
|
|
||||||
QUOTA_IMAGE_SIZE_TOTAL = 'image_size_total'
|
QUOTA_IMAGE_SIZE_TOTAL = 'image_size_total'
|
||||||
QUOTA_IMAGE_STAGING_TOTAL = 'image_stage_total'
|
QUOTA_IMAGE_STAGING_TOTAL = 'image_stage_total'
|
||||||
|
QUOTA_IMAGE_COUNT_TOTAL = 'image_count_total'
|
||||||
|
|
||||||
|
|
||||||
def _enforce_some(context, project_id, quota_value_fns, deltas):
|
def _enforce_some(context, project_id, quota_value_fns, deltas):
|
||||||
@ -112,3 +113,15 @@ def enforce_image_staging_total(context, project_id, delta=0):
|
|||||||
context, project_id, QUOTA_IMAGE_STAGING_TOTAL,
|
context, project_id, QUOTA_IMAGE_STAGING_TOTAL,
|
||||||
lambda: db.user_get_staging_usage(context, project_id) // units.Mi,
|
lambda: db.user_get_staging_usage(context, project_id) // units.Mi,
|
||||||
delta=delta)
|
delta=delta)
|
||||||
|
|
||||||
|
|
||||||
|
def enforce_image_count_total(context, project_id):
|
||||||
|
"""Enforce the image_count_total quota.
|
||||||
|
|
||||||
|
This enforces the total count of non-deleted images owned by the
|
||||||
|
supplied project_id.
|
||||||
|
"""
|
||||||
|
_enforce_one(
|
||||||
|
context, project_id, QUOTA_IMAGE_COUNT_TOTAL,
|
||||||
|
lambda: db.user_get_image_count(context, project_id),
|
||||||
|
delta=1)
|
||||||
|
@ -7058,7 +7058,8 @@ class TestKeystoneQuotas(functional.SynchronousAPIBase):
|
|||||||
|
|
||||||
def test_upload(self):
|
def test_upload(self):
|
||||||
# Set a quota of 5MiB
|
# Set a quota of 5MiB
|
||||||
self.set_limit({'image_size_total': 5})
|
self.set_limit({'image_size_total': 5,
|
||||||
|
'image_count_total': 10})
|
||||||
self.start_server()
|
self.start_server()
|
||||||
|
|
||||||
# First upload of 3MiB is good
|
# First upload of 3MiB is good
|
||||||
@ -7081,7 +7082,8 @@ class TestKeystoneQuotas(functional.SynchronousAPIBase):
|
|||||||
|
|
||||||
def test_import(self):
|
def test_import(self):
|
||||||
# Set a quota of 5MiB
|
# Set a quota of 5MiB
|
||||||
self.set_limit({'image_size_total': 5})
|
self.set_limit({'image_size_total': 5,
|
||||||
|
'image_count_total': 10})
|
||||||
self.start_server()
|
self.start_server()
|
||||||
|
|
||||||
# First upload of 3MiB is good
|
# First upload of 3MiB is good
|
||||||
@ -7103,7 +7105,8 @@ class TestKeystoneQuotas(functional.SynchronousAPIBase):
|
|||||||
|
|
||||||
def test_import_would_go_over(self):
|
def test_import_would_go_over(self):
|
||||||
# Set a quota limit of 5MiB
|
# Set a quota limit of 5MiB
|
||||||
self.set_limit({'image_size_total': 5})
|
self.set_limit({'image_size_total': 5,
|
||||||
|
'image_count_total': 10})
|
||||||
self.start_server()
|
self.start_server()
|
||||||
|
|
||||||
# First upload of 3MiB is good
|
# First upload of 3MiB is good
|
||||||
@ -7142,6 +7145,7 @@ class TestKeystoneQuotas(functional.SynchronousAPIBase):
|
|||||||
def test_copy(self):
|
def test_copy(self):
|
||||||
# Set a size quota of 5MiB, with more staging quota than we need.
|
# Set a size quota of 5MiB, with more staging quota than we need.
|
||||||
self.set_limit({'image_size_total': 5,
|
self.set_limit({'image_size_total': 5,
|
||||||
|
'image_count_total': 10,
|
||||||
'image_stage_total': 15})
|
'image_stage_total': 15})
|
||||||
self.start_server()
|
self.start_server()
|
||||||
|
|
||||||
@ -7183,7 +7187,8 @@ class TestKeystoneQuotas(functional.SynchronousAPIBase):
|
|||||||
def test_stage(self):
|
def test_stage(self):
|
||||||
# Set a quota of 5MiB
|
# Set a quota of 5MiB
|
||||||
self.set_limit({'image_size_total': 15,
|
self.set_limit({'image_size_total': 15,
|
||||||
'image_stage_total': 5})
|
'image_stage_total': 5,
|
||||||
|
'image_count_total': 10})
|
||||||
self.start_server()
|
self.start_server()
|
||||||
|
|
||||||
# Stage 6MiB, which is allowed to complete, but leaves us over
|
# Stage 6MiB, which is allowed to complete, but leaves us over
|
||||||
@ -7213,3 +7218,25 @@ class TestKeystoneQuotas(functional.SynchronousAPIBase):
|
|||||||
# Stage should now succeed because we have freed up quota
|
# Stage should now succeed because we have freed up quota
|
||||||
self._create_and_stage(
|
self._create_and_stage(
|
||||||
data_iter=test_utils.FakeData(6 * units.Mi))
|
data_iter=test_utils.FakeData(6 * units.Mi))
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
# Set a quota of 2 images
|
||||||
|
self.set_limit({'image_size_total': 15,
|
||||||
|
'image_count_total': 2})
|
||||||
|
self.start_server()
|
||||||
|
|
||||||
|
# Create one image
|
||||||
|
image_id = self._create().json['id']
|
||||||
|
|
||||||
|
# Create a second. This leaves us *at* quota
|
||||||
|
self._create()
|
||||||
|
|
||||||
|
# Attempt to create a third is rejected as OverLimit
|
||||||
|
resp = self._create()
|
||||||
|
self.assertEqual(413, resp.status_code)
|
||||||
|
|
||||||
|
# Delete one image, which should put us under quota
|
||||||
|
self.api_delete('/v2/images/%s' % image_id)
|
||||||
|
|
||||||
|
# Now we can create that third image
|
||||||
|
self._create()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user