Don't create default SG in transaction
Creating default security groups should not be done inside another transaction because the creation code emits AFTER events. Handlers for the push notification code expect to be able to read from the DB during an after event using a new transaction, which will not work if they are inside of exising transactions. This patch adjusts the default security group creation logic to avoid using layered transactions for the default security group creation and adjusts ML2 to stop calling ensure_default_security_group inside of a port/network transaction. Partially-Implements: blueprint push-notifications Change-Id: Iaa83c8664d5bfde31fdcdd694f6f18d9ef9bf14a
This commit is contained in:
parent
499f3c6d56
commit
ffc4489a58
|
@ -83,6 +83,11 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
|||
|
||||
if not default_sg:
|
||||
self._ensure_default_security_group(context, tenant_id)
|
||||
else:
|
||||
existing_def_sg_id = self._get_default_sg_id(context, tenant_id)
|
||||
if existing_def_sg_id is not None:
|
||||
# default already exists, return it
|
||||
return self.get_security_group(context, existing_def_sg_id)
|
||||
|
||||
with db_api.autonested_transaction(context.session):
|
||||
security_group_db = sg_models.SecurityGroup(id=s.get('id') or (
|
||||
|
@ -661,27 +666,30 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
|||
port[ext_sg.SECURITYGROUPS] = (security_group_ids and
|
||||
list(security_group_ids) or [])
|
||||
|
||||
def _ensure_default_security_group(self, context, tenant_id):
|
||||
"""Create a default security group if one doesn't exist.
|
||||
|
||||
:returns: the default security group id for given tenant.
|
||||
"""
|
||||
def _get_default_sg_id(self, context, tenant_id):
|
||||
try:
|
||||
query = self._model_query(context, sg_models.DefaultSecurityGroup)
|
||||
default_group = query.filter_by(tenant_id=tenant_id).one()
|
||||
return default_group['security_group_id']
|
||||
except exc.NoResultFound:
|
||||
security_group = {
|
||||
'security_group':
|
||||
{'name': 'default',
|
||||
'tenant_id': tenant_id,
|
||||
'description': _('Default security group')}
|
||||
}
|
||||
# starting a transaction before create to avoid db retries
|
||||
with context.session.begin(subtransactions=True):
|
||||
sg_id = self.create_security_group(
|
||||
context, security_group, default_sg=True)['id']
|
||||
return sg_id
|
||||
pass
|
||||
|
||||
def _ensure_default_security_group(self, context, tenant_id):
|
||||
"""Create a default security group if one doesn't exist.
|
||||
|
||||
:returns: the default security group id for given tenant.
|
||||
"""
|
||||
existing = self._get_default_sg_id(context, tenant_id)
|
||||
if existing is not None:
|
||||
return existing
|
||||
security_group = {
|
||||
'security_group':
|
||||
{'name': 'default',
|
||||
'tenant_id': tenant_id,
|
||||
'description': _('Default security group')}
|
||||
}
|
||||
return self.create_security_group(context, security_group,
|
||||
default_sg=True)['id']
|
||||
|
||||
def _get_security_groups_on_port(self, context, port):
|
||||
"""Check that all security groups on port belong to tenant.
|
||||
|
|
|
@ -740,7 +740,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||
tenant_id = net_data['tenant_id']
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
self._ensure_default_security_group(context, tenant_id)
|
||||
net_db = self.create_network_db(context, network)
|
||||
result = self._make_network_dict(net_db, process_extensions=False,
|
||||
context=context)
|
||||
|
@ -776,6 +775,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||
@utils.transaction_guard
|
||||
@db_api.retry_if_session_inactive()
|
||||
def create_network(self, context, network):
|
||||
self._ensure_default_security_group(context,
|
||||
network['network']['tenant_id'])
|
||||
result, mech_context = self._create_network_db(context, network)
|
||||
kwargs = {'context': context, 'network': result}
|
||||
registry.notify(resources.NETWORK, events.AFTER_CREATE, self, **kwargs)
|
||||
|
@ -792,6 +793,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||
@utils.transaction_guard
|
||||
@db_api.retry_if_session_inactive()
|
||||
def create_network_bulk(self, context, networks):
|
||||
tenants = {n['network']['tenant_id'] for n in networks['networks']}
|
||||
map(lambda t: self._ensure_default_security_group(context, t), tenants)
|
||||
objects = self._create_bulk_ml2(attributes.NETWORK, context, networks)
|
||||
return [obj['result'] for obj in objects]
|
||||
|
||||
|
@ -1245,6 +1248,9 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||
if not attrs.get('status'):
|
||||
attrs['status'] = const.PORT_STATUS_DOWN
|
||||
|
||||
# NOTE(kevinbenton): triggered outside of transaction since it
|
||||
# emits 'AFTER' events if it creates.
|
||||
self._ensure_default_security_group(context, attrs['tenant_id'])
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
dhcp_opts = attrs.get(edo_ext.EXTRADHCPOPTS, [])
|
||||
|
|
|
@ -20,6 +20,9 @@ import mock
|
|||
from neutron_lib import constants as const
|
||||
from neutron_lib.plugins import directory
|
||||
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import registry
|
||||
from neutron.callbacks import resources
|
||||
from neutron import context
|
||||
from neutron.extensions import securitygroup as ext_sg
|
||||
from neutron.tests import tools
|
||||
|
@ -148,6 +151,20 @@ class TestMl2SecurityGroups(Ml2SecurityGroupsTestCase,
|
|||
# the or_ function should only have one argument
|
||||
or_mock.assert_called_once_with(mock.ANY)
|
||||
|
||||
def test_security_groups_created_outside_transaction(self):
|
||||
def record_after_state(r, e, t, context, *args, **kwargs):
|
||||
self.was_active = context.session.is_active
|
||||
|
||||
registry.subscribe(record_after_state, resources.SECURITY_GROUP,
|
||||
events.AFTER_CREATE)
|
||||
with self.subnet() as s:
|
||||
self.assertFalse(self.was_active)
|
||||
self._delete(
|
||||
'security-groups',
|
||||
self._list('security-groups')['security_groups'][0]['id'])
|
||||
with self.port(subnet=s):
|
||||
self.assertFalse(self.was_active)
|
||||
|
||||
|
||||
class TestMl2SGServerRpcCallBack(
|
||||
Ml2SecurityGroupsTestCase,
|
||||
|
|
Loading…
Reference in New Issue