Merge "Default security group update"
This commit is contained in:
commit
a83b082511
|
@ -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