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:
parent
2d43c3f7aa
commit
aaf2501285
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue