Quotas API
This commit is contained in:
@@ -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
49
quark/quota_driver.py
Normal 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)
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user