Support ram quota
Set default to -1 so this is backwards compatible. Existing installations will need to manully backfill quote usage for this to work as expected. Story: 2008293 Task: 41172 Change-Id: I455477a2e7a00f0d132971a2a684352967ac19b9
This commit is contained in:
parent
78772cef68
commit
5be23d1b20
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added the ability to quota on total amount of RAM in MB used per project.
|
||||
Set ``quota.max_ram_per_tenant`` to enable. Default is -1 (unlimited)
|
||||
to be backwards compatible. Existing installations will need to manually
|
||||
backfill quote usage for this to work as expected.
|
@ -221,6 +221,9 @@ common_opts = [
|
||||
default=10,
|
||||
help='Default maximum number of instances per tenant.',
|
||||
deprecated_name='max_instances_per_user'),
|
||||
cfg.IntOpt('max_ram_per_tenant',
|
||||
default=-1,
|
||||
help='Default maximum total amount of RAM in MB per tenant.'),
|
||||
cfg.IntOpt('max_accepted_volume_size', default=10,
|
||||
help='Default maximum volume size (in GB) for an instance.'),
|
||||
cfg.IntOpt('max_volumes_per_tenant', default=40,
|
||||
|
@ -716,8 +716,8 @@ class BaseInstance(SimpleInstance):
|
||||
self.update_db(task_status=InstanceTasks.DELETING,
|
||||
configuration_id=None)
|
||||
task_api.API(self.context).delete_instance(self.id)
|
||||
|
||||
deltas = {'instances': -1}
|
||||
flavor = self.get_flavor()
|
||||
deltas = {'instances': -1, 'ram': -flavor.ram}
|
||||
if self.volume_support:
|
||||
deltas['volumes'] = -self.volume_size
|
||||
return run_with_quotas(self.tenant_id,
|
||||
@ -913,6 +913,9 @@ class BaseInstance(SimpleInstance):
|
||||
except exception.ModelNotFoundError:
|
||||
pass
|
||||
|
||||
def get_flavor(self):
|
||||
return self.nova_client.flavors.get(self.flavor_id)
|
||||
|
||||
@property
|
||||
def volume_client(self):
|
||||
if not self._volume_client:
|
||||
@ -1153,7 +1156,7 @@ class Instance(BuiltInstance):
|
||||
cls._validate_remote_datastore(context, region_name, flavor,
|
||||
datastore, datastore_version)
|
||||
|
||||
deltas = {'instances': 1}
|
||||
deltas = {'instances': 1, 'ram': flavor.ram}
|
||||
if volume_support:
|
||||
if replica_source:
|
||||
try:
|
||||
@ -1351,9 +1354,6 @@ class Instance(BuiltInstance):
|
||||
module_models.InstanceModule.create(
|
||||
context, instance_id, module.id, module.md5)
|
||||
|
||||
def get_flavor(self):
|
||||
return self.nova_client.flavors.get(self.flavor_id)
|
||||
|
||||
def get_default_configuration_template(self):
|
||||
flavor = self.get_flavor()
|
||||
LOG.debug("Getting default config template for datastore version "
|
||||
@ -1371,13 +1371,13 @@ class Instance(BuiltInstance):
|
||||
if self.db_info.cluster_id is not None:
|
||||
raise exception.ClusterInstanceOperationNotSupported()
|
||||
|
||||
# Validate that the old and new flavor IDs are not the same, new flavor
|
||||
# can be found and has ephemeral/volume support if required by the
|
||||
# current flavor.
|
||||
# Validate that the old and new flavor IDs are not the same, new
|
||||
# flavor can be found and has ephemeral/volume support if required
|
||||
# by the current flavor.
|
||||
if self.flavor_id == new_flavor_id:
|
||||
raise exception.BadRequest(_("The new flavor id must be different "
|
||||
"than the current flavor id of '%s'.")
|
||||
% self.flavor_id)
|
||||
raise exception.BadRequest(
|
||||
_("The new flavor id must be different "
|
||||
"than the current flavor id of '%s'.") % self.flavor_id)
|
||||
try:
|
||||
new_flavor = self.nova_client.flavors.get(new_flavor_id)
|
||||
except nova_exceptions.NotFound:
|
||||
@ -1390,13 +1390,20 @@ class Instance(BuiltInstance):
|
||||
elif self.device_path is not None:
|
||||
# ephemeral support enabled
|
||||
if new_flavor.ephemeral == 0:
|
||||
raise exception.LocalStorageNotSpecified(flavor=new_flavor_id)
|
||||
raise exception.LocalStorageNotSpecified(
|
||||
flavor=new_flavor_id)
|
||||
|
||||
# Set the task to RESIZING and begin the async call before returning.
|
||||
self.update_db(task_status=InstanceTasks.RESIZING)
|
||||
LOG.debug("Instance %s set to RESIZING.", self.id)
|
||||
task_api.API(self.context).resize_flavor(self.id, old_flavor,
|
||||
new_flavor)
|
||||
def _resize_flavor():
|
||||
# Set the task to RESIZING and begin the async call before
|
||||
# returning.
|
||||
self.update_db(task_status=InstanceTasks.RESIZING)
|
||||
LOG.debug("Instance %s set to RESIZING.", self.id)
|
||||
task_api.API(self.context).resize_flavor(self.id, old_flavor,
|
||||
new_flavor)
|
||||
|
||||
return run_with_quotas(self.tenant_id,
|
||||
{'ram': new_flavor.ram - old_flavor.ram},
|
||||
_resize_flavor)
|
||||
|
||||
def resize_volume(self, new_size):
|
||||
"""Resize instance volume.
|
||||
|
@ -75,6 +75,7 @@ class Resource(object):
|
||||
"""Describe a single resource for quota checking."""
|
||||
|
||||
INSTANCES = 'instances'
|
||||
RAM = 'ram'
|
||||
VOLUMES = 'volumes'
|
||||
BACKUPS = 'backups'
|
||||
|
||||
|
@ -349,6 +349,7 @@ QUOTAS = QuotaEngine()
|
||||
''' Define all kind of resources here '''
|
||||
|
||||
resources = [Resource(Resource.INSTANCES, 'max_instances_per_tenant'),
|
||||
Resource(Resource.RAM, 'max_ram_per_tenant'),
|
||||
Resource(Resource.BACKUPS, 'max_backups_per_tenant'),
|
||||
Resource(Resource.VOLUMES, 'max_volumes_per_tenant')]
|
||||
|
||||
|
@ -39,6 +39,7 @@ DEFAULT_RATE = CONF.http_get_rate
|
||||
DEFAULT_MAX_VOLUMES = CONF.max_volumes_per_tenant
|
||||
DEFAULT_MAX_INSTANCES = CONF.max_instances_per_tenant
|
||||
DEFAULT_MAX_BACKUPS = CONF.max_backups_per_tenant
|
||||
DEFAULT_MAX_RAM = CONF.max_ram_per_tenant
|
||||
|
||||
|
||||
def ensure_limits_are_not_faked(func):
|
||||
@ -109,6 +110,7 @@ class Limits(object):
|
||||
assert_equal(int(abs_limits.max_instances), DEFAULT_MAX_INSTANCES)
|
||||
assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS)
|
||||
assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES)
|
||||
assert_equal(int(abs_limits.max_ram), DEFAULT_MAX_RAM)
|
||||
|
||||
for k in d:
|
||||
assert_equal(d[k].verb, k)
|
||||
@ -132,6 +134,7 @@ class Limits(object):
|
||||
assert_equal(int(abs_limits.max_instances), DEFAULT_MAX_INSTANCES)
|
||||
assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS)
|
||||
assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES)
|
||||
assert_equal(int(abs_limits.max_ram), DEFAULT_MAX_RAM)
|
||||
assert_equal(get.verb, "GET")
|
||||
assert_equal(get.unit, "MINUTE")
|
||||
assert_true(int(get.remaining) <= DEFAULT_RATE - 5)
|
||||
@ -163,6 +166,8 @@ class Limits(object):
|
||||
DEFAULT_MAX_BACKUPS)
|
||||
assert_equal(int(abs_limits.max_volumes),
|
||||
DEFAULT_MAX_VOLUMES)
|
||||
assert_equal(int(abs_limits.max_ram,),
|
||||
DEFAULT_MAX_RAM)
|
||||
|
||||
except exceptions.OverLimit:
|
||||
encountered = True
|
||||
|
@ -48,6 +48,7 @@ class BaseLimitTestSuite(trove_testtools.TestCase):
|
||||
self.context = trove_testtools.TroveTestContext(self)
|
||||
self.absolute_limits = {"max_instances": 55,
|
||||
"max_volumes": 100,
|
||||
"max_ram": 200,
|
||||
"max_backups": 40}
|
||||
|
||||
|
||||
@ -114,6 +115,10 @@ class LimitsControllerTest(BaseLimitTestSuite):
|
||||
resource="instances",
|
||||
hard_limit=100),
|
||||
|
||||
"ram": Quota(tenant_id=tenant_id,
|
||||
resource="ram",
|
||||
hard_limit=200),
|
||||
|
||||
"backups": Quota(tenant_id=tenant_id,
|
||||
resource="backups",
|
||||
hard_limit=40),
|
||||
@ -135,6 +140,7 @@ class LimitsControllerTest(BaseLimitTestSuite):
|
||||
{
|
||||
'max_instances': 100,
|
||||
'max_backups': 40,
|
||||
'max_ram': 200,
|
||||
'verb': 'ABSOLUTE',
|
||||
'max_volumes': 55
|
||||
},
|
||||
@ -798,7 +804,7 @@ class LimitsViewsTest(trove_testtools.TestCase):
|
||||
"resetTime": 1311272226
|
||||
}
|
||||
]
|
||||
abs_view = {"instances": 55, "volumes": 100, "backups": 40}
|
||||
abs_view = {"instances": 55, "volumes": 100, "backups": 40, 'ram': 200}
|
||||
|
||||
view_data = views.LimitViews(abs_view, rate_limits)
|
||||
self.assertIsNotNone(view_data)
|
||||
@ -806,6 +812,7 @@ class LimitsViewsTest(trove_testtools.TestCase):
|
||||
data = view_data.data()
|
||||
expected = {'limits': [{'max_instances': 55,
|
||||
'max_backups': 40,
|
||||
'max_ram': 200,
|
||||
'verb': 'ABSOLUTE',
|
||||
'max_volumes': 100},
|
||||
{'regex': '.*',
|
||||
|
Loading…
Reference in New Issue
Block a user