Merge "Default security group update"

This commit is contained in:
Zuul 2017-11-14 01:38:08 +00:00 committed by Gerrit Code Review
commit a83b082511
6 changed files with 128 additions and 108 deletions

View File

@ -0,0 +1,3 @@
---
features:
- Support updating default security group using asynchronous methods.

View File

@ -44,6 +44,14 @@ class CentralizedSNATPortNotFound(exceptions.NotFound):
message = _('Centralized snat port for subnet %(subnet_id)s not found')
class SecurityGroupNotFound(exceptions.NotFound):
message = _('Security group for %(sg_id)s not found')
class SecurityGroupRuleNotFound(exceptions.NotFound):
message = _('Security group rule for id %(rule_id)s not found')
class NetAttachedToNonLocalRouter(exceptions.Conflict):
message = _('Network %(network_id)s has already been attached to non '
'local router %(router_id)s')

View File

@ -14,29 +14,17 @@
# under the License.
from neutron.db import securitygroups_db
import neutronclient.common.exceptions as q_exceptions
from tricircle.common import constants
from tricircle.common import context
import tricircle.db.api as db_api
from tricircle.common import xrpcapi
import tricircle.network.exceptions as n_exceptions
class TricircleSecurityGroupMixin(securitygroups_db.SecurityGroupDbMixin):
@staticmethod
def _safe_create_security_group_rule(t_context, client, body):
try:
client.create_security_group_rules(t_context, body)
except q_exceptions.Conflict:
return
@staticmethod
def _safe_delete_security_group_rule(t_context, client, _id):
try:
client.delete_security_group_rules(t_context, _id)
except q_exceptions.NotFound:
return
def __init__(self):
super(TricircleSecurityGroupMixin, self).__init__()
self.xjob_handler = xrpcapi.XJobAPI()
@staticmethod
def _compare_rule(rule1, rule2):
@ -52,57 +40,43 @@ class TricircleSecurityGroupMixin(securitygroups_db.SecurityGroupDbMixin):
raise n_exceptions.RemoteGroupNotSupported()
sg_id = rule['security_group_id']
sg = self.get_security_group(q_context, sg_id)
if sg['name'] == 'default':
raise n_exceptions.DefaultGroupUpdateNotSupported()
if not sg:
raise n_exceptions.SecurityGroupNotFound(sg_id=sg_id)
new_rule = super(TricircleSecurityGroupMixin,
self).create_security_group_rule(q_context,
security_group_rule)
t_context = context.get_context_from_neutron_context(q_context)
mappings = db_api.get_bottom_mappings_by_top_id(
t_context, sg_id, constants.RT_SG)
try:
for pod, b_sg_id in mappings:
client = self._get_client(pod['region_name'])
rule['security_group_id'] = b_sg_id
self._safe_create_security_group_rule(
t_context, client, {'security_group_rule': rule})
self.xjob_handler.configure_security_group_rules(
t_context, rule['project_id'])
except Exception:
super(TricircleSecurityGroupMixin,
self).delete_security_group_rule(q_context, new_rule['id'])
raise n_exceptions.BottomPodOperationFailure(
resource='security group rule', region_name=pod['region_name'])
resource='security group rule', region_name='')
return new_rule
def delete_security_group_rule(self, q_context, _id):
rule = self.get_security_group_rule(q_context, _id)
if not rule:
raise n_exceptions.SecurityGroupRuleNotFound(rule_id=_id)
if rule['remote_group_id']:
raise n_exceptions.RemoteGroupNotSupported()
sg_id = rule['security_group_id']
sg = self.get_security_group(q_context, sg_id)
if sg['name'] == 'default':
raise n_exceptions.DefaultGroupUpdateNotSupported()
t_context = context.get_context_from_neutron_context(q_context)
mappings = db_api.get_bottom_mappings_by_top_id(
t_context, sg_id, constants.RT_SG)
try:
for pod, b_sg_id in mappings:
client = self._get_client(pod['region_name'])
rule['security_group_id'] = b_sg_id
b_sg = client.get_security_groups(t_context, b_sg_id)
for b_rule in b_sg['security_group_rules']:
if not self._compare_rule(b_rule, rule):
continue
self._safe_delete_security_group_rule(t_context, client,
b_rule['id'])
break
except Exception:
raise n_exceptions.BottomPodOperationFailure(
resource='security group rule', region_name=pod['region_name'])
if not sg:
raise n_exceptions.SecurityGroupNotFound(sg_id=sg_id)
super(TricircleSecurityGroupMixin,
self).delete_security_group_rule(q_context, _id)
t_context = context.get_context_from_neutron_context(q_context)
try:
self.xjob_handler.configure_security_group_rules(
t_context, rule['project_id'])
except Exception:
raise n_exceptions.BottomPodOperationFailure(
resource='security group rule', region_name='')

View File

