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 936bd67aa4)
This commit is contained in:
Rodolfo Alonso Hernandez 2020-01-08 13:31:29 +00:00 committed by Bernard Cafarelli
parent 496132a156
commit ff607c3f22
No known key found for this signature in database
GPG Key ID: 9531F08245465A52
8 changed files with 67 additions and 0 deletions

View File

@ -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
@resource_extend.has_resource_extenders
@ -108,6 +109,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
@ -127,6 +134,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)

View File

@ -334,3 +334,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})

View File

@ -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
@ -517,3 +518,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)

View File

@ -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,

View File

@ -45,6 +45,7 @@ from neutron.db import common_db_mixin
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()

View File

@ -26,6 +26,7 @@ import testtools
from neutron.db import common_db_mixin
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
@ -73,6 +74,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:

View File

@ -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
@ -2000,6 +2001,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):
@ -2720,6 +2725,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):

View File

@ -45,6 +45,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
@ -1477,6 +1478,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,