Skip to content

Commit

Permalink
Add quotas for fixed ips.
Browse files Browse the repository at this point in the history
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.

Change-Id: Iffa19583340f80cb2a13ba5fce31f7ff724a52d6
  • Loading branch information
mikalstill committed Mar 14, 2013
1 parent 9df61c0 commit 9942921
Show file tree
Hide file tree
Showing 31 changed files with 198 additions and 54 deletions.
@@ -1,6 +1,7 @@
{
"quota_class_set": {
"cores": 20,
"fixed_ips": 10,
"floating_ips": 10,
"id": "test_class",
"injected_file_content_bytes": 10240,
Expand Down
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<quota_class_set id="test_class">
<cores>20</cores>
<fixed_ips>10</fixed_ips>
<floating_ips>10</floating_ips>
<injected_file_content_bytes>10240</injected_file_content_bytes>
<injected_file_path_bytes>255</injected_file_path_bytes>
Expand Down
@@ -1,6 +1,7 @@
{
"quota_class_set": {
"cores": 50,
"fixed_ips": 10,
"floating_ips": 10,
"injected_file_content_bytes": 10240,
"injected_file_path_bytes": 255,
Expand Down
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<quota_class_set>
<cores>50</cores>
<fixed_ips>10</fixed_ips>
<floating_ips>10</floating_ips>
<injected_file_content_bytes>10240</injected_file_content_bytes>
<injected_file_path_bytes>255</injected_file_path_bytes>
Expand Down
@@ -1,6 +1,7 @@
{
"quota_set": {
"cores": 20,
"fixed_ips": 10,
"floating_ips": 10,
"id": "fake_tenant",
"injected_file_content_bytes": 10240,
Expand All @@ -13,4 +14,4 @@
"security_group_rules": 20,
"security_groups": 10
}
}
}
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<quota_set id="fake_tenant">
<cores>20</cores>
<fixed_ips>10</fixed_ips>
<floating_ips>10</floating_ips>
<injected_file_content_bytes>10240</injected_file_content_bytes>
<injected_file_path_bytes>255</injected_file_path_bytes>
Expand All @@ -11,4 +12,4 @@
<ram>51200</ram>
<security_group_rules>20</security_group_rules>
<security_groups>10</security_groups>
</quota_set>
</quota_set>
3 changes: 2 additions & 1 deletion doc/api_samples/os-quota-sets/quotas-show-get-resp.json
@@ -1,6 +1,7 @@
{
"quota_set": {
"cores": 20,
"fixed_ips": 10,
"floating_ips": 10,
"id": "fake_tenant",
"injected_file_content_bytes": 10240,
Expand All @@ -13,4 +14,4 @@
"security_group_rules": 20,
"security_groups": 10
}
}
}
3 changes: 2 additions & 1 deletion doc/api_samples/os-quota-sets/quotas-show-get-resp.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<quota_set id="fake_tenant">
<cores>20</cores>
<fixed_ips>10</fixed_ips>
<floating_ips>10</floating_ips>
<injected_file_content_bytes>10240</injected_file_content_bytes>
<injected_file_path_bytes>255</injected_file_path_bytes>
Expand All @@ -11,4 +12,4 @@
<ram>51200</ram>
<security_group_rules>20</security_group_rules>
<security_groups>10</security_groups>
</quota_set>
</quota_set>
3 changes: 2 additions & 1 deletion doc/api_samples/os-quota-sets/quotas-update-post-resp.json
@@ -1,6 +1,7 @@
{
"quota_set": {
"cores": 20,
"fixed_ips": 10,
"floating_ips": 10,
"injected_file_content_bytes": 10240,
"injected_file_path_bytes": 255,
Expand All @@ -12,4 +13,4 @@
"security_group_rules": 20,
"security_groups": 45
}
}
}
3 changes: 2 additions & 1 deletion doc/api_samples/os-quota-sets/quotas-update-post-resp.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<quota_set>
<cores>20</cores>
<fixed_ips>10</fixed_ips>
<floating_ips>10</floating_ips>
<injected_file_content_bytes>10240</injected_file_content_bytes>
<injected_file_path_bytes>255</injected_file_path_bytes>
Expand All @@ -11,4 +12,4 @@
<ram>51200</ram>
<security_group_rules>20</security_group_rules>
<security_groups>45</security_groups>
</quota_set>
</quota_set>
6 changes: 6 additions & 0 deletions nova/db/api.py
Expand Up @@ -510,6 +510,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)

####################


Expand Down
12 changes: 12 additions & 0 deletions nova/db/sqlalchemy/api.py
Expand Up @@ -1245,6 +1245,18 @@ 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):
nova.context.authorize_project_context(context, project_id)
return model_query(context, models.FixedIp.id,
base_model=models.FixedIp, read_deleted="no",
session=session).\
join((models.Instance,
models.Instance.uuid == models.FixedIp.instance_uuid)).\
filter(models.Instance.uuid == project_id).\
count()


###################


Expand Down
4 changes: 4 additions & 0 deletions nova/exception.py
Expand Up @@ -1008,6 +1008,10 @@ class FloatingIpLimitExceeded(QuotaError):
message = _("Maximum number of floating ips exceeded")


class FixedIpLimitExceeded(QuotaError):
message = _("Maximum number of fixed ips exceeded")


class MetadataLimitExceeded(QuotaError):
message = _("Maximum number of metadata items exceeds %(allowed)d")

