diff --git a/tricircle/common/client.py b/tricircle/common/client.py index b9a482f2..8e3d28f3 100644 --- a/tricircle/common/client.py +++ b/tricircle/common/client.py @@ -62,7 +62,13 @@ client_opts = [ cfg.StrOpt('admin_tenant_domain_name', default='Default', help='tenant domain name of admin account, needed when' - ' auto_refresh_endpoint set to True') + ' auto_refresh_endpoint set to True'), + cfg.StrOpt('ew_bridge_cidr', + default='100.0.0.0/9', + help='cidr pool of the east-west bridge network'), + cfg.StrOpt('ns_bridge_cidr', + default='100.128.0.0/9', + help='cidr pool of the north-south bridge network') ] client_opt_group = cfg.OptGroup('client') cfg.CONF.register_group(client_opt_group) diff --git a/tricircle/common/constants.py b/tricircle/common/constants.py index d2333c29..267e07c0 100644 --- a/tricircle/common/constants.py +++ b/tricircle/common/constants.py @@ -79,6 +79,7 @@ PROFILE_REGION = 'region' JT_ROUTER = 'router' JT_ROUTER_SETUP = 'router_setup' JT_PORT_DELETE = 'port_delete' +JT_SEG_RULE_SETUP = 'seg_rule_setup' # network type NT_LOCAL = 'local' diff --git a/tricircle/common/xrpcapi.py b/tricircle/common/xrpcapi.py index 163be9c1..58f771cb 100644 --- a/tricircle/common/xrpcapi.py +++ b/tricircle/common/xrpcapi.py @@ -94,3 +94,8 @@ class XJobAPI(object): self.client.prepare(exchange='openstack').cast( ctxt, 'delete_server_port', payload={constants.JT_PORT_DELETE: port_id}) + + def configure_security_group_rules(self, ctxt, project_id): + self.client.prepare(exchange='openstack').cast( + ctxt, 'configure_security_group_rules', + payload={constants.JT_SEG_RULE_SETUP: project_id}) diff --git a/tricircle/network/central_plugin.py b/tricircle/network/central_plugin.py index 11f117a7..c2d5cabd 100644 --- a/tricircle/network/central_plugin.py +++ b/tricircle/network/central_plugin.py @@ -400,7 +400,11 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2, gateway_port_body) return super(TricirclePlugin, self).get_port(context, t_gateway_id) db_port = super(TricirclePlugin, self).create_port_db(context, port) - return self._make_port_dict(db_port) + self._ensure_default_security_group_on_port(context, port) + sgids = self._get_security_groups_on_port(context, port) + result = self._make_port_dict(db_port) + self._process_port_create_security_group(context, result, sgids) + return result def update_port(self, context, port_id, port): # TODO(zhiyuan) handle bottom port update @@ -419,6 +423,9 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2, t_constants.RT_SUBNET) for ip in res['fixed_ips']] entries.append((res['network_id'], t_constants.RT_NETWORK)) entries.append((res['id'], t_constants.RT_PORT)) + if res['security_groups']: + for sg_id in res['security_groups']: + entries.append((sg_id, t_constants.RT_SG)) for resource_id, resource_type in entries: if db_api.get_bottom_id_by_top_id_pod_name( @@ -441,6 +448,8 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2, admin_context, res['network_id'], interfaces[0]['device_id'], pod['pod_id']) + self.xjob_handler.configure_security_group_rules(t_ctx, + res['tenant_id']) return res def delete_port(self, context, port_id, l3_port_check=True): @@ -916,10 +925,10 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2, def _get_bridge_subnet_pool_id(self, t_ctx, q_ctx, project_id, pod, is_ew): if is_ew: pool_name = t_constants.ew_bridge_subnet_pool_name - pool_cidr = '100.0.0.0/9' + pool_cidr = cfg.CONF.client.ew_bridge_cidr else: pool_name = t_constants.ns_bridge_subnet_pool_name - pool_cidr = '100.128.0.0/9' + pool_cidr = cfg.CONF.client.ns_bridge_cidr pool_ele = {'id': pool_name} body = {'subnetpool': {'tenant_id': project_id, 'name': pool_name, diff --git a/tricircle/network/helper.py b/tricircle/network/helper.py index b466a431..ce792070 100644 --- a/tricircle/network/helper.py +++ b/tricircle/network/helper.py @@ -567,3 +567,18 @@ class NetworkHelper(object): return getattr(self.call_obj, 'get_%s' % _type)(q_ctx, _id) else: return getattr(self._get_client(), 'get_%ss' % _type)(t_ctx, _id) + + @staticmethod + def get_create_sg_rule_body(rule, sg_id, ip=None): + ip = ip or rule['remote_ip_prefix'] + # if ip is passed, this is a extended rule for remote group + return {'security_group_rule': { + 'tenant_id': rule['tenant_id'], + 'remote_group_id': None, + 'direction': rule['direction'], + 'remote_ip_prefix': ip, + 'protocol': rule.get('protocol'), + 'ethertype': rule['ethertype'], + 'port_range_max': rule.get('port_range_max'), + 'port_range_min': rule.get('port_range_min'), + 'security_group_id': sg_id}} diff --git a/tricircle/network/local_plugin.py b/tricircle/network/local_plugin.py index a7f35f22..f3cd6af8 100644 --- a/tricircle/network/local_plugin.py +++ b/tricircle/network/local_plugin.py @@ -22,6 +22,7 @@ import neutron_lib.constants as q_constants import neutron_lib.exceptions as q_exceptions from neutron.common import utils +import neutron.extensions.securitygroup as ext_sg from neutron.plugins.ml2 import plugin from tricircle.common import client # noqa @@ -381,6 +382,8 @@ class TricirclePlugin(plugin.Ml2Plugin): for field in ('name', 'device_id'): if port_body.get(field): t_port[field] = port_body[field] + + self._handle_security_group(t_ctx, context, t_port) b_port = self.core_plugin.create_port(context, {'port': t_port}) return b_port @@ -408,6 +411,7 @@ class TricirclePlugin(plugin.Ml2Plugin): raise q_exceptions.PortNotFound(port_id=_id) self._ensure_network_subnet(context, t_port) self._adapt_port_body_for_call(t_port) + self._handle_security_group(t_ctx, context, t_port) b_port = self.core_plugin.create_port(context, {'port': t_port}) return self._fields(b_port, fields) @@ -446,6 +450,7 @@ class TricirclePlugin(plugin.Ml2Plugin): for port in t_ports: self._ensure_network_subnet(context, port) self._adapt_port_body_for_call(port) + self._handle_security_group(t_ctx, context, port) b_port = self.core_plugin.create_port(context, {'port': port}) b_ports.append(self._fields(b_port, fields)) @@ -455,3 +460,68 @@ class TricirclePlugin(plugin.Ml2Plugin): t_ctx = t_context.get_context_from_neutron_context(context) self.neutron_handle.handle_delete(t_ctx, t_constants.RT_PORT, _id) self.core_plugin.delete_port(context, _id, l3_port_check) + + def _handle_security_group(self, t_ctx, q_ctx, port): + if not port['security_groups']: + raw_client = self.neutron_handle._get_client(t_ctx) + params = {'name': 'default'} + t_sgs = raw_client.list_security_groups( + **params)['security_groups'] + if t_sgs: + port['security_groups'] = [t_sgs[0]['id']] + if port['security_groups'] is q_constants.ATTR_NOT_SPECIFIED: + return + for sg_id in port['security_groups']: + self.get_security_group(q_ctx, sg_id) + + def get_security_group(self, context, _id, fields=None, tenant_id=None): + try: + return self.core_plugin.get_security_group( + context, _id, fields, tenant_id) + except q_exceptions.NotFound: + t_ctx = t_context.get_context_from_neutron_context(context) + t_sg = self.neutron_handle.handle_get(t_ctx, + 'security_group', _id) + if not t_sg: + raise ext_sg.SecurityGroupNotFound(id=_id) + self.core_plugin.create_security_group(context, + {'security_group': t_sg}) + return self.core_plugin.get_security_group( + context, _id, fields, tenant_id) + + def get_security_groups(self, context, filters=None, fields=None, + sorts=None, limit=None, marker=None, + page_reverse=False, default_sg=False): + # if id is not specified in the filter, we just return security group + # data in local Neutron server, otherwise id is specified, we need to + # retrieve network data from central Neutron server and create network + # which doesn't exist in local Neutron server. + if not filters or 'id' not in filters: + return self.core_plugin.get_security_groups( + context, filters, fields, sorts, limit, marker, page_reverse, + default_sg) + + b_sgs = self.core_plugin.get_security_groups( + context, filters, fields, sorts, limit, marker, page_reverse, + default_sg) + if len(b_sgs) == len(filters['id']): + return b_sgs + + t_ctx = t_context.get_context_from_neutron_context(context) + raw_client = self.neutron_handle._get_client(t_ctx) + params = self._construct_params(filters, sorts, limit, marker, + page_reverse) + t_sgs = raw_client.list_security_groups(**params)['security_groups'] + + t_id_set = set([sg['id'] for sg in t_sgs]) + b_id_set = set([sg['id'] for sg in b_sgs]) + missing_id_set = t_id_set - b_id_set + if missing_id_set: + missing_sgs = [sg for sg in t_sgs if ( + sg['id'] in missing_id_set)] + for sg in missing_sgs: + b_sg = self.core_plugin.create_security_group( + context, {'security_group': sg}) + b_sgs.append(self.core_plugin.get_security_group( + context, b_sg['id'], fields)) + return b_sgs diff --git a/tricircle/tests/unit/network/test_local_plugin.py b/tricircle/tests/unit/network/test_local_plugin.py index df24c8c1..b00daa2f 100644 --- a/tricircle/tests/unit/network/test_local_plugin.py +++ b/tricircle/tests/unit/network/test_local_plugin.py @@ -33,14 +33,17 @@ import tricircle.network.local_plugin as plugin TOP_NETS = [] TOP_SUBNETS = [] TOP_PORTS = [] +TOP_SGS = [] BOTTOM_NETS = [] BOTTOM_SUBNETS = [] BOTTOM_PORTS = [] -RES_LIST = [TOP_NETS, TOP_SUBNETS, TOP_PORTS, - BOTTOM_NETS, BOTTOM_SUBNETS, BOTTOM_PORTS] +BOTTOM_SGS = [] +RES_LIST = [TOP_NETS, TOP_SUBNETS, TOP_PORTS, TOP_SGS, + BOTTOM_NETS, BOTTOM_SUBNETS, BOTTOM_PORTS, BOTTOM_SGS] RES_MAP = {'network': {True: TOP_NETS, False: BOTTOM_NETS}, 'subnet': {True: TOP_SUBNETS, False: BOTTOM_SUBNETS}, - 'port': {True: TOP_PORTS, False: BOTTOM_PORTS}} + 'port': {True: TOP_PORTS, False: BOTTOM_PORTS}, + 'security_group': {True: TOP_SGS, False: BOTTOM_SGS}} def create_resource(_type, is_top, body): @@ -117,6 +120,14 @@ class FakeCorePlugin(object): limit=None, marker=None, page_reverse=False): return list_resource('port', False, filters) + def create_security_group(self, context, security_group, default_sg=False): + create_resource('security_group', False, + security_group['security_group']) + return security_group['security_group'] + + def get_security_group(self, context, _id, fields=None, tenant_id=None): + return get_resource('security_group', False, _id) + class FakeSession(object): class WithWrapper(object): @@ -176,6 +187,10 @@ class FakeClient(object): ports.append(copy.deepcopy(port)) return {'ports': ports} + def list_security_groups(self, **kwargs): + return {'security_groups': list_resource('security_group', + True, kwargs)} + class FakeNeutronHandle(object): def _get_client(self, context): @@ -210,6 +225,7 @@ class PluginTest(unittest.TestCase): network_id = uuidutils.generate_uuid() subnet_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() + sg_id = uuidutils.generate_uuid() t_net = {'id': network_id, 'tenant_id': self.tenant_id, 'name': 'net1', @@ -235,10 +251,25 @@ class PluginTest(unittest.TestCase): 'fixed_ips': [{'subnet_id': subnet_id, 'ip_address': '10.0.1.2'}], 'binding:profile': {}} + t_sg = { + 'id': sg_id, + 'tenant_id': self.tenant_id, + 'name': 'default', + 'security_group_rules': [{ + 'remote_group_id': sg_id, + 'direction': 'ingress', + 'remote_ip_prefix': None, + 'protocol': None, + 'ethertype': 'IPv4', + 'port_range_max': -1, + 'port_range_min': -1, + 'security_group_id': sg_id}] + } TOP_NETS.append(t_net) TOP_SUBNETS.append(t_subnet) TOP_PORTS.append(t_port) - return t_net, t_subnet, t_port + TOP_SGS.append(t_sg) + return t_net, t_subnet, t_port, t_sg def _validate(self, net, subnet, port): b_net = self.plugin.get_network(self.context, net['id']) @@ -273,13 +304,13 @@ class PluginTest(unittest.TestCase): @patch.object(t_context, 'get_context_from_neutron_context', new=mock.Mock) def test_get_network(self): - t_net, t_subnet, t_port = self._prepare_resource() + t_net, t_subnet, t_port, _ = self._prepare_resource() self._validate(t_net, t_subnet, t_port) @patch.object(t_context, 'get_context_from_neutron_context', new=mock.Mock) def test_get_networks(self): - t_net1, t_subnet1, t_port1 = self._prepare_resource() - t_net2, t_subnet2, t_port2 = self._prepare_resource() + t_net1, t_subnet1, t_port1, _ = self._prepare_resource() + t_net2, t_subnet2, t_port2, _ = self._prepare_resource() self.plugin.get_networks(self.context, {'id': [t_net1['id'], t_net2['id'], 'fake_net_id']}) @@ -288,10 +319,11 @@ class PluginTest(unittest.TestCase): @patch.object(t_context, 'get_context_from_neutron_context', new=mock.Mock) def test_create_port(self): - t_net, t_subnet, t_port = self._prepare_resource() + t_net, t_subnet, t_port, _ = self._prepare_resource() port = { 'port': {'network_id': t_net['id'], - 'fixed_ips': q_constants.ATTR_NOT_SPECIFIED} + 'fixed_ips': q_constants.ATTR_NOT_SPECIFIED, + 'security_groups': []} } t_port = self.plugin.create_port(self.context, port) b_port = get_resource('port', False, t_port['id']) @@ -299,7 +331,7 @@ class PluginTest(unittest.TestCase): @patch.object(t_context, 'get_context_from_neutron_context', new=mock.Mock) def test_create_port_ip_specified(self): - t_net, t_subnet, t_port = self._prepare_resource() + t_net, t_subnet, t_port, t_sg = self._prepare_resource() port_body = { 'port': {'network_id': t_net['id'], 'fixed_ips': [{'ip_address': '10.0.1.4'}]} @@ -315,14 +347,15 @@ class PluginTest(unittest.TestCase): 'mac_address': 'fa:16:3e:96:41:04', 'fixed_ips': [{'subnet_id': t_subnet['id'], 'ip_address': '10.0.1.4'}], - 'binding:profile': {}} + 'binding:profile': {}, + 'security_groups': [t_sg['id']]} TOP_PORTS.append(t_port) b_port = self.plugin.create_port(self.context, port_body) self.assertDictEqual(t_port, b_port) @patch.object(t_context, 'get_context_from_neutron_context', new=mock.Mock) def test_get_port(self): - t_net, t_subnet, t_port = self._prepare_resource() + t_net, t_subnet, t_port, _ = self._prepare_resource() port_id = uuidutils.generate_uuid() t_port = {'id': port_id, 'tenant_id': self.tenant_id, @@ -331,7 +364,8 @@ class PluginTest(unittest.TestCase): 'mac_address': 'fa:16:3e:96:41:04', 'fixed_ips': [{'subnet_id': t_subnet['id'], 'ip_address': '10.0.1.4'}], - 'binding:profile': {}} + 'binding:profile': {}, + 'security_groups': []} TOP_PORTS.append(t_port) t_port = self.plugin.get_port(self.context, port_id) b_port = get_resource('port', False, t_port['id']) @@ -339,7 +373,7 @@ class PluginTest(unittest.TestCase): @patch.object(t_context, 'get_context_from_neutron_context', new=mock.Mock) def test_get_ports(self): - t_net, t_subnet, t_port = self._prepare_resource() + t_net, t_subnet, t_port, t_sg = self._prepare_resource() t_ports = [] for i in (4, 5): port_id = uuidutils.generate_uuid() @@ -350,7 +384,8 @@ class PluginTest(unittest.TestCase): 'mac_address': 'fa:16:3e:96:41:04', 'fixed_ips': [{'subnet_id': t_subnet['id'], 'ip_address': '10.0.1.%d' % i}], - 'binding:profile': {}} + 'binding:profile': {}, + 'security_groups': [t_sg['id']]} TOP_PORTS.append(t_port) t_ports.append(t_port) self.plugin.get_ports(self.context, diff --git a/tricircle/tests/unit/xjob/test_xmanager.py b/tricircle/tests/unit/xjob/test_xmanager.py index b5c096c7..34a98cd5 100644 --- a/tricircle/tests/unit/xjob/test_xmanager.py +++ b/tricircle/tests/unit/xjob/test_xmanager.py @@ -30,34 +30,43 @@ from tricircle.xjob import xmanager from tricircle.xjob import xservice +TOP_NETWORK = [] BOTTOM1_NETWORK = [] BOTTOM2_NETWORK = [] +TOP_SUBNET = [] BOTTOM1_SUBNET = [] BOTTOM2_SUBNET = [] BOTTOM1_PORT = [] BOTTOM2_PORT = [] BOTTOM1_ROUTER = [] BOTTOM2_ROUTER = [] -RES_LIST = [BOTTOM1_NETWORK, BOTTOM2_NETWORK, BOTTOM1_SUBNET, BOTTOM2_SUBNET, - BOTTOM1_PORT, BOTTOM2_PORT, BOTTOM1_ROUTER, BOTTOM2_ROUTER] -RES_MAP = {'pod_1': {'network': BOTTOM1_NETWORK, +TOP_SG = [] +BOTTOM1_SG = [] +BOTTOM2_SG = [] +RES_LIST = [TOP_NETWORK, BOTTOM1_NETWORK, BOTTOM2_NETWORK, TOP_SUBNET, + BOTTOM1_SUBNET, BOTTOM2_SUBNET, BOTTOM1_PORT, BOTTOM2_PORT, + BOTTOM1_ROUTER, BOTTOM2_ROUTER, TOP_SG, BOTTOM1_SG, BOTTOM2_SG] +RES_MAP = {'top': {'network': TOP_NETWORK, + 'subnet': TOP_SUBNET, + 'security_group': TOP_SG}, + 'pod_1': {'network': BOTTOM1_NETWORK, 'subnet': BOTTOM1_SUBNET, 'port': BOTTOM1_PORT, - 'router': BOTTOM1_ROUTER}, + 'router': BOTTOM1_ROUTER, + 'security_group': BOTTOM1_SG}, 'pod_2': {'network': BOTTOM2_NETWORK, 'subnet': BOTTOM2_SUBNET, 'port': BOTTOM2_PORT, - 'router': BOTTOM2_ROUTER}} + 'router': BOTTOM2_ROUTER, + 'security_group': BOTTOM2_SG}} class FakeXManager(xmanager.XManager): def __init__(self): - self.clients = {'pod_1': FakeClient('pod_1'), + self.clients = {'top': FakeClient(), + 'pod_1': FakeClient('pod_1'), 'pod_2': FakeClient('pod_2')} - def _get_client(self, pod_name=None): - return self.clients[pod_name] - class FakeClient(object): def __init__(self, pod_name=None): @@ -85,6 +94,9 @@ class FakeClient(object): def list_ports(self, cxt, filters=None): return self.list_resources('port', cxt, filters) + def list_subnets(self, cxt, filters=None): + return self.list_resources('subnet', cxt, filters) + def get_subnets(self, cxt, subnet_id): return self.list_resources( 'subnet', cxt, @@ -93,6 +105,20 @@ class FakeClient(object): def update_routers(self, cxt, *args, **kwargs): pass + def list_security_groups(self, cxt, filters=None): + return self.list_resources('security_group', cxt, filters) + + def get_security_groups(self, cxt, sg_id): + return self.list_resources( + 'security_group', cxt, + [{'key': 'id', 'comparator': 'eq', 'value': sg_id}])[0] + + def delete_security_group_rules(self, cxt, sg_id): + pass + + def create_security_group_rules(self, cxt, *args, **kwargs): + pass + class XManagerTest(unittest.TestCase): def setUp(self): @@ -106,6 +132,7 @@ class XManagerTest(unittest.TestCase): cfg.CONF.register_opt(opt) self.context = context.Context() self.xmanager = FakeXManager() + self.xmanager = FakeXManager() @patch.object(FakeClient, 'update_routers') def test_configure_extra_routes(self, mock_update): @@ -207,6 +234,77 @@ class XManagerTest(unittest.TestCase): called = called and (mock_update.call_args_list[0] == calls[0]) self.assertTrue(called) + @patch.object(FakeClient, 'delete_security_group_rules') + @patch.object(FakeClient, 'create_security_group_rules') + def test_configure_security_group_rules(self, mock_create, mock_delete): + project_id = uuidutils.generate_uuid() + sg_id = uuidutils.generate_uuid() + sg_rule_id = uuidutils.generate_uuid() + sg = {'id': sg_id, + 'tenant_id': project_id, + 'name': 'default', + 'security_group_rules': [{ + 'id': sg_rule_id, + 'remote_group_id': sg_id, + 'direction': 'ingress', + 'remote_ip_prefix': None, + 'protocol': None, + 'ethertype': 'IPv4', + 'port_range_max': -1, + 'port_range_min': -1, + 'security_group_id': sg_id}]} + RES_MAP['top']['security_group'].append(sg) + + for i in xrange(1, 3): + pod_dict = {'pod_id': 'pod_id_%d' % i, + 'pod_name': 'pod_%d' % i, + 'az_name': 'az_name_%d' % i} + db_api.create_pod(self.context, pod_dict) + + network = {'id': 'network_%d_id' % i, + 'tenant_id': project_id} + subnet = {'id': 'subnet_%d_id' % i, + 'network_id': network['id'], + 'cidr': '10.0.%d.0/24' % i, + 'gateway_ip': '10.0.%d.1' % i, + 'tenant_id': project_id} + RES_MAP['top']['network'].append(network) + RES_MAP['top']['subnet'].append(subnet) + + pod_name = 'pod_%d' % i + RES_MAP[pod_name]['security_group'].append(sg) + route = {'top_id': sg_id, 'bottom_id': sg_id, + 'pod_id': pod_dict['pod_id'], + 'resource_type': 'security_group'} + with self.context.session.begin(): + core.create_resource(self.context, models.ResourceRouting, + route) + + self.xmanager.configure_security_group_rules( + self.context, payload={'seg_rule_setup': project_id}) + + calls = [mock.call(self.context, sg_rule_id)] + mock_delete.assert_has_calls(calls) + calls = [mock.call(self.context, + {'security_group_rules': [ + {'remote_group_id': None, + 'direction': 'ingress', + 'remote_ip_prefix': '10.0.1.0/24', + 'protocol': None, + 'ethertype': 'IPv4', + 'port_range_max': -1, + 'port_range_min': -1, + 'security_group_id': sg_id}, + {'remote_group_id': None, + 'direction': 'ingress', + 'remote_ip_prefix': '10.0.2.0/24', + 'protocol': None, + 'ethertype': 'IPv4', + 'port_range_max': -1, + 'port_range_min': -1, + 'security_group_id': sg_id}]})] + mock_create.assert_has_calls(calls) + def test_job_handle(self): @xmanager._job_handle('fake_resource') def fake_handle(self, ctx, payload): diff --git a/tricircle/xjob/xmanager.py b/tricircle/xjob/xmanager.py index f7c443fe..f1bf3434 100644 --- a/tricircle/xjob/xmanager.py +++ b/tricircle/xjob/xmanager.py @@ -24,6 +24,7 @@ from oslo_log import log as logging import oslo_messaging as messaging from oslo_service import periodic_task +import neutron_lib.exceptions as q_exceptions import neutronclient.common.exceptions as q_cli_exceptions from tricircle.common import client @@ -150,7 +151,8 @@ class XManager(PeriodicTasks): self.job_handles = { constants.JT_ROUTER: self.configure_extra_routes, constants.JT_ROUTER_SETUP: self.setup_bottom_router, - constants.JT_PORT_DELETE: self.delete_server_port} + constants.JT_PORT_DELETE: self.delete_server_port, + constants.JT_SEG_RULE_SETUP: self.configure_security_group_rules} self.helper = helper.NetworkHelper() self.xjob_handler = xrpcapi.XJobAPI() super(XManager, self).__init__() @@ -611,8 +613,8 @@ class XManager(PeriodicTasks): router_ips_map[b_router_ids[i]] = {} for b_interface in b_interfaces: ip = b_interface['fixed_ips'][0]['ip_address'] - ew_bridge_cidr = '100.0.0.0/9' - ns_bridge_cidr = '100.128.0.0/9' + ew_bridge_cidr = CONF.client.ew_bridge_cidr + ns_bridge_cidr = CONF.client.ns_bridge_cidr if netaddr.IPAddress(ip) in netaddr.IPNetwork(ew_bridge_cidr): router_bridge_ip_map[b_router_ids[i]] = ip continue @@ -655,3 +657,108 @@ class XManager(PeriodicTasks): def delete_server_port(self, ctx, payload): t_port_id = payload[constants.JT_PORT_DELETE] self._get_client().delete_ports(ctx, t_port_id) + + @staticmethod + def _safe_create_security_group_rule(context, client, body): + try: + client.create_security_group_rules(context, body) + except q_exceptions.Conflict: + return + + @staticmethod + def _safe_delete_security_group_rule(context, client, _id): + try: + client.delete_security_group_rules(context, _id) + except q_exceptions.NotFound: + return + + @staticmethod + def _construct_bottom_rule(rule, sg_id, ip=None): + ip = ip or rule['remote_ip_prefix'] + # if ip is passed, this is a extended rule for remote group + return {'remote_group_id': None, + 'direction': rule['direction'], + 'remote_ip_prefix': ip, + 'protocol': rule.get('protocol'), + 'ethertype': rule['ethertype'], + 'port_range_max': rule.get('port_range_max'), + 'port_range_min': rule.get('port_range_min'), + 'security_group_id': sg_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 + + @_job_handle(constants.JT_SEG_RULE_SETUP) + def configure_security_group_rules(self, ctx, payload): + project_id = payload[constants.JT_SEG_RULE_SETUP] + top_client = self._get_client() + sg_filters = [{'key': 'tenant_id', 'comparator': 'eq', + 'value': project_id}] + top_sgs = top_client.list_security_groups(ctx, sg_filters) + for top_sg in top_sgs: + new_b_rules = [] + for t_rule in top_sg['security_group_rules']: + if not t_rule['remote_group_id']: + # leave sg_id empty here + new_b_rules.append( + self._construct_bottom_rule(t_rule, '')) + continue + if top_sg['name'] != 'default': + # currently we only handle rules containing remote_group_id + # for default security group + continue + if t_rule['ethertype'] != 'IPv4': + continue + subnets = top_client.list_subnets( + ctx, [{'key': 'tenant_id', 'comparator': 'eq', + 'value': project_id}]) + ew_bridge_ip_net = netaddr.IPNetwork( + CONF.client.ew_bridge_cidr) + ns_bridge_ip_net = netaddr.IPNetwork( + CONF.client.ns_bridge_cidr) + for subnet in subnets: + ip_net = netaddr.IPNetwork(subnet['cidr']) + if ip_net in ew_bridge_ip_net or ( + ip_net in ns_bridge_ip_net): + continue + # leave sg_id empty here + new_b_rules.append( + self._construct_bottom_rule(t_rule, '', + subnet['cidr'])) + + mappings = db_api.get_bottom_mappings_by_top_id( + ctx, top_sg['id'], constants.RT_SG) + for pod, b_sg_id in mappings: + client = self._get_client(pod['pod_name']) + b_sg = client.get_security_groups(ctx, b_sg_id) + add_rules = [] + del_rules = [] + match_index = set() + for b_rule in b_sg['security_group_rules']: + match = False + for i, rule in enumerate(new_b_rules): + if self._compare_rule(b_rule, rule): + match = True + match_index.add(i) + break + if not match: + del_rules.append(b_rule) + for i, rule in enumerate(new_b_rules): + if i not in match_index: + add_rules.append(rule) + + for del_rule in del_rules: + self._safe_delete_security_group_rule( + ctx, client, del_rule['id']) + if add_rules: + rule_body = {'security_group_rules': []} + for add_rule in add_rules: + add_rule['security_group_id'] = b_sg_id + rule_body['security_group_rules'].append(add_rule) + self._safe_create_security_group_rule( + ctx, client, rule_body)