Adds a quota for the number of security groups allowed per port.
This commit is contained in:
Matt Dietz
2014-09-17 08:26:18 +00:00
parent 18d1a2c319
commit c9b9e1ed75
4 changed files with 80 additions and 32 deletions

View File

@@ -42,7 +42,9 @@ quark_resources = [
quota.BaseResource('routes_per_subnet',
'quota_routes_per_subnet'),
quota.BaseResource('security_rules_per_group',
'quota_security_rules_per_group')
'quota_security_rules_per_group'),
quota.BaseResource('security_groups_per_port',
'quota_security_groups_per_port')
]
quark_quota_opts = [
@@ -54,7 +56,10 @@ quark_quota_opts = [
help=_('Maximum routes per subnet')),
cfg.IntOpt('quota_security_rules_per_group',
default=20,
help=_('Maximum security group rules in a group'))
help=_('Maximum security group rules in a group')),
cfg.IntOpt("quota_security_groups_per_port",
default=5,
help=_("Maximum number of security groups per port"))
]

View File

@@ -15,6 +15,7 @@
import netaddr
from neutron.common import exceptions
from neutron.extensions import securitygroup as sg_ext
from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils
from neutron import quota
@@ -90,9 +91,11 @@ def create_port(context, port):
ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"])
net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"])
group_ids, security_groups = v.make_security_group_list(
group_ids, security_groups = _make_security_group_list(
context, port["port"].pop("security_groups", None))
quota.QUOTAS.limit_check(context, context.tenant_id,
security_groups_per_port=len(group_ids))
addresses = []
mac = None
backend_port = None
@@ -225,6 +228,11 @@ def update_port(context, id, port):
utils.filter_body(context, port_dict, admin_only=admin_only,
always_filter=always_filter)
group_ids, security_groups = _make_security_group_list(
context, port_dict.pop("security_groups", None))
quota.QUOTAS.limit_check(context, context.tenant_id,
security_groups_per_port=len(group_ids))
if fixed_ips is not None:
# NOTE(mdietz): we want full control over IPAM since
# we're allocating by subnet instead of
@@ -283,8 +291,6 @@ def update_port(context, id, port):
port_dict["addresses"] = port_db["ip_addresses"]
port_dict["addresses"].extend(addresses)
group_ids, security_groups = v.make_security_group_list(
context, port_dict.pop("security_groups", None))
net_driver = registry.DRIVER_REGISTRY.get_driver(
port_db.network["network_plugin"])
net_driver.update_port(context, port_id=port_db.backend_key,
@@ -489,3 +495,17 @@ def diagnose_port(context, id, fields):
raise exceptions.PortNotFound(port_id=id, net_id='')
port = _diag_port(context, db_port, fields)
return {'ports': port}
def _make_security_group_list(context, group_ids):
if not group_ids or not utils.attr_specified(group_ids):
return ([], [])
group_ids = list(set(group_ids))
groups = []
for gid in group_ids:
group = db_api.security_group_find(context, id=gid,
scope=db_api.ONE)
if not group:
raise sg_ext.SecurityGroupNotFound(id=gid)
groups.append(group)
return (group_ids, groups)

View File

@@ -18,15 +18,13 @@ View Helpers for Quark Plugin
"""
import netaddr
from neutron.extensions import securitygroup as sg_ext
from neutron.openstack.common import log as logging
from oslo.config import cfg
from quark.db import api as db_api
from quark.db import models
from quark import network_strategy
from quark import protocols
from quark import utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@@ -260,17 +258,3 @@ def _make_ip_policy_dict(ipp):
"subnet_ids": [s["id"] for s in ipp["subnets"]],
"network_ids": [n["id"] for n in ipp["networks"]],
"exclude": [ippc["cidr"] for ippc in ipp["exclude"]]}
def make_security_group_list(context, group_ids):
if not group_ids or not utils.attr_specified(group_ids):
return ([], [])
group_ids = list(set(group_ids))
groups = []
for gid in group_ids:
group = db_api.security_group_find(context, id=gid,
scope=db_api.ONE)
if not group:
raise sg_ext.SecurityGroupNotFound(id=gid)
groups.append(group)
return (group_ids, groups)

View File

@@ -243,7 +243,8 @@ class TestQuarkCreatePortFailure(test_quark_plugin.TestQuarkPlugin):
class TestQuarkCreatePort(test_quark_plugin.TestQuarkPlugin):
@contextlib.contextmanager
def _stubs(self, port=None, network=None, addr=None, mac=None):
def _stubs(self, port=None, network=None, addr=None, mac=None,
limit_raise=False):
if network:
network["network_plugin"] = "BASE"
network["ipam_strategy"] = "ANY"
@@ -259,12 +260,16 @@ class TestQuarkCreatePort(test_quark_plugin.TestQuarkPlugin):
mock.patch("%s.allocate_ip_address" % ipam),
mock.patch("%s.allocate_mac_address" % ipam),
mock.patch("%s.port_count_all" % db_mod),
) as (port_create, net_find, alloc_ip, alloc_mac, port_count):
mock.patch("neutron.quota.QuotaEngine.limit_check")
) as (port_create, net_find, alloc_ip, alloc_mac, port_count,
limit_check):
port_create.return_value = port_models
net_find.return_value = network
alloc_ip.return_value = addr
alloc_mac.return_value = mac
port_count.return_value = 0
if limit_raise:
limit_check.side_effect = exceptions.OverQuota
yield port_create
def test_create_port(self):
@@ -419,6 +424,32 @@ class TestQuarkCreatePort(test_quark_plugin.TestQuarkPlugin):
with self.assertRaises(sg_ext.SecurityGroupNotFound):
self.test_create_port_security_groups([])
def test_create_port_security_groups_over_quota(self):
network = dict(id=1)
mac = dict(address="AA:BB:CC:DD:EE:FF")
port_name = "foobar"
ip = dict()
groups = []
group_ids = range(6)
for gid in group_ids:
group = models.SecurityGroup()
group.update({'id': gid, 'tenant_id': self.context.tenant_id,
'name': 'foo', 'description': 'bar'})
groups.append(group)
port = dict(port=dict(mac_address=mac["address"], network_id=1,
tenant_id=self.context.tenant_id, device_id=2,
name=port_name, security_groups=groups))
with self._stubs(port=port["port"], network=network, addr=ip,
mac=mac, limit_raise=True):
with mock.patch("quark.db.api.security_group_find") as group_find:
group_find.return_value = groups
port["port"]["security_groups"] = groups
with self.assertRaises(exceptions.OverQuota):
self.plugin.create_port(self.context, port)
class TestQuarkPortCreateQuota(test_quark_plugin.TestQuarkPlugin):
@contextlib.contextmanager
@@ -438,12 +469,15 @@ class TestQuarkPortCreateQuota(test_quark_plugin.TestQuarkPlugin):
mock.patch("%s.allocate_ip_address" % ipam),
mock.patch("%s.allocate_mac_address" % ipam),
mock.patch("quark.db.api.port_count_all"),
) as (port_create, net_find, alloc_ip, alloc_mac, port_count):
mock.patch("neutron.quota.QuotaEngine.limit_check")
) as (port_create, net_find, alloc_ip, alloc_mac, port_count,
limit_check):
port_create.return_value = port_models
net_find.return_value = network
alloc_ip.return_value = addr
alloc_mac.return_value = mac
port_count.return_value = len(network["ports"])
limit_check.side_effect = exceptions.OverQuota
yield port_create
def test_create_port_net_at_max(self):
@@ -473,8 +507,9 @@ class TestQuarkUpdatePort(test_quark_plugin.TestQuarkPlugin):
mock.patch("quark.db.api.port_find"),
mock.patch("quark.db.api.port_update"),
mock.patch("quark.ipam.QuarkIpam.allocate_ip_address"),
mock.patch("quark.ipam.QuarkIpam.deallocate_ips_by_port")
) as (port_find, port_update, alloc_ip, dealloc_ip):
mock.patch("quark.ipam.QuarkIpam.deallocate_ips_by_port"),
mock.patch("neutron.quota.QuotaEngine.limit_check")
) as (port_find, port_update, alloc_ip, dealloc_ip, limit_check):
port_find.return_value = port_model
port_update.return_value = port_model
if new_ips:
@@ -574,8 +609,9 @@ class TestQuarkUpdatePortSetsIps(test_quark_plugin.TestQuarkPlugin):
with contextlib.nested(
mock.patch("quark.db.api.port_find"),
mock.patch("quark.db.api.port_update"),
mock.patch("quark.ipam.QuarkIpam.deallocate_ips_by_port")
) as (port_find, port_update, dealloc_ip):
mock.patch("quark.ipam.QuarkIpam.deallocate_ips_by_port"),
mock.patch("neutron.quota.QuotaEngine.limit_check")
) as (port_find, port_update, dealloc_ip, limit_check):
port_find.return_value = port_model
port_update.return_value = port_model
alloc_ip = mock.patch("quark.ipam.QuarkIpam.allocate_ip_address",
@@ -756,7 +792,8 @@ class TestQuarkCreatePortOnSharedNetworks(test_quark_plugin.TestQuarkPlugin):
mock.patch("%s.network_find" % db_mod),
mock.patch("%s.allocate_ip_address" % ipam),
mock.patch("%s.allocate_mac_address" % ipam),
) as (port_create, net_find, alloc_ip, alloc_mac):
mock.patch("neutron.quota.QuotaEngine.limit_check")
) as (port_create, net_find, alloc_ip, alloc_mac, limit_check):
port_create.return_value = port_models
net_find.return_value = network
alloc_ip.return_value = addr
@@ -996,8 +1033,9 @@ class TestQuarkPortCreateFiltering(test_quark_plugin.TestQuarkPlugin):
mock.patch("neutron.openstack.common.uuidutils.generate_uuid"),
mock.patch("quark.plugin_views._make_port_dict"),
mock.patch("%s.port_count_all" % db_mod),
mock.patch("neutron.quota.QuotaEngine.limit_check")
) as (port_create, net_find, alloc_ip, alloc_mac, gen_uuid, make_port,
port_count):
port_count, limit_check):
net_find.return_value = network
alloc_ip.return_value = addr
alloc_mac.return_value = mac
@@ -1084,7 +1122,8 @@ class TestQuarkPortUpdateFiltering(test_quark_plugin.TestQuarkPlugin):
mock.patch("quark.db.api.port_update"),
mock.patch("quark.drivers.registry.DriverRegistry.get_driver"),
mock.patch("quark.plugin_views._make_port_dict"),
) as (port_find, port_update, get_driver, make_port):
mock.patch("neutron.quota.QuotaEngine.limit_check")
) as (port_find, port_update, get_driver, make_port, limit_check):
yield port_find, port_update
def test_update_port_attribute_filtering(self):