From 967d3dea9142c4784302a637b752c673ebe6fc67 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 8 Jan 2020 13:31:29 +0000 Subject: [PATCH] Check "security_group_rule" quota during security group creation The tracked resources quota check is done at the beginning of an API call to the Neutron server. The API call contains a resource and an action over the resource. In case of creation, the server checks if the number of items requested fits in the existing quota. In case of security group creation, the tracked resource checked is "security_group". But "SecurityGroupDbMixin.create_security_group" method also creates several default rules for the new group and the quota for "security_group_rule" is not enforced. This patch checks the number of security group rules being created ("delta") and checks in the plugin method (not in the API method) if there is enough room for those new rules (tracked resource "security_group_rule"). Change-Id: I0a9b91b09d6260ff96fdba2f0a455de53bbc1f00 Closes-Bug: #1858680 (cherry picked from commit 936bd67aa42b2e06241d309315b895c9c9c49dcc) --- neutron/db/securitygroups_db.py | 10 ++++++++ neutron/tests/fullstack/resources/client.py | 3 +++ neutron/tests/fullstack/test_securitygroup.py | 25 +++++++++++++++++++ neutron/tests/functional/db/test_network.py | 5 ++++ neutron/tests/unit/db/test_l3_hamode_db.py | 5 ++++ .../tests/unit/db/test_securitygroups_db.py | 5 ++++ neutron/tests/unit/plugins/ml2/test_plugin.py | 9 +++++++ .../unit/scheduler/test_l3_agent_scheduler.py | 5 ++++ 8 files changed, 67 insertions(+) diff --git a/neutron/db/securitygroups_db.py b/neutron/db/securitygroups_db.py index 56004fc094d..d68460e44b3 100644 --- a/neutron/db/securitygroups_db.py +++ b/neutron/db/securitygroups_db.py @@ -41,6 +41,7 @@ from neutron.extensions import securitygroup as ext_sg from neutron.objects import base as base_obj from neutron.objects import ports as port_obj from neutron.objects import securitygroup as sg_obj +from neutron import quota LOG = logging.getLogger(__name__) @@ -111,6 +112,12 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase, name=s['name'], is_default=default_sg) sg.create() + delta = len(ext_sg.sg_supported_ethertypes) + delta = delta * 2 if default_sg else delta + reservation = quota.QUOTAS.make_reservation( + context, tenant_id, {'security_group_rule': delta}, + self) + for ethertype in ext_sg.sg_supported_ethertypes: if default_sg: # Allow intercommunication @@ -130,6 +137,9 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase, sg.rules.append(egress_rule) sg.obj_reset_changes(['rules']) + quota.QUOTAS.commit_reservation(context, + reservation.reservation_id) + # fetch sg from db to load the sg rules with sg model. sg = sg_obj.SecurityGroup.get_object(context, id=sg.id) secgroup_dict = self._make_security_group_dict(sg) diff --git a/neutron/tests/fullstack/resources/client.py b/neutron/tests/fullstack/resources/client.py index 5ca657a552a..7becaf9a43b 100644 --- a/neutron/tests/fullstack/resources/client.py +++ b/neutron/tests/fullstack/resources/client.py @@ -342,3 +342,6 @@ class ClientFixture(fixtures.Fixture): self.addCleanup( _safe_method(self.client.delete_network_log), net_log['log']['id']) return net_log + + def update_quota(self, project_id, tracked_resource, quota): + self._update_resource('quota', project_id, {tracked_resource: quota}) diff --git a/neutron/tests/fullstack/test_securitygroup.py b/neutron/tests/fullstack/test_securitygroup.py index cc9fda6fd24..95f1406dcc1 100644 --- a/neutron/tests/fullstack/test_securitygroup.py +++ b/neutron/tests/fullstack/test_securitygroup.py @@ -13,6 +13,7 @@ # under the License. from neutron_lib import constants +from neutronclient.common import exceptions as nc_exc from oslo_utils import uuidutils from neutron.cmd.sanity import checks @@ -507,3 +508,27 @@ class TestSecurityGroupsSameNetwork(BaseSecurityGroupsSameNetworkTest): vm1, vm2, net_helpers.NetcatTester.TCP, port + 1) self.verify_no_connectivity_between_vms( vm2, vm1, net_helpers.NetcatTester.TCP, port + 1) + + +class SecurityGroupRulesTest(base.BaseFullStackTestCase): + + def setUp(self): + host_descriptions = [environment.HostDescription()] + env = environment.Environment(environment.EnvironmentDescription(), + host_descriptions) + super(SecurityGroupRulesTest, self).setUp(env) + + def test_security_group_rule_quota(self): + project_id = uuidutils.generate_uuid() + quota = self.client.show_quota_details(project_id) + sg_rules_used = quota['quota']['security_group_rule']['used'] + self.assertEqual(0, sg_rules_used) + + self.safe_client.create_security_group(project_id) + quota = self.client.show_quota_details(project_id) + sg_rules_used = quota['quota']['security_group_rule']['used'] + self.safe_client.update_quota(project_id, 'security_group_rule', + sg_rules_used) + + self.assertRaises(nc_exc.OverQuotaClient, + self.safe_client.create_security_group, project_id) diff --git a/neutron/tests/functional/db/test_network.py b/neutron/tests/functional/db/test_network.py index efeb133a9b7..6532d78c503 100644 --- a/neutron/tests/functional/db/test_network.py +++ b/neutron/tests/functional/db/test_network.py @@ -21,6 +21,7 @@ from oslo_utils import uuidutils from neutron.objects import network as network_obj from neutron.plugins.ml2 import plugin as ml2_plugin +from neutron import quota from neutron.tests.unit import testlib_api @@ -43,6 +44,10 @@ class NetworkRBACTestCase(testlib_api.SqlTestCase): self.subnet_1_id = uuidutils.generate_uuid() self.subnet_2_id = uuidutils.generate_uuid() self.port_id = uuidutils.generate_uuid() + make_res = mock.patch.object(quota.QuotaEngine, 'make_reservation') + self.mock_quota_make_res = make_res.start() + commit_res = mock.patch.object(quota.QuotaEngine, 'commit_reservation') + self.mock_quota_commit_res = commit_res.start() def _create_network(self, tenant_id, network_id, shared, external=False): network = {'tenant_id': tenant_id, diff --git a/neutron/tests/unit/db/test_l3_hamode_db.py b/neutron/tests/unit/db/test_l3_hamode_db.py index 84cba8c5ec7..0c052b7fde8 100644 --- a/neutron/tests/unit/db/test_l3_hamode_db.py +++ b/neutron/tests/unit/db/test_l3_hamode_db.py @@ -46,6 +46,7 @@ from neutron.db import agents_db from neutron.db import l3_agentschedulers_db from neutron.db import l3_hamode_db from neutron.objects import l3_hamode +from neutron import quota from neutron.scheduler import l3_agent_scheduler from neutron.services.revisions import revision_plugin from neutron.tests.common import helpers @@ -71,6 +72,10 @@ class L3HATestFramework(testlib_api.SqlTestCase): notif_p = mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin, '_notify_router_updated') self.notif_m = notif_p.start() + make_res = mock.patch.object(quota.QuotaEngine, 'make_reservation') + self.mock_quota_make_res = make_res.start() + commit_res = mock.patch.object(quota.QuotaEngine, 'commit_reservation') + self.mock_quota_commit_res = commit_res.start() cfg.CONF.set_override('allow_overlapping_ips', True) self.plugin = FakeL3PluginWithAgents() diff --git a/neutron/tests/unit/db/test_securitygroups_db.py b/neutron/tests/unit/db/test_securitygroups_db.py index 43c716a4364..0aa0ccf5f56 100644 --- a/neutron/tests/unit/db/test_securitygroups_db.py +++ b/neutron/tests/unit/db/test_securitygroups_db.py @@ -25,6 +25,7 @@ import testtools from neutron.db import securitygroups_db from neutron.extensions import securitygroup +from neutron import quota from neutron.services.revisions import revision_plugin from neutron.tests.unit import testlib_api @@ -71,6 +72,10 @@ class SecurityGroupDbMixinTestCase(testlib_api.SqlTestCase): self.setup_coreplugin(core_plugin=DB_PLUGIN_KLASS) self.ctx = context.get_admin_context() self.mixin = SecurityGroupDbMixinImpl() + make_res = mock.patch.object(quota.QuotaEngine, 'make_reservation') + self.mock_quota_make_res = make_res.start() + commit_res = mock.patch.object(quota.QuotaEngine, 'commit_reservation') + self.mock_quota_commit_res = commit_res.start() def test_create_security_group_conflict(self): with mock.patch.object(registry, "publish") as mock_publish: diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py index b4c2fd5ae93..4f9df74c768 100644 --- a/neutron/tests/unit/plugins/ml2/test_plugin.py +++ b/neutron/tests/unit/plugins/ml2/test_plugin.py @@ -62,6 +62,7 @@ from neutron.plugins.ml2.drivers import type_vlan from neutron.plugins.ml2 import managers from neutron.plugins.ml2 import models from neutron.plugins.ml2 import plugin as ml2_plugin +from neutron import quota from neutron.services.revisions import revision_plugin from neutron.services.segments import db as segments_plugin_db from neutron.services.segments import plugin as segments_plugin @@ -2148,6 +2149,10 @@ class TestMl2PortBinding(Ml2PluginV2TestCase, cfg.CONF.set_override( 'enable_security_group', self.ENABLE_SG, group='SECURITYGROUP') + make_res = mock.patch.object(quota.QuotaEngine, 'make_reservation') + self.mock_quota_make_res = make_res.start() + commit_res = mock.patch.object(quota.QuotaEngine, 'commit_reservation') + self.mock_quota_commit_res = commit_res.start() super(TestMl2PortBinding, self).setUp() def _check_port_binding_profile(self, port, profile=None): @@ -2868,6 +2873,10 @@ class TestMl2PortSecurity(Ml2PluginV2TestCase): cfg.CONF.set_override('enable_security_group', False, group='SECURITYGROUP') + make_res = mock.patch.object(quota.QuotaEngine, 'make_reservation') + self.mock_quota_make_res = make_res.start() + commit_res = mock.patch.object(quota.QuotaEngine, 'commit_reservation') + self.mock_quota_commit_res = commit_res.start() super(TestMl2PortSecurity, self).setUp() def test_port_update_without_security_groups(self): diff --git a/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py b/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py index 76e95dcad24..42a2d0e8c6d 100644 --- a/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py +++ b/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py @@ -47,6 +47,7 @@ from neutron import manager from neutron.objects import agent as agent_obj from neutron.objects import l3_hamode from neutron.objects import l3agent as rb_obj +from neutron import quota from neutron.scheduler import l3_agent_scheduler from neutron.tests import base from neutron.tests.common import helpers @@ -1493,6 +1494,10 @@ class L3HATestCaseMixin(testlib_api.SqlTestCase, 'neutron.scheduler.l3_agent_scheduler.ChanceScheduler' ) self._register_l3_agents() + make_res = mock.patch.object(quota.QuotaEngine, 'make_reservation') + self.mock_make_res = make_res.start() + commit_res = mock.patch.object(quota.QuotaEngine, 'commit_reservation') + self.mock_quota_commit_res = commit_res.start() @staticmethod def get_router_l3_agent_binding(context, router_id, l3_agent_id=None,