Expand Down
95 changes: 62 additions & 33 deletions nova/network/manager.py
Expand Up @@ -69,11 +69,14 @@
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
from nova import quota
from nova import servicegroup
from nova import utils

LOG = logging.getLogger(__name__)

QUOTAS = quota.QUOTAS


network_opts = [
cfg.StrOpt('flat_network_bridge',
Expand Down Expand Up @@ -823,47 +826,69 @@ def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
# network_get_by_compute_host
address = None

if network['cidr']:
address = kwargs.get('address', None)
if address:
address = self.db.fixed_ip_associate(context,
address,
instance_id,
network['id'])
else:
address = self.db.fixed_ip_associate_pool(context.elevated(),
network['id'],
instance_id)
self._do_trigger_security_group_members_refresh_for_instance(
instance_id)
self._do_trigger_security_group_handler(
'instance_add_security_group', instance_id)
get_vif = self.db.virtual_interface_get_by_instance_and_network
vif = get_vif(context, instance_id, network['id'])
values = {'allocated': True,
'virtual_interface_id': vif['id']}
self.db.fixed_ip_update(context, address, values)
# Check the quota; can't put this in the API because we get
# called into from other places
try:
reservations = QUOTAS.reserve(context, fixed_ips=1)
except exception.OverQuota:
pid = context.project_id
LOG.warn(_("Quota exceeded for %(pid)s, tried to allocate "
"fixed IP") % locals())
raise exception.FixedIpLimitExceeded()

# NOTE(vish) This db query could be removed if we pass az and name
# (or the whole instance object).
instance = self.db.instance_get_by_uuid(context, instance_id)
name = instance['display_name']
try:
if network['cidr']:
address = kwargs.get('address', None)
if address:
address = self.db.fixed_ip_associate(context,
address,
instance_id,
network['id'])
else:
address = self.db.fixed_ip_associate_pool(
context.elevated(), network['id'], instance_id)
self._do_trigger_security_group_members_refresh_for_instance(
instance_id)
self._do_trigger_security_group_handler(
'instance_add_security_group', instance_id)
get_vif = self.db.virtual_interface_get_by_instance_and_network
vif = get_vif(context, instance_id, network['id'])
values = {'allocated': True,
'virtual_interface_id': vif['id']}
self.db.fixed_ip_update(context, address, values)

# NOTE(vish) This db query could be removed if we pass az and name
# (or the whole instance object).
instance = self.db.instance_get_by_uuid(context, instance_id)
name = instance['display_name']

if self._validate_instance_zone_for_dns_domain(context, instance):
self.instance_dns_manager.create_entry(name, address,
"A",
self.instance_dns_domain)
self.instance_dns_manager.create_entry(instance_id, address,
"A",
self.instance_dns_domain)
self._setup_network_on_host(context, network)
return address
if self._validate_instance_zone_for_dns_domain(context, instance):
self.instance_dns_manager.create_entry(
name, address, "A", self.instance_dns_domain)
self.instance_dns_manager.create_entry(
instance_id, address, "A", self.instance_dns_domain)
self._setup_network_on_host(context, network)

QUOTAS.commit(context, reservations)
return address

except Exception:
with excutils.save_and_reraise_exception():
QUOTAS.rollback(context, reservations)

def deallocate_fixed_ip(self, context, address, host=None, teardown=True):
"""Returns a fixed ip to the pool."""
fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
instance_uuid = fixed_ip_ref['instance_uuid']
vif_id = fixed_ip_ref['virtual_interface_id']

try:
reservations = QUOTAS.reserve(context, fixed_ips=-1)
except Exception:
reservations = None
LOG.exception(_("Failed to update usages deallocating "
"fixed IP"))

self._do_trigger_security_group_members_refresh_for_instance(
instance_uuid)
self._do_trigger_security_group_handler(
Expand Down Expand Up @@ -912,6 +937,10 @@ def deallocate_fixed_ip(self, context, address, host=None, teardown=True):

self._teardown_network_on_host(context, network)

# Commit the reservations
if reservations:
QUOTAS.commit(context, reservations)

def lease_fixed_ip(self, context, address):
"""Called by dhcp-bridge when ip is leased."""
LOG.debug(_('Leased IP |%(address)s|'), locals(), context=context)
Expand Down
10 changes: 10 additions & 0 deletions nova/quota.py
Expand Up @@ -43,6 +43,10 @@
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'),
Expand Down Expand Up @@ -1062,6 +1066,11 @@ def _sync_floating_ips(context, project_id, session):
context, project_id, session=session))


def _sync_fixed_ips(context, project_id, session):
return dict(fixed_ips=db.fixed_ip_count_by_project(
context, project_id, session=session))


def _sync_security_groups(context, project_id, session):
return dict(security_groups=db.security_group_count_by_project(
context, project_id, session=session))
Expand All @@ -1076,6 +1085,7 @@ def _sync_security_groups(context, project_id, session):
ReservableResource('ram', _sync_instances, 'quota_ram'),
ReservableResource('floating_ips', _sync_floating_ips,
'quota_floating_ips'),
ReservableResource('fixed_ips', _sync_fixed_ips, 'quota_fixed_ips'),
AbsoluteResource('metadata_items', 'quota_metadata_items'),
AbsoluteResource('injected_files', 'quota_injected_files'),
AbsoluteResource('injected_file_content_bytes',
Expand Down

0 comments on commit 9942921

Please sign in to comment.