@ -442,22 +442,32 @@ class FakeClient(test_utils.FakeClient):
def delete_floatingips(self, ctx, _id):
self.delete_resources('floatingip', ctx, _id)
@staticmethod
def _compare_rule(rule1, rule2):
for key in ('direction', 'remote_ip_prefix', 'protocol', 'ethertype',
'port_range_max', 'port_range_min'):
if rule1[key] != rule2[key]:
return False
return True
def create_security_group_rules(self, ctx, body):
sg_id = body['security_group_rule']['security_group_id']
sg_id = body['security_group_rules'][0]['security_group_id']
res_list = self._res_map[self.region_name]['security_group']
for sg in res_list:
if sg['id'] == sg_id:
target_sg = sg
new_rule = copy.copy(body['security_group_rule'])
new_rules = copy.copy(body['security_group_rules'])
match_found = False
for rule in target_sg['security_group_rules']:
old_rule = copy.copy(rule)
if new_rule == old_rule:
match_found = True
break
for new_rule in new_rules:
for rule in target_sg['security_group_rules']:
if self._compare_rule(rule, new_rule):
match_found = True
break
if not match_found:
new_rule['id'] = uuidutils.generate_uuid()
if match_found:
raise q_exceptions.Conflict()
target_sg['security_group_rules'].append(body['security_group_rule'])
target_sg['security_group_rules'].extend(body['security_group_rules'])
def delete_security_group_rules(self, ctx, rule_id):
res_list = self._res_map[self.region_name]['security_group']
@ -477,6 +487,9 @@ class FakeClient(test_utils.FakeClient):
def get_security_group(self, context, _id, fields=None, tenant_id=None):
pass
def list_security_groups(self, ctx, sg_filters):
return self.list_resources('security_group', ctx, sg_filters)
def update_floatingip_dict(fip_dict, update_dict):
if not update_dict.get('port_id'):
@ -586,7 +599,8 @@ class FakeRPCAPI(FakeBaseRPCAPI):
pass
def configure_security_group_rules(self, ctxt, project_id):
pass
self.xmanager.configure_security_group_rules(
ctxt, payload={constants.JT_SEG_RULE_SETUP: project_id})
def setup_shadow_ports(self, ctxt, project_id, pod_id, net_id):
combine_id = '%s#%s' % (pod_id, net_id)
@ -3314,19 +3328,6 @@ class PluginTest(unittest.TestCase,
'pod_id_1', TOP_SGS,
TOP_SG_RULES, BOTTOM1_SGS)
@patch.object(context, 'get_context_from_neutron_context')
def test_handle_default_sg_invalid_input(self, mock_context):
self._basic_pod_route_setup()
fake_plugin = FakePlugin()
q_ctx = FakeNeutronContext()
t_ctx = context.get_db_context()
mock_context.return_value = t_ctx
self._test_handle_default_sg_invalid_input(fake_plugin, q_ctx, t_ctx,
'pod_id_1', TOP_SGS,
TOP_SG_RULES, BOTTOM1_SGS)
@patch.object(FakeClient, 'create_security_group_rules')
@patch.object(context, 'get_context_from_neutron_context')
def test_create_security_group_rule_exception(self, mock_context,
@ -3358,6 +3359,19 @@ class PluginTest(unittest.TestCase,
fake_plugin, q_ctx, t_ctx, 'pod_id_1', TOP_SGS, TOP_SG_RULES,
BOTTOM1_SGS)
@patch.object(context, 'get_context_from_neutron_context')
def test_update_default_sg(self, mock_context):
self._basic_pod_route_setup()
fake_plugin = FakePlugin()
q_ctx = FakeNeutronContext()
t_ctx = context.get_db_context()
mock_context.return_value = t_ctx
self._test_update_default_sg(fake_plugin, q_ctx, t_ctx,
'pod_id_1', TOP_SGS,
TOP_SG_RULES, BOTTOM1_SGS)
@patch.object(FakeBaseRPCAPI, 'setup_shadow_ports')
@patch.object(FakeClient, 'update_ports')
@patch.object(context, 'get_context_from_neutron_context')

View File

@ -37,6 +37,14 @@ class TricircleSecurityGroupTestMixin(object):
'port_range_min': None,
'ethertype': 'IPv4'}
@staticmethod
def _compare_rule(rule1, rule2):
for key in ('direction', 'remote_ip_prefix', 'protocol', 'ethertype',
'port_range_max', 'port_range_min'):
if rule1[key] != rule2[key] and str(rule1[key]) != str(rule2[key]):
return False
return True
def _test_create_security_group_rule(self, plugin, q_ctx, t_ctx, pod_id,
top_sgs, bottom1_sgs):
t_sg_id = uuidutils.generate_uuid()
@ -67,9 +75,6 @@ class TricircleSecurityGroupTestMixin(object):
self.assertEqual(1, len(bottom1_sgs[0]['security_group_rules']))
b_rule = bottom1_sgs[0]['security_group_rules'][0]
self.assertEqual(b_sg_id, b_rule['security_group_id'])
rule['security_group_rule'].pop('security_group_id', None)
b_rule.pop('security_group_id', None)
self.assertEqual(rule['security_group_rule'], b_rule)
def _test_delete_security_group_rule(self, plugin, q_ctx, t_ctx, pod_id,
top_sgs, top_rules, bottom1_sgs):
@ -149,41 +154,6 @@ class TricircleSecurityGroupTestMixin(object):
self.assertRaises(exceptions.RemoteGroupNotSupported,
plugin.delete_security_group_rule, q_ctx, t_rule1_id)
def _test_handle_default_sg_invalid_input(self, plugin, q_ctx, t_ctx,
pod_id, top_sgs, top_rules,
bottom1_sgs):
t_sg_id = uuidutils.generate_uuid()
t_rule1_id = uuidutils.generate_uuid()
t_rule2_id = uuidutils.generate_uuid()
b_sg_id = uuidutils.generate_uuid()
project_id = 'test_prject_id'
t_rule1 = self._build_test_rule(
t_rule1_id, t_sg_id, project_id, '10.0.0.0/24')
t_rule2 = self._build_test_rule(
t_rule2_id, t_sg_id, project_id, '10.0.1.0/24')
t_sg = {'id': t_sg_id, 'name': 'default', 'description': '',
'tenant_id': project_id,
'security_group_rules': [t_rule1]}
b_sg = {'id': b_sg_id, 'name': t_sg_id, 'description': '',
'tenant_id': project_id,
'security_group_rules': []}
top_sgs.append(t_sg)
top_rules.append(t_rule1)
bottom1_sgs.append(b_sg)
route1 = {
'top_id': t_sg_id,
'pod_id': pod_id,
'bottom_id': b_sg_id,
'resource_type': constants.RT_SG}
with t_ctx.session.begin():
core.create_resource(t_ctx, models.ResourceRouting, route1)
self.assertRaises(exceptions.DefaultGroupUpdateNotSupported,
plugin.create_security_group_rule, q_ctx,
{'security_group_rule': t_rule2})
self.assertRaises(exceptions.DefaultGroupUpdateNotSupported,
plugin.delete_security_group_rule, q_ctx, t_rule1_id)
def _test_create_security_group_rule_exception(
self, plugin, q_ctx, t_ctx, pod_id, top_sgs, bottom1_sgs):
t_sg_id = uuidutils.generate_uuid()
@ -242,3 +212,51 @@ class TricircleSecurityGroupTestMixin(object):
self.assertRaises(exceptions.BottomPodOperationFailure,
plugin.delete_security_group_rule, q_ctx, t_rule_id)
def _test_update_default_sg(self, plugin, q_ctx, t_ctx,
pod_id, top_sgs, top_rules,
bottom1_sgs):
t_sg_id = uuidutils.generate_uuid()
t_rule1_id = uuidutils.generate_uuid()
t_rule2_id = uuidutils.generate_uuid()
b_sg_id = uuidutils.generate_uuid()
project_id = 'test_prject_id'
t_rule1 = self._build_test_rule(
t_rule1_id, t_sg_id, project_id, '10.0.0.0/24')
t_sg = {'id': t_sg_id, 'name': 'default', 'description': '',
'tenant_id': project_id,
'security_group_rules': [t_rule1]}
b_sg = {'id': b_sg_id, 'name': 'default', 'description': '',
'tenant_id': project_id,
'security_group_rules': []}
top_sgs.append(t_sg)
top_rules.append(t_rule1)
bottom1_sgs.append(b_sg)
route1 = {
'top_id': t_sg_id,
'pod_id': pod_id,
'bottom_id': b_sg_id,
'resource_type': constants.RT_SG}
with t_ctx.session.begin():
core.create_resource(t_ctx, models.ResourceRouting, route1)
t_rule2 = {
'security_group_rule': self._build_test_rule(
t_rule2_id, t_sg_id, project_id, '10.0.1.0/24')}
plugin.create_security_group_rule(q_ctx, t_rule2)
self.assertEqual(len(top_sgs[0]['security_group_rules']),
len(bottom1_sgs[0]['security_group_rules']))
for i in range(len(bottom1_sgs[0]['security_group_rules'])):
self.assertTrue(self._compare_rule(
bottom1_sgs[0]['security_group_rules'][i],
top_sgs[0]['security_group_rules'][i]))
plugin.delete_security_group_rule(q_ctx, t_rule1_id)
self.assertEqual(len(bottom1_sgs[0]['security_group_rules']),
len(top_sgs[0]['security_group_rules']))
for i in range(len(bottom1_sgs[0]['security_group_rules'])):
self.assertTrue(self._compare_rule(
bottom1_sgs[0]['security_group_rules'][i],
top_sgs[0]['security_group_rules'][i]))

View File

@ -456,6 +456,9 @@ class FakeSession(object):
def delete(self, model_obj):
unlink_models(self.resource_store.store_map['routers'], model_obj,
'router_id', 'id', 'attached_ports', 'port_id', 'id')
unlink_models(self.resource_store.store_map['securitygroups'],
model_obj, 'security_group_id', 'id',
'security_group_rules', 'id', 'id')
self._cascade_delete(model_obj, 'port_id', 'ipallocations', 'id')
key = self.delete_hook(model_obj)
for res_list in self.resource_store.store_map.values():