Default security group update
1. What is the problem? Currently when users update the default security group it will raise an exception. So we can't update default security group. When we create or delete rules it uses a synchronous way to create or delete the related rules in bottom pod. In order to accelerate response we need to use an asynchronous way. 2. What is the solution to the problem? Enable default security group to support add rules and delete rules. Using Xjob to create or delete related rules in bottom pod. 3. What the features need to be implemented to the Tricircle to realize the solution? Allow users to update default security group. Change-Id: Ibf896f15390b656bda6d438bd3176e4b15dd359c
This commit is contained in:
parent
8a81cebe14
commit
25ada0602e
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
features:
|
||||
- Support updating default security group using asynchronous methods.
|
|
@ -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')
|
||||
|
|
|
@ -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='')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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]))
|
||||
|
|
|
@ -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():
|
||||
|
|
Loading…
Reference in New Issue