Add quotas for fixed ips.

DocImpact: there is now a default quota of 10 fixed ips per tenant.
This will need to be adjusted by deployers if that number does not
meet their needs.

Resolves bug 1125468 for essex.

Change-Id: I2a5afaa47afb182f4917cd43e1ebd0d6cd1330e3
This commit is contained in:
Michael Still 2013-03-15 03:45:41 +11:00
parent b683ced5df
commit efaacdaee1
7 changed files with 67 additions and 6 deletions

View File

@ -30,8 +30,9 @@ authorize = extensions.extension_authorizer('compute', 'quotas')
quota_resources = ['metadata_items', 'injected_file_content_bytes',
'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances',
'injected_files', 'cores', 'security_groups', 'security_group_rules']
'volumes', 'gigabytes', 'ram', 'floating_ips', 'fixed_ips',
'instances', 'injected_files', 'cores', 'security_groups',
'security_group_rules']
class QuotaTemplate(xmlutil.TemplateBuilder):

View File

@ -461,6 +461,12 @@ def fixed_ip_update(context, address, values):
"""Create a fixed ip from the values dictionary."""
return IMPL.fixed_ip_update(context, address, values)
def fixed_ip_count_by_project(context, project_id, session=None):
"""Count fixed ips used by project."""
return IMPL.fixed_ip_count_by_project(context, project_id,
session=session)
####################

View File

@ -1114,6 +1114,27 @@ def fixed_ip_update(context, address, values):
fixed_ip_ref.save(session=session)
@require_context
def fixed_ip_count_by_project(context, project_id, session=None):
authorize_project_context(context, project_id)
# NOTE(mikal): Yes I know this is horrible, but I couldn't
# get a query using a join working, mainly because of a failure
# to be able to express the where clause sensibly. Patches
# welcome.
session = get_session()
with session.begin():
instance_id_query = model_query(context, models.Instance.id,
read_deleted="no", session=session).\
filter(models.Instance.project_id == \
project_id)
id_filter = models.FixedIp.instance_id.in_(instance_id_query)
return model_query(context, models.FixedIp, read_deleted="no",
session=session).\
filter(id_filter).\
count()
###################

View File

@ -1152,6 +1152,12 @@ class NetworkManager(manager.SchedulerDependentManager):
def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
"""Gets a fixed ip from the pool."""
LOG.debug("QUOTA: %s" % quota.allowed_fixed_ips(context, 1))
if quota.allowed_fixed_ips(context, 1) < 1:
LOG.warn(_('Quota exceeded for %s, tried to allocate address'),
context.project_id)
raise exception.QuotaError(code='FixedAddressLimitExceeded')
# TODO(vish): when this is called by compute, we can associate compute
# with a network, or a cluster of computes with a network
# and use that network here with a method like

View File

