Merge "Add security groups events"
This commit is contained in:
commit
876717baf8
|
@ -14,10 +14,14 @@ PORT = 'port'
|
|||
ROUTER = 'router'
|
||||
ROUTER_GATEWAY = 'router_gateway'
|
||||
ROUTER_INTERFACE = 'router_interface'
|
||||
SECURITY_GROUP = 'security_group'
|
||||
SECURITY_GROUP_RULE = 'security_group_rule'
|
||||
|
||||
VALID = (
|
||||
PORT,
|
||||
ROUTER,
|
||||
ROUTER_GATEWAY,
|
||||
ROUTER_INTERFACE,
|
||||
SECURITY_GROUP,
|
||||
SECURITY_GROUP_RULE,
|
||||
)
|
||||
|
|
|
@ -21,6 +21,10 @@ from sqlalchemy.orm import exc
|
|||
from sqlalchemy.orm import scoped_session
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import exceptions
|
||||
from neutron.callbacks import registry
|
||||
from neutron.callbacks import resources
|
||||
from neutron.common import constants
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db import db_base_plugin_v2
|
||||
|
@ -125,6 +129,21 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
|||
a given tenant if it does not exist.
|
||||
"""
|
||||
s = security_group['security_group']
|
||||
kwargs = {
|
||||
'context': context,
|
||||
'security_group': s,
|
||||
'is_default': default_sg,
|
||||
}
|
||||
# NOTE(armax): a callback exception here will prevent the request
|
||||
# from being processed. This is a hook point for backend's validation;
|
||||
# we raise to propagate the reason for the failure.
|
||||
try:
|
||||
registry.notify(
|
||||
resources.SECURITY_GROUP, events.BEFORE_CREATE, self,
|
||||
**kwargs)
|
||||
except exceptions.CallbackFailure as e:
|
||||
raise ext_sg.SecurityGroupConflict(reason=e)
|
||||
|
||||
tenant_id = self._get_tenant_id_for_create(context, s)
|
||||
|
||||
if not default_sg:
|
||||
|
@ -159,7 +178,12 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
|||
ethertype=ethertype)
|
||||
context.session.add(egress_rule)
|
||||
|
||||
return self._make_security_group_dict(security_group_db)
|
||||
secgroup_dict = self._make_security_group_dict(security_group_db)
|
||||
|
||||
kwargs['security_group'] = secgroup_dict
|
||||
registry.notify(resources.SECURITY_GROUP, events.AFTER_CREATE, self,
|
||||
**kwargs)
|
||||
return secgroup_dict
|
||||
|
||||
def get_security_groups(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None,
|
||||
|
@ -229,17 +253,58 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
|||
|
||||
if sg['name'] == 'default' and not context.is_admin:
|
||||
raise ext_sg.SecurityGroupCannotRemoveDefault()
|
||||
kwargs = {
|
||||
'context': context,
|
||||
'security_group_id': id,
|
||||
'security_group': sg,
|
||||
}
|
||||
# NOTE(armax): a callback exception here will prevent the request
|
||||
# from being processed. This is a hook point for backend's validation;
|
||||
# we raise to propagate the reason for the failure.
|
||||
try:
|
||||
registry.notify(
|
||||
resources.SECURITY_GROUP, events.BEFORE_DELETE, self,
|
||||
**kwargs)
|
||||
except exceptions.CallbackFailure as e:
|
||||
reason = _('cannot be deleted due to %s') % e
|
||||
raise ext_sg.SecurityGroupInUse(id=id, reason=reason)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
context.session.delete(sg)
|
||||
|
||||
kwargs.pop('security_group')
|
||||
registry.notify(resources.SECURITY_GROUP, events.AFTER_DELETE, self,
|
||||
**kwargs)
|
||||
|
||||
def update_security_group(self, context, id, security_group):
|
||||
s = security_group['security_group']
|
||||
|
||||
kwargs = {
|
||||
'context': context,
|
||||
'security_group_id': id,
|
||||
'security_group': s,
|
||||
}
|
||||
# NOTE(armax): a callback exception here will prevent the request
|
||||
# from being processed. This is a hook point for backend's validation;
|
||||
# we raise to propagate the reason for the failure.
|
||||
try:
|
||||
registry.notify(
|
||||
resources.SECURITY_GROUP, events.BEFORE_UPDATE, self,
|
||||
**kwargs)
|
||||
except exceptions.CallbackFailure as e:
|
||||
raise ext_sg.SecurityGroupConflict(reason=e)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
sg = self._get_security_group(context, id)
|
||||
if sg['name'] == 'default' and 'name' in s:
|
||||
raise ext_sg.SecurityGroupCannotUpdateDefault()
|
||||
sg.update(s)
|
||||
return self._make_security_group_dict(sg)
|
||||
sg_dict = self._make_security_group_dict(sg)
|
||||
|
||||
kwargs['security_group'] = sg_dict
|
||||
registry.notify(resources.SECURITY_GROUP, events.AFTER_UPDATE, self,
|
||||
**kwargs)
|
||||
return sg_dict
|
||||
|
||||
def _make_security_group_dict(self, security_group, fields=None):
|
||||
res = {'id': security_group['id'],
|
||||
|
@ -313,9 +378,29 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
|||
return ret
|
||||
|
||||
def create_security_group_rule(self, context, security_group_rule):
|
||||
kwargs = {
|
||||
'context': context,
|
||||
'security_group_rule': security_group_rule,
|
||||
}
|
||||
# NOTE(armax): a callback exception here will prevent the request
|
||||
# from being processed. This is a hook point for backend's validation;
|
||||
# we raise to propagate the reason for the failure.
|
||||
try:
|
||||
registry.notify(
|
||||
resources.SECURITY_GROUP_RULE, events.BEFORE_CREATE, self,
|
||||
**kwargs)
|
||||
except exceptions.CallbackFailure as e:
|
||||
raise ext_sg.SecurityGroupConflict(reason=e)
|
||||
|
||||
bulk_rule = {'security_group_rules': [security_group_rule]}
|
||||
return self.create_security_group_rule_bulk_native(context,
|
||||
bulk_rule)[0]
|
||||
sg_rule_dict = self.create_security_group_rule_bulk_native(
|
||||
context, bulk_rule)[0]
|
||||
|
||||
kwargs['security_group_rule'] = sg_rule_dict
|
||||
registry.notify(
|
||||
resources.SECURITY_GROUP_RULE, events.AFTER_CREATE, self,
|
||||
**kwargs)
|
||||
return sg_rule_dict
|
||||
|
||||
def _get_ip_proto_number(self, protocol):
|
||||
if protocol is None:
|
||||
|
@ -494,11 +579,30 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
|
|||
return sgr
|
||||
|
||||
def delete_security_group_rule(self, context, id):
|
||||
kwargs = {
|
||||
'context': context,
|
||||
'security_group_rule_id': id
|
||||
}
|
||||
# NOTE(armax): a callback exception here will prevent the request
|
||||
# from being processed. This is a hook point for backend's validation;
|
||||
# we raise to propagate the reason for the failure.
|
||||
try:
|
||||
registry.notify(
|
||||
resources.SECURITY_GROUP_RULE, events.BEFORE_DELETE, self,
|
||||
**kwargs)
|
||||
except exceptions.CallbackFailure as e:
|
||||
reason = _('cannot be deleted due to %s') % e
|
||||
raise ext_sg.SecurityGroupRuleInUse(id=id, reason=reason)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
query = self._model_query(context, SecurityGroupRule)
|
||||
if query.filter(SecurityGroupRule.id == id).delete() == 0:
|
||||
raise ext_sg.SecurityGroupRuleNotFound(id=id)
|
||||
|
||||
registry.notify(
|
||||
resources.SECURITY_GROUP_RULE, events.AFTER_DELETE, self,
|
||||
**kwargs)
|
||||
|
||||
def _extend_port_dict_security_group(self, port_res, port_db):
|
||||
# Security group bindings will be retrieved from the sqlalchemy
|
||||
# model. As they're loaded eagerly with ports because of the
|
||||
|
|
|
@ -50,7 +50,12 @@ class SecurityGroupMissingIcmpType(nexception.InvalidInput):
|
|||
|
||||
|
||||
class SecurityGroupInUse(nexception.InUse):
|
||||
message = _("Security Group %(id)s in use.")
|
||||
message = _("Security Group %(id)s %(reason)s.")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if 'reason' not in kwargs:
|
||||
kwargs['reason'] = _("in use")
|
||||
super(SecurityGroupInUse, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class SecurityGroupCannotRemoveDefault(nexception.InUse):
|
||||
|
@ -106,10 +111,23 @@ class SecurityGroupRuleExists(nexception.InUse):
|
|||
message = _("Security group rule already exists. Rule id is %(id)s.")
|
||||
|
||||
|
||||
class SecurityGroupRuleInUse(nexception.InUse):
|
||||
message = _("Security Group Rule %(id)s %(reason)s.")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if 'reason' not in kwargs:
|
||||
kwargs['reason'] = _("in use")
|
||||
super(SecurityGroupRuleInUse, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class SecurityGroupRuleParameterConflict(nexception.InvalidInput):
|
||||
message = _("Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s")
|
||||
|
||||
|
||||
class SecurityGroupConflict(nexception.Conflict):
|
||||
message = _("Error %(reason)s while attempting the operation.")
|
||||
|
||||
|
||||
def convert_protocol(value):
|
||||
if value is None:
|
||||
return
|
||||
|
|
|
@ -35,6 +35,8 @@ from oslo_utils import strutils
|
|||
import testtools
|
||||
|
||||
from neutron.agent.linux import external_process
|
||||
from neutron.callbacks import manager as registry_manager
|
||||
from neutron.callbacks import registry
|
||||
from neutron.common import config
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.db import agentschedulers_db
|
||||
|
@ -266,6 +268,7 @@ class BaseTestCase(DietTestCase):
|
|||
|
||||
self.setup_rpc_mocks()
|
||||
self.setup_config()
|
||||
self.setup_test_registry_instance()
|
||||
|
||||
policy.init()
|
||||
self.addCleanup(policy.reset)
|
||||
|
@ -327,6 +330,12 @@ class BaseTestCase(DietTestCase):
|
|||
self.addCleanup(n_rpc.cleanup)
|
||||
n_rpc.init(CONF)
|
||||
|
||||
def setup_test_registry_instance(self):
|
||||
"""Give a private copy of the registry to each test."""
|
||||
self._callback_manager = registry_manager.CallbacksManager()
|
||||
mock.patch.object(registry, '_get_callback_manager',
|
||||
return_value=self._callback_manager).start()
|
||||
|
||||
def setup_config(self, args=None):
|
||||
"""Tests that need a non-default config can override this method."""
|
||||
self.config_parse(args=args)
|
||||
|
|
|
@ -38,7 +38,6 @@ from neutron.agent.linux import external_process
|
|||
from neutron.agent.linux import ip_lib
|
||||
from neutron.agent.linux import utils
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import manager
|
||||
from neutron.callbacks import registry
|
||||
from neutron.callbacks import resources
|
||||
from neutron.common import config as common_config
|
||||
|
@ -65,12 +64,6 @@ class L3AgentTestFramework(base.BaseLinuxTestCase):
|
|||
def setUp(self):
|
||||
super(L3AgentTestFramework, self).setUp()
|
||||
mock.patch('neutron.agent.l3.agent.L3PluginApi').start()
|
||||
|
||||
# TODO(pcm): Move this to BaseTestCase, if we find that more tests
|
||||
# use this mechanism.
|
||||
self._callback_manager = manager.CallbacksManager()
|
||||
mock.patch.object(registry, '_get_callback_manager',
|
||||
return_value=self._callback_manager).start()
|
||||
self.agent = self._configure_agent('agent1')
|
||||
|
||||
def _get_config_opts(self):
|
||||
|
|
|
@ -11,8 +11,12 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import contextlib
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from neutron.callbacks import exceptions
|
||||
from neutron.callbacks import registry
|
||||
from neutron import context
|
||||
from neutron.db import common_db_mixin
|
||||
from neutron.db import securitygroups_db
|
||||
|
@ -32,6 +36,47 @@ class SecurityGroupDbMixinTestCase(testlib_api.SqlTestCase):
|
|||
self.ctx = context.get_admin_context()
|
||||
self.mixin = SecurityGroupDbMixinImpl()
|
||||
|
||||
def test_create_security_group_conflict(self):
|
||||
with mock.patch.object(registry, "notify") as mock_notify:
|
||||
mock_notify.side_effect = exceptions.CallbackFailure(Exception())
|
||||
secgroup = {'security_group': mock.ANY}
|
||||
with testtools.ExpectedException(
|
||||
securitygroup.SecurityGroupConflict):
|
||||
self.mixin.create_security_group(self.ctx, secgroup)
|
||||
|
||||
def test_delete_security_group_in_use(self):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.mixin, '_get_port_security_group_bindings'),
|
||||
mock.patch.object(self.mixin, '_get_security_group'),
|
||||
mock.patch.object(registry, "notify"),
|
||||
) as (_, _, mock_notify):
|
||||
mock_notify.side_effect = exceptions.CallbackFailure(Exception())
|
||||
with testtools.ExpectedException(
|
||||
securitygroup.SecurityGroupInUse):
|
||||
self.mixin.delete_security_group(self.ctx, mock.ANY)
|
||||
|
||||
def test_update_security_group_conflict(self):
|
||||
with mock.patch.object(registry, "notify") as mock_notify:
|
||||
mock_notify.side_effect = exceptions.CallbackFailure(Exception())
|
||||
secgroup = {'security_group': mock.ANY}
|
||||
with testtools.ExpectedException(
|
||||
securitygroup.SecurityGroupConflict):
|
||||
self.mixin.update_security_group(self.ctx, 'foo_id', secgroup)
|
||||
|
||||
def test_create_security_group_rule_conflict(self):
|
||||
with mock.patch.object(registry, "notify") as mock_notify:
|
||||
mock_notify.side_effect = exceptions.CallbackFailure(Exception())
|
||||
with testtools.ExpectedException(
|
||||
securitygroup.SecurityGroupConflict):
|
||||
self.mixin.create_security_group_rule(self.ctx, mock.ANY)
|
||||
|
||||
def test_delete_security_group_rule_in_use(self):
|
||||
with mock.patch.object(registry, "notify") as mock_notify:
|
||||
mock_notify.side_effect = exceptions.CallbackFailure(Exception())
|
||||
with testtools.ExpectedException(
|
||||
securitygroup.SecurityGroupRuleInUse):
|
||||
self.mixin.delete_security_group_rule(self.ctx, mock.ANY)
|
||||
|
||||
def test_delete_security_group_rule_raise_error_on_not_found(self):
|
||||
with testtools.ExpectedException(
|
||||
securitygroup.SecurityGroupRuleNotFound):
|
||||
|
|
Loading…
Reference in New Issue