Central and local plugin (part3, security group)

1. What is the problem
Necessary changes for local plugin and central plugin to boot
a instance have been submitted in this patch[1], but security
group has not been supported yet.

2. What is the solution to the problem
(1) Modify local plugin that when security group query request
    is sent to local Neutron server, local plugin retrieves
    information from central Neutron server and creates necessary
    local security group.
(2) Add a new job to xjob to sync security group rules across
    OpenStack instances.

3. What the features need to be implemented to the Tricircle
   to realize the solution
With this patch, users can boot a instance directly via
the local Nova server with security group supported.

[1] https://review.openstack.org/375281

Change-Id: Ibc9898ac577bee19a017746356a8f826e0a8b16f
This commit is contained in:
zhiyuan_cai 2016-09-22 09:51:01 +08:00
parent 2d43c3f7aa
commit aaf2501285
9 changed files with 377 additions and 31 deletions

View File

@ -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)

View File

@ -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'

View File

@ -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})

View File

@ -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,

View File

@ -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}}

View File

@ -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

View File

@ -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,

View File

@ -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):

View File

@ -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)