Quotas API

This commit is contained in:
Kevin George
2013-07-02 05:18:39 -05:00
parent d190f0c86c
commit 21a6f752ae
3 changed files with 105 additions and 15 deletions

View File

@@ -35,6 +35,7 @@ from neutron.openstack.common.db.sqlalchemy import session as neutron_session
from neutron.openstack.common import importutils from neutron.openstack.common import importutils
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils from neutron.openstack.common import uuidutils
from neutron import quota
from neutron import neutron_plugin_base_v2 from neutron import neutron_plugin_base_v2
@@ -66,8 +67,18 @@ quark_opts = [
help=_("Path to the config for the net driver")) help=_("Path to the config for the net driver"))
] ]
quark_quota_opts = [
cfg.IntOpt('quota_ports_per_network',
default=64,
help=_('Maximum ports per network per tenant')),
cfg.IntOpt('quota_security_rules_per_group',
default=20,
help=_('Maximum security group rules in a group')),
]
STRATEGY = network_strategy.STRATEGY STRATEGY = network_strategy.STRATEGY
CONF.register_opts(quark_opts, "QUARK") CONF.register_opts(quark_opts, "QUARK")
CONF.register_opts(quark_quota_opts, "QUOTAS")
def _pop_param(attrs, param, default=None): def _pop_param(attrs, param, default=None):
@@ -93,7 +104,8 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
supported_extension_aliases = ["mac_address_ranges", "routes", supported_extension_aliases = ["mac_address_ranges", "routes",
"ip_addresses", "ports_quark", "ip_addresses", "ports_quark",
"security-group", "security-group",
"subnets_quark", "provider", "ip_policies"] "subnets_quark", "provider",
"ip_policies", "quotas"]
def _initDBMaker(self): def _initDBMaker(self):
# This needs to be called after _ENGINE is configured # This needs to be called after _ENGINE is configured
@@ -125,6 +137,14 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
self.ipam_reuse_after = CONF.QUARK.ipam_reuse_after self.ipam_reuse_after = CONF.QUARK.ipam_reuse_after
neutron_db_api.register_models(base=models.BASEV2) neutron_db_api.register_models(base=models.BASEV2)
quark_resources = [
quota.BaseResource('ports_per_network',
'quota_ports_per_network'),
quota.BaseResource('security_rules_per_group',
'quota_security_rules_per_group'),
]
quota.QUOTAS.register_resources(quark_resources)
def _make_security_group_list(self, context, group_ids): def _make_security_group_list(self, context, group_ids):
if not group_ids or group_ids is attributes.ATTR_NOT_SPECIFIED: if not group_ids or group_ids is attributes.ATTR_NOT_SPECIFIED:
return ([], []) return ([], [])
@@ -590,6 +610,10 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
if not net: if not net:
raise exceptions.NetworkNotFound(net_id=net_id) raise exceptions.NetworkNotFound(net_id=net_id)
quota.QUOTAS.limit_check(
context, context.tenant_id,
ports_per_network=len(net.get('ports', []))+1)
if fixed_ips: if fixed_ips:
for fixed_ip in fixed_ips: for fixed_ip in fixed_ips:
subnet_id = fixed_ip.get("subnet_id") subnet_id = fixed_ip.get("subnet_id")
@@ -1080,9 +1104,6 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
def create_security_group(self, context, security_group): def create_security_group(self, context, security_group):
LOG.info("create_security_group for tenant %s" % LOG.info("create_security_group for tenant %s" %
(context.tenant_id)) (context.tenant_id))
if (db_api.security_group_find(context).count() >=
CONF.QUOTAS.quota_security_group):
raise exceptions.OverQuota(overs="security groups")
group = security_group["security_group"] group = security_group["security_group"]
group_name = group.get('name', '') group_name = group.get('name', '')
if group_name == "default": if group_name == "default":
@@ -1107,14 +1128,13 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
'group_id': '00000000-0000-0000-0000-000000000000', 'group_id': '00000000-0000-0000-0000-000000000000',
'port_egress_rules': [], 'port_egress_rules': [],
'port_ingress_rules': [ 'port_ingress_rules': [
{'ethertype': 'IPv4', 'protocol': 6, {'ethertype': 'IPv4', 'protocol': 1},
'port_range_min': 0, 'port_range_max': 65535}, {'ethertype': 'IPv4', 'protocol': 6},
{'ethertype': 'IPv4', 'protocol': 17, {'ethertype': 'IPv4', 'protocol': 17},
'port_range_min': 0, 'port_range_max': 65535}, {'ethertype': 'IPv6', 'protocol': 1},
{'ethertype': 'IPv6', 'protocol': 6, {'ethertype': 'IPv6', 'protocol': 6},
'port_range_min': 0, 'port_range_max': 65535}, {'ethertype': 'IPv6', 'protocol': 17},
{'ethertype': 'IPv6', 'protocol': 17, ]}
'port_range_min': 0, 'port_range_max': 65535}]}
self.net_driver.create_security_group( self.net_driver.create_security_group(
context, context,
@@ -1134,9 +1154,6 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
def create_security_group_rule(self, context, security_group_rule): def create_security_group_rule(self, context, security_group_rule):
LOG.info("create_security_group for tenant %s" % LOG.info("create_security_group for tenant %s" %
(context.tenant_id)) (context.tenant_id))
if (db_api.security_group_rule_find(context).count() >=
CONF.QUOTAS.quota_security_group_rule):
raise exceptions.OverQuota(overs="security group rules")
rule = self._validate_security_group_rule( rule = self._validate_security_group_rule(
context, security_group_rule["security_group_rule"]) context, security_group_rule["security_group_rule"])
rule['id'] = uuidutils.generate_uuid() rule['id'] = uuidutils.generate_uuid()
@@ -1147,6 +1164,10 @@ class Plugin(neutron_plugin_base_v2.NeutronPluginBaseV2,
if not group: if not group:
raise sg_ext.SecurityGroupNotFound(group_id=group_id) raise sg_ext.SecurityGroupNotFound(group_id=group_id)
quota.QUOTAS.limit_check(
context, context.tenant_id,
security_rules_per_group=len(group.get('rules', []))+1)
self.net_driver.create_security_group_rule( self.net_driver.create_security_group_rule(
context, context,
group_id, group_id,

49
quark/quota_driver.py Normal file
View File

@@ -0,0 +1,49 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.db import quota_db
class QuarkQuotaDriver(quota_db.DbQuotaDriver):
"""Driver to perform necessary checks to enforce quotas and obtain quota
information.
The default driver utilizes the local database.
"""
@staticmethod
def delete_tenant_quota(context, tenant_id):
"""Delete the quota entries for a given tenant_id.
Atfer deletion, this tenant will use default quota values in conf.
"""
tenant_quotas = context.session.query(quota_db.Quota)
tenant_quotas = tenant_quotas.filter_by(tenant_id=tenant_id)
tenant_quotas.delete()
@staticmethod
def update_quota_limit(context, tenant_id, resource, limit):
tenant_quota = context.session.query(quota_db.Quota).filter_by(
tenant_id=tenant_id, resource=resource).first()
if tenant_quota:
tenant_quota.update({'limit': limit})
else:
tenant_quota = quota_db.Quota(tenant_id=tenant_id,
resource=resource,
limit=limit)
context.session.add(tenant_quota)

View File

@@ -37,6 +37,7 @@ class TestQuarkPlugin(test_base.TestBase):
def setUp(self): def setUp(self):
super(TestQuarkPlugin, self).setUp() super(TestQuarkPlugin, self).setUp()
cfg.CONF.set_override('quota_ports_per_network', 1, 'QUOTAS')
cfg.CONF.set_override('connection', 'sqlite://', 'database') cfg.CONF.set_override('connection', 'sqlite://', 'database')
db_api.configure_db() db_api.configure_db()
self.plugin = quark.plugin.Plugin() self.plugin = quark.plugin.Plugin()
@@ -1219,6 +1220,18 @@ class TestQuarkCreatePort(TestQuarkPlugin):
with self.assertRaises(exceptions.NetworkNotFound): with self.assertRaises(exceptions.NetworkNotFound):
self.plugin.create_port(self.context, port) self.plugin.create_port(self.context, port)
def test_create_port_net_at_max(self):
network = dict(id=1, ports=[models.Port()])
mac = dict(address="aa:bb:cc:dd:ee:ff")
port_name = "foobar"
ip = dict()
port = dict(port=dict(mac_address=mac["address"], network_id=1,
tenant_id=self.context.tenant_id, device_id=2,
name=port_name))
with self._stubs(port=port["port"], network=network, addr=ip, mac=mac):
with self.assertRaises(exceptions.OverQuota):
self.plugin.create_port(self.context, port)
def test_create_port_security_groups(self, groups=[1]): def test_create_port_security_groups(self, groups=[1]):
network = dict(id=1) network = dict(id=1)
mac = dict(address="aa:bb:cc:dd:ee:ff") mac = dict(address="aa:bb:cc:dd:ee:ff")
@@ -1888,6 +1901,7 @@ class TestQuarkCreateSecurityGroupRule(TestQuarkPlugin):
def setUp(self, *args, **kwargs): def setUp(self, *args, **kwargs):
super(TestQuarkCreateSecurityGroupRule, self).setUp(*args, **kwargs) super(TestQuarkCreateSecurityGroupRule, self).setUp(*args, **kwargs)
cfg.CONF.set_override('quota_security_group_rule', 1, 'QUOTAS') cfg.CONF.set_override('quota_security_group_rule', 1, 'QUOTAS')
cfg.CONF.set_override('quota_security_rules_per_group', 1, 'QUOTAS')
self.rule = {'id': 1, 'ethertype': 'IPv4', self.rule = {'id': 1, 'ethertype': 'IPv4',
'security_group_id': 1, 'group': {'id': 1}} 'security_group_id': 1, 'group': {'id': 1}}
self.expected = { self.expected = {
@@ -1928,6 +1942,7 @@ class TestQuarkCreateSecurityGroupRule(TestQuarkPlugin):
rule = dict(self.rule, **ruleset) rule = dict(self.rule, **ruleset)
group = rule.pop('group') group = rule.pop('group')
expected = dict(self.expected, **ruleset) expected = dict(self.expected, **ruleset)
expected.pop('group', None)
with self._stubs(rule, group) as rule_create: with self._stubs(rule, group) as rule_create:
result = self.plugin.create_security_group_rule( result = self.plugin.create_security_group_rule(
self.context, {'security_group_rule': rule}) self.context, {'security_group_rule': rule})
@@ -1973,6 +1988,11 @@ class TestQuarkCreateSecurityGroupRule(TestQuarkPlugin):
with self.assertRaises(sg_ext.SecurityGroupNotFound): with self.assertRaises(sg_ext.SecurityGroupNotFound):
self._test_create_security_rule(group=None) self._test_create_security_rule(group=None)
def test_create_security_rule_group_at_max(self):
with self.assertRaises(exceptions.OverQuota):
self._test_create_security_rule(
group={'id': 1, 'rules': [models.SecurityGroupRule()]})
class TestQuarkDeleteSecurityGroupRule(TestQuarkPlugin): class TestQuarkDeleteSecurityGroupRule(TestQuarkPlugin):
@contextlib.contextmanager @contextlib.contextmanager