@ -42,6 +42,10 @@ quota_opts = [
cfg.IntOpt('quota_floating_ips',
default=10,
help='number of floating ips allowed per project'),
cfg.IntOpt('quota_fixed_ips',
default=10,
help=('number of fixed ips allowed per project (this should be '
'at least the number of instances allowed)')),
cfg.IntOpt('quota_metadata_items',
default=128,
help='number of metadata items allowed per instance'),
@ -74,6 +78,7 @@ def _get_default_quotas():
'volumes': FLAGS.quota_volumes,
'gigabytes': FLAGS.quota_gigabytes,
'floating_ips': FLAGS.quota_floating_ips,
'fixed_ips': FLAGS.quota_fixed_ips,
'metadata_items': FLAGS.quota_metadata_items,
'injected_files': FLAGS.quota_max_injected_files,
'injected_file_content_bytes':
@ -173,6 +178,18 @@ def allowed_floating_ips(context, requested_floating_ips):
return min(requested_floating_ips, allowed_floating_ips)
def allowed_fixed_ips(context, requested_fixed_ips):
"""Check quota and return min(requested, allowed) fixed ips."""
project_id = context.project_id
context = context.elevated()
used_fixed_ips = db.fixed_ip_count_by_project(context, project_id)
quota = get_project_quotas(context, project_id)
allowed_fixed_ips = _get_request_allotment(requested_fixed_ips,
used_fixed_ips,
quota['fixed_ips'])
return min(requested_fixed_ips, allowed_fixed_ips)
def allowed_security_groups(context, requested_security_groups):
"""Check quota and return min(requested, allowed) security groups."""
project_id = context.project_id

View File

@ -27,8 +27,8 @@ from nova.tests.api.openstack import fakes
def quota_set(id):
return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10,
'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
'instances': 10, 'injected_files': 5, 'cores': 20,
'injected_file_content_bytes': 10240,
'fixed_ips': 10, 'instances': 10, 'injected_files': 5,
'cores': 20, 'injected_file_content_bytes': 10240,
'security_groups': 10, 'security_group_rules': 20}}
@ -50,6 +50,8 @@ class QuotaSetsTest(test.TestCase):
'ram': 51200,
'volumes': 10,
'floating_ips': 10,
'fixed_ips': 10,
'fixed_ips': 10,
'metadata_items': 128,
'gigabytes': 1000,
'injected_files': 5,
@ -69,6 +71,7 @@ class QuotaSetsTest(test.TestCase):
self.assertEqual(qs['volumes'], 10)
self.assertEqual(qs['gigabytes'], 1000)
self.assertEqual(qs['floating_ips'], 10)
self.assertEqual(qs['fixed_ips'], 10)
self.assertEqual(qs['metadata_items'], 128)
self.assertEqual(qs['injected_files'], 5)
self.assertEqual(qs['injected_file_content_bytes'], 10240)
@ -89,6 +92,7 @@ class QuotaSetsTest(test.TestCase):
'volumes': 10,
'gigabytes': 1000,
'floating_ips': 10,
'fixed_ips': 10,
'metadata_items': 128,
'injected_files': 5,
'injected_file_content_bytes': 10240,
@ -114,7 +118,8 @@ class QuotaSetsTest(test.TestCase):
body = {'quota_set': {'instances': 50, 'cores': 50,
'ram': 51200, 'volumes': 10,
'gigabytes': 1000, 'floating_ips': 10,
'metadata_items': 128, 'injected_files': 5,
'fixed_ips': 10, 'metadata_items': 128,
'injected_files': 5,
'injected_file_content_bytes': 10240,
'security_groups': 10,
'security_group_rules': 20}}
@ -129,7 +134,8 @@ class QuotaSetsTest(test.TestCase):
body = {'quota_set': {'instances': 50, 'cores': 50,
'ram': 51200, 'volumes': 10,
'gigabytes': 1000, 'floating_ips': 10,
'metadata_items': 128, 'injected_files': 5,
'fixed_ips': 10, 'metadata_items': 128,
'injected_files': 5,
'injected_file_content_bytes': 10240,
'security_groups': 10,
'security_group_rules': 20}}
@ -154,6 +160,7 @@ class QuotaXMLSerializerTest(test.TestCase):
gigabytes=40,
ram=50,
floating_ips=60,
fixed_ips=10,
instances=70,
injected_files=80,
security_groups=10,
@ -179,6 +186,7 @@ class QuotaXMLSerializerTest(test.TestCase):
gigabytes='40',
ram='50',
floating_ips='60',
fixed_ips='10',
instances='70',
injected_files='80',
security_groups='10',
@ -193,6 +201,7 @@ class QuotaXMLSerializerTest(test.TestCase):
'<gigabytes>40</gigabytes>'
'<ram>50</ram>'
'<floating_ips>60</floating_ips>'
'<fixed_ips>10</fixed_ips>'
'<instances>70</instances>'
'<injected_files>80</injected_files>'
'<security_groups>10</security_groups>'

View File

@ -432,6 +432,7 @@ class VlanNetworkTestCase(test.TestCase):
db.instance_get(mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({'security_groups':
[{'id': 0}]})
db.fixed_ip_associate_pool(mox.IgnoreArg(),
mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn('192.168.0.1')