RM8894
Adds a quota for the number of security groups allowed per port.
This commit is contained in:
@@ -42,7 +42,9 @@ quark_resources = [
|
|||||||
quota.BaseResource('routes_per_subnet',
|
quota.BaseResource('routes_per_subnet',
|
||||||
'quota_routes_per_subnet'),
|
'quota_routes_per_subnet'),
|
||||||
quota.BaseResource('security_rules_per_group',
|
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 = [
|
quark_quota_opts = [
|
||||||
@@ -54,7 +56,10 @@ quark_quota_opts = [
|
|||||||
help=_('Maximum routes per subnet')),
|
help=_('Maximum routes per subnet')),
|
||||||
cfg.IntOpt('quota_security_rules_per_group',
|
cfg.IntOpt('quota_security_rules_per_group',
|
||||||
default=20,
|
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"))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
from neutron.common import exceptions
|
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 log as logging
|
||||||
from neutron.openstack.common import uuidutils
|
from neutron.openstack.common import uuidutils
|
||||||
from neutron import quota
|
from neutron import quota
|
||||||
@@ -90,9 +91,11 @@ def create_port(context, port):
|
|||||||
|
|
||||||
ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"])
|
ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"])
|
||||||
net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"])
|
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))
|
context, port["port"].pop("security_groups", None))
|
||||||
|
|
||||||
|
quota.QUOTAS.limit_check(context, context.tenant_id,
|
||||||
|
security_groups_per_port=len(group_ids))
|
||||||
addresses = []
|
addresses = []
|
||||||
mac = None
|
mac = None
|
||||||
backend_port = None
|
backend_port = None
|
||||||
@@ -225,6 +228,11 @@ def update_port(context, id, port):
|
|||||||
utils.filter_body(context, port_dict, admin_only=admin_only,
|
utils.filter_body(context, port_dict, admin_only=admin_only,
|
||||||
always_filter=always_filter)
|
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:
|
if fixed_ips is not None:
|
||||||
# NOTE(mdietz): we want full control over IPAM since
|
# NOTE(mdietz): we want full control over IPAM since
|
||||||
# we're allocating by subnet instead of
|
# 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"] = port_db["ip_addresses"]
|
||||||
port_dict["addresses"].extend(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(
|
net_driver = registry.DRIVER_REGISTRY.get_driver(
|
||||||
port_db.network["network_plugin"])
|
port_db.network["network_plugin"])
|
||||||
net_driver.update_port(context, port_id=port_db.backend_key,
|
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='')
|
raise exceptions.PortNotFound(port_id=id, net_id='')
|
||||||
port = _diag_port(context, db_port, fields)
|
port = _diag_port(context, db_port, fields)
|
||||||
return {'ports': port}
|
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)
|
||||||
|
|||||||
@@ -18,15 +18,13 @@ View Helpers for Quark Plugin
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
from neutron.extensions import securitygroup as sg_ext
|
|
||||||
from neutron.openstack.common import log as logging
|
from neutron.openstack.common import log as logging
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from quark.db import api as db_api
|
|
||||||
from quark.db import models
|
from quark.db import models
|
||||||
from quark import network_strategy
|
from quark import network_strategy
|
||||||
from quark import protocols
|
from quark import protocols
|
||||||
from quark import utils
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@@ -260,17 +258,3 @@ def _make_ip_policy_dict(ipp):
|
|||||||
"subnet_ids": [s["id"] for s in ipp["subnets"]],
|
"subnet_ids": [s["id"] for s in ipp["subnets"]],
|
||||||
"network_ids": [n["id"] for n in ipp["networks"]],
|
"network_ids": [n["id"] for n in ipp["networks"]],
|
||||||
"exclude": [ippc["cidr"] for ippc in ipp["exclude"]]}
|
"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)
|
|
||||||
|
|||||||
@@ -243,7 +243,8 @@ class TestQuarkCreatePortFailure(test_quark_plugin.TestQuarkPlugin):
|
|||||||
|
|
||||||
class TestQuarkCreatePort(test_quark_plugin.TestQuarkPlugin):
|
class TestQuarkCreatePort(test_quark_plugin.TestQuarkPlugin):
|
||||||
@contextlib.contextmanager
|
@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:
|
if network:
|
||||||
network["network_plugin"] = "BASE"
|
network["network_plugin"] = "BASE"
|
||||||
network["ipam_strategy"] = "ANY"
|
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_ip_address" % ipam),
|
||||||
mock.patch("%s.allocate_mac_address" % ipam),
|
mock.patch("%s.allocate_mac_address" % ipam),
|
||||||
mock.patch("%s.port_count_all" % db_mod),
|
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
|
port_create.return_value = port_models
|
||||||
net_find.return_value = network
|
net_find.return_value = network
|
||||||
alloc_ip.return_value = addr
|
alloc_ip.return_value = addr
|
||||||
alloc_mac.return_value = mac
|
alloc_mac.return_value = mac
|
||||||
port_count.return_value = 0
|
port_count.return_value = 0
|
||||||
|
if limit_raise:
|
||||||
|
limit_check.side_effect = exceptions.OverQuota
|
||||||
yield port_create
|
yield port_create
|
||||||
|
|
||||||
def test_create_port(self):
|
def test_create_port(self):
|
||||||
@@ -419,6 +424,32 @@ class TestQuarkCreatePort(test_quark_plugin.TestQuarkPlugin):
|
|||||||
with self.assertRaises(sg_ext.SecurityGroupNotFound):
|
with self.assertRaises(sg_ext.SecurityGroupNotFound):
|
||||||
self.test_create_port_security_groups([])
|
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):
|
class TestQuarkPortCreateQuota(test_quark_plugin.TestQuarkPlugin):
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
@@ -438,12 +469,15 @@ class TestQuarkPortCreateQuota(test_quark_plugin.TestQuarkPlugin):
|
|||||||
mock.patch("%s.allocate_ip_address" % ipam),
|
mock.patch("%s.allocate_ip_address" % ipam),
|
||||||
mock.patch("%s.allocate_mac_address" % ipam),
|
mock.patch("%s.allocate_mac_address" % ipam),
|
||||||
mock.patch("quark.db.api.port_count_all"),
|
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
|
port_create.return_value = port_models
|
||||||
net_find.return_value = network
|
net_find.return_value = network
|
||||||
alloc_ip.return_value = addr
|
alloc_ip.return_value = addr
|
||||||
alloc_mac.return_value = mac
|
alloc_mac.return_value = mac
|
||||||
port_count.return_value = len(network["ports"])
|
port_count.return_value = len(network["ports"])
|
||||||
|
limit_check.side_effect = exceptions.OverQuota
|
||||||
yield port_create
|
yield port_create
|
||||||
|
|
||||||
def test_create_port_net_at_max(self):
|
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_find"),
|
||||||
mock.patch("quark.db.api.port_update"),
|
mock.patch("quark.db.api.port_update"),
|
||||||
mock.patch("quark.ipam.QuarkIpam.allocate_ip_address"),
|
mock.patch("quark.ipam.QuarkIpam.allocate_ip_address"),
|
||||||
mock.patch("quark.ipam.QuarkIpam.deallocate_ips_by_port")
|
mock.patch("quark.ipam.QuarkIpam.deallocate_ips_by_port"),
|
||||||
) as (port_find, port_update, alloc_ip, dealloc_ip):
|
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_find.return_value = port_model
|
||||||
port_update.return_value = port_model
|
port_update.return_value = port_model
|
||||||
if new_ips:
|
if new_ips:
|
||||||
@@ -574,8 +609,9 @@ class TestQuarkUpdatePortSetsIps(test_quark_plugin.TestQuarkPlugin):
|
|||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch("quark.db.api.port_find"),
|
mock.patch("quark.db.api.port_find"),
|
||||||
mock.patch("quark.db.api.port_update"),
|
mock.patch("quark.db.api.port_update"),
|
||||||
mock.patch("quark.ipam.QuarkIpam.deallocate_ips_by_port")
|
mock.patch("quark.ipam.QuarkIpam.deallocate_ips_by_port"),
|
||||||
) as (port_find, port_update, dealloc_ip):
|
mock.patch("neutron.quota.QuotaEngine.limit_check")
|
||||||
|
) as (port_find, port_update, dealloc_ip, limit_check):
|
||||||
port_find.return_value = port_model
|
port_find.return_value = port_model
|
||||||
port_update.return_value = port_model
|
port_update.return_value = port_model
|
||||||
alloc_ip = mock.patch("quark.ipam.QuarkIpam.allocate_ip_address",
|
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.network_find" % db_mod),
|
||||||
mock.patch("%s.allocate_ip_address" % ipam),
|
mock.patch("%s.allocate_ip_address" % ipam),
|
||||||
mock.patch("%s.allocate_mac_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
|
port_create.return_value = port_models
|
||||||
net_find.return_value = network
|
net_find.return_value = network
|
||||||
alloc_ip.return_value = addr
|
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("neutron.openstack.common.uuidutils.generate_uuid"),
|
||||||
mock.patch("quark.plugin_views._make_port_dict"),
|
mock.patch("quark.plugin_views._make_port_dict"),
|
||||||
mock.patch("%s.port_count_all" % db_mod),
|
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,
|
) as (port_create, net_find, alloc_ip, alloc_mac, gen_uuid, make_port,
|
||||||
port_count):
|
port_count, limit_check):
|
||||||
net_find.return_value = network
|
net_find.return_value = network
|
||||||
alloc_ip.return_value = addr
|
alloc_ip.return_value = addr
|
||||||
alloc_mac.return_value = mac
|
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.db.api.port_update"),
|
||||||
mock.patch("quark.drivers.registry.DriverRegistry.get_driver"),
|
mock.patch("quark.drivers.registry.DriverRegistry.get_driver"),
|
||||||
mock.patch("quark.plugin_views._make_port_dict"),
|
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
|
yield port_find, port_update
|
||||||
|
|
||||||
def test_update_port_attribute_filtering(self):
|
def test_update_port_attribute_filtering(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user