Merge "Central and local plugin (part 2, l3 functionality)"

This commit is contained in:
Jenkins 2016-10-15 10:58:14 +00:00 committed by Gerrit Code Review
commit 2d43c3f7aa
14 changed files with 283 additions and 63 deletions

View File

@ -46,16 +46,9 @@ OVS_BRIDGE_MAPPINGS=bridge:br-bridge
Q_ENABLE_TRICIRCLE=True
enable_plugin tricircle https://github.com/openstack/tricircle/
# Tricircle Services
enable_service t-api
enable_service t-ngw
enable_service t-cgw
enable_service t-job
# Use Neutron instead of nova-network
disable_service n-net
enable_service q-svc
enable_service q-svc1
enable_service q-dhcp
enable_service q-agt
enable_service q-l3
@ -68,3 +61,22 @@ disable_service n-obj
disable_service c-bak
disable_service tempest
disable_service horizon
CENTRAL_REGION_NAME=CentralRegion
TRICIRCLE_NEUTRON_PORT=20001
[[post-config|$NEUTRON_CONF]]
[DEFAULT]
core_plugin=tricircle.network.local_plugin.TricirclePlugin
[client]
admin_username=admin
admin_password=$ADMIN_PASSWORD
admin_tenant=demo
auto_refresh_endpoint=True
top_pod_name=$CENTRAL_REGION_NAME
[tricircle]
real_core_plugin=neutron.plugins.ml2.plugin.Ml2Plugin
central_neutron_url=http://127.0.0.1:$TRICIRCLE_NEUTRON_PORT

View File

@ -38,12 +38,11 @@ NEUTRON_CREATE_INITIAL_NETWORKS=False
Q_USE_PROVIDERNET_FOR_PUBLIC=True
HOST_IP=10.250.201.25
REGION_NAME=Pod2
REGION_NAME=RegionTwo
KEYSTONE_REGION_NAME=RegionOne
SERVICE_HOST=$HOST_IP
KEYSTONE_SERVICE_HOST=10.250.201.24
KEYSTONE_AUTH_HOST=10.250.201.24
GLANCE_SERVICE_HOST=10.250.201.24
Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS=(network_vlan_ranges=bridge:2001:3000,extern:3001:4000)
OVS_BRIDGE_MAPPINGS=bridge:br-bridge,extern:br-ext
@ -60,8 +59,26 @@ enable_service c-vol
enable_service c-sch
disable_service n-obj
disable_service g-api
disable_service g-reg
disable_service c-bak
disable_service tempest
disable_service horizon
CENTRAL_REGION_NAME=CentralRegion
TRICIRCLE_NEUTRON_PORT=20001
[[post-config|$NEUTRON_CONF]]
[DEFAULT]
core_plugin=tricircle.network.local_plugin.TricirclePlugin
[client]
admin_username=admin
admin_password=$ADMIN_PASSWORD
admin_tenant=demo
auto_refresh_endpoint=True
top_pod_name=$CENTRAL_REGION_NAME
[tricircle]
real_core_plugin=neutron.plugins.ml2.plugin.Ml2Plugin
# change the ip to the ip of the machine hosting central Neutron server
central_neutron_url=http://10.250.201.24:$TRICIRCLE_NEUTRON_PORT

View File

@ -35,10 +35,6 @@ LIBVIRT_FIREWALL_DRIVER=nova.virt.firewall.NoopFirewallDriver
Q_ENABLE_TRICIRCLE=True
enable_plugin tricircle https://github.com/openstack/tricircle/
# Tricircle Services
enable_service t-api
enable_service t-job
# Use Neutron instead of nova-network
disable_service n-net
enable_service q-svc

View File

@ -86,7 +86,6 @@ function configure_tricircle_xjob {
echo "Configuring Tricircle xjob"
init_common_tricircle_conf $TRICIRCLE_XJOB_CONF
iniset $TRICIRCLE_XJOB_CONF DEFAULT enable_api_gateway False
setup_colorized_logging $TRICIRCLE_XJOB_CONF DEFAULT
fi

View File

@ -59,6 +59,7 @@ ns_bridge_port_name = 'ns_bridge_port_%s_%s_%s'
dhcp_port_name = 'dhcp_port_%s' # subnet_id
interface_port_name = 'interface_%s_%s' # b_pod_id t_subnet_id
interface_port_device_id = 'reserved_gateway_port'
MAX_INT = 0x7FFFFFFF
expire_time = datetime.datetime(2000, 1, 1)

View File

@ -18,6 +18,7 @@ import six
import pecan
from oslo_log import log as logging
from oslo_utils import uuidutils
from tricircle.common import constants as cons
import tricircle.common.exceptions as t_exceptions
@ -126,6 +127,21 @@ def get_bottom_network_name(network):
return '%s#%s' % (network['id'], network['name'])
def get_id_from_name(_type, name):
if _type == cons.RT_NETWORK:
tokens = name.split('#')
if len(tokens) == 2:
id_candidate = tokens[1]
else:
id_candidate = tokens[0]
else:
id_candidate = name
if uuidutils.is_uuid_like(id_candidate):
return id_candidate
else:
return None
def format_error(code, message, error_type=None):
error_type_map = {400: 'badRequest',
403: 'forbidden',

View File

@ -57,9 +57,8 @@ def get_pod(context, pod_id):
def list_pods(context, filters=None, sorts=None):
with context.session.begin():
return core.query_resource(context, models.Pod, filters or [],
sorts or [])
return core.query_resource(context, models.Pod, filters or [],
sorts or [])
def update_pod(context, pod_id, update_dict):
@ -116,9 +115,8 @@ def get_pod_service_configuration(context, config_id):
def list_pod_service_configurations(context, filters=None, sorts=None):
with context.session.begin():
return core.query_resource(context, models.PodServiceConfiguration,
filters or [], sorts or [])
return core.query_resource(context, models.PodServiceConfiguration,
filters or [], sorts or [])
def update_pod_service_configuration(context, config_id, update_dict):

View File

@ -386,6 +386,19 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
context, subnet_id, subnet)
def create_port(self, context, port):
port_body = port['port']
if port_body['device_id'] == t_constants.interface_port_device_id:
_, region_name, subnet_id = port_body['name'].split('_')
gateway_port_body = self.helper.get_create_interface_body(
port_body['tenant_id'], port_body['network_id'], region_name,
subnet_id)
t_ctx = t_context.get_context_from_neutron_context(context)
pod = db_api.get_pod_by_name(t_ctx, region_name)
_, t_gateway_id = self.helper.prepare_top_element(
t_ctx, context, port_body['tenant_id'], pod,
{'id': port_body['name']}, t_constants.RT_PORT,
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)
@ -414,6 +427,20 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
db_api.create_resource_mapping(t_ctx, resource_id, resource_id,
pod['pod_id'], res['tenant_id'],
resource_type)
interfaces = super(TricirclePlugin, self).get_ports(
context,
{'network_id': [res['network_id']],
'device_owner': [constants.DEVICE_OWNER_ROUTER_INTF]})
interfaces = [inf for inf in interfaces if inf['device_id']]
if interfaces:
# request may be come from service, we use an admin context
# to run the xjob
admin_context = t_context.get_admin_context()
self.xjob_handler.setup_bottom_router(
admin_context, res['network_id'],
interfaces[0]['device_id'], pod['pod_id'])
return res
def delete_port(self, context, port_id, l3_port_check=True):

View File

@ -32,3 +32,8 @@ class BottomPodOperationFailure(exceptions.NeutronException):
class DhcpPortNotFound(exceptions.NotFound):
message = _('Dhcp port for subnet %(subnet_id)s not found')
class GatewayPortNotFound(exceptions.NotFound):
message = _('Gateway port for subnet %(subnet_id)s and region %(region)s '
'not found')

View File

@ -28,7 +28,9 @@ from tricircle.common import client # noqa
import tricircle.common.constants as t_constants
import tricircle.common.context as t_context
from tricircle.common.i18n import _
from tricircle.common import resource_handle
import tricircle.common.utils as t_utils
import tricircle.network.exceptions as t_exceptions
from tricircle.network import helper
@ -59,6 +61,18 @@ class TricirclePlugin(plugin.Ml2Plugin):
self.neutron_handle.endpoint_url = \
cfg.CONF.tricircle.central_neutron_url
def start_rpc_listeners(self):
return self.core_plugin.start_rpc_listeners()
def start_rpc_state_reports_listener(self):
return self.core_plugin.start_rpc_state_reports_listener()
def rpc_workers_supported(self):
return self.core_plugin.rpc_workers_supported()
def rpc_state_report_workers_supported(self):
return self.core_plugin.rpc_state_report_workers_supported()
@staticmethod
def _adapt_network_body(network):
network_type = network.get('provider:network_type')
@ -147,6 +161,43 @@ class TricirclePlugin(plugin.Ml2Plugin):
dhcp_port_body['port']['id'] = t_ports[0]['id']
self.core_plugin.create_port(q_ctx, dhcp_port_body)
def _ensure_gateway_port(self, t_ctx, t_subnet):
region_name = cfg.CONF.nova.region_name
gateway_port_name = t_constants.interface_port_name % (region_name,
t_subnet['id'])
gateway_port_body = {
'port': {'tenant_id': t_subnet['tenant_id'],
'admin_state_up': True,
'name': gateway_port_name,
'network_id': t_subnet['network_id'],
'device_id': t_constants.interface_port_device_id}}
try:
return self.neutron_handle.handle_create(
t_ctx, t_constants.RT_PORT, gateway_port_body)
except Exception:
raw_client = self.neutron_handle._get_client(t_ctx)
params = {'name': gateway_port_name}
t_ports = raw_client.list_ports(**params)['ports']
if not t_ports:
raise t_exceptions.GatewayPortNotFound(
subnet_id=t_subnet['id'], region=region_name)
return t_ports[0]
def create_network(self, context, network):
# this method is overwritten for bottom bridge network and external
# network creation, for internal network, get_network and get_networks
# will do the trick
net_body = network['network']
self._adapt_network_body(net_body)
if net_body['name']:
net_id = t_utils.get_id_from_name(t_constants.RT_NETWORK,
net_body['name'])
if net_id:
net_body['id'] = net_id
b_network = self.core_plugin.create_network(context,
{'network': net_body})
return b_network
def get_network(self, context, _id, fields=None):
try:
b_network = self.core_plugin.get_network(context, _id, fields)
@ -174,12 +225,14 @@ class TricirclePlugin(plugin.Ml2Plugin):
return self.core_plugin.get_networks(
context, filters, fields, sorts, limit, marker, page_reverse)
b_networks = self.core_plugin.get_networks(
context, filters, fields, sorts, limit, marker, page_reverse)
for b_network in b_networks:
b_full_networks = self.core_plugin.get_networks(
context, filters, None, sorts, limit, marker, page_reverse)
b_networks = []
for b_network in b_full_networks:
subnet_ids = self._ensure_subnet(context, b_network, False)
if subnet_ids:
b_network['subnets'] = subnet_ids
b_networks.append(self._fields(b_network, fields))
if len(b_networks) == len(filters['id']):
return b_networks
@ -206,6 +259,31 @@ class TricirclePlugin(plugin.Ml2Plugin):
b_networks.append(self._fields(b_network, fields))
return b_networks
def create_subnet(self, context, subnet):
# this method is overwritten for bottom bridge subnet and external
# subnet creation, for internal subnet, get_subnet and get_subnets
# will do the trick
subnet_body = subnet['subnet']
if subnet_body['name']:
subnet_id = t_utils.get_id_from_name(t_constants.RT_SUBNET,
subnet_body['name'])
if subnet_id:
subnet_body['id'] = subnet_id
b_subnet = self.core_plugin.create_subnet(context,
{'subnet': subnet_body})
return b_subnet
def _create_bottom_subnet(self, t_ctx, q_ctx, t_subnet):
gateway_port = self._ensure_gateway_port(t_ctx, t_subnet)
subnet_body = helper.NetworkHelper.get_create_subnet_body(
gateway_port['tenant_id'], t_subnet, t_subnet['network_id'],
gateway_port['fixed_ips'][0]['ip_address'])['subnet']
t_subnet['gateway_ip'] = subnet_body['gateway_ip']
t_subnet['allocation_pools'] = subnet_body['allocation_pools']
b_subnet = self.core_plugin.create_subnet(q_ctx, {'subnet': t_subnet})
return b_subnet
def get_subnet(self, context, _id, fields=None):
t_ctx = t_context.get_context_from_neutron_context(context)
try:
@ -214,11 +292,9 @@ class TricirclePlugin(plugin.Ml2Plugin):
t_subnet = self.neutron_handle.handle_get(t_ctx, 'subnet', _id)
if not t_subnet:
raise q_exceptions.SubnetNotFound(subnet_id=_id)
b_subnet = self.core_plugin.create_subnet(context,
{'subnet': t_subnet})
b_subnet = self._create_bottom_subnet(t_ctx, context, t_subnet)
if b_subnet['enable_dhcp']:
self._ensure_subnet_dhcp_port(t_ctx, context, b_subnet)
return self._fields(b_subnet, fields)
def get_subnets(self, context, filters=None, fields=None, sorts=None,
@ -232,10 +308,13 @@ class TricirclePlugin(plugin.Ml2Plugin):
context, filters, fields, sorts, limit, marker, page_reverse)
t_ctx = t_context.get_context_from_neutron_context(context)
b_subnets = self.core_plugin.get_subnets(
context, filters, fields, sorts, limit, marker, page_reverse)
for b_subnet in b_subnets:
self._ensure_subnet_dhcp_port(t_ctx, context, b_subnet)
b_full_subnets = self.core_plugin.get_subnets(
context, filters, None, sorts, limit, marker, page_reverse)
b_subnets = []
for b_subnet in b_full_subnets:
if b_subnet['enable_dhcp']:
self._ensure_subnet_dhcp_port(t_ctx, context, b_subnet)
b_subnets.append(self._fields(b_subnet, fields))
if len(b_subnets) == len(filters['id']):
return b_subnets
@ -251,13 +330,19 @@ class TricirclePlugin(plugin.Ml2Plugin):
missing_subnets = [subnet for subnet in t_subnets if (
subnet['id'] in missing_id_set)]
for subnet in missing_subnets:
b_subnet = self.core_plugin.create_subnet(
context, {'subnet': subnet})
b_subnet = self._create_bottom_subnet(t_ctx, context, subnet)
if b_subnet['enable_dhcp']:
self._ensure_subnet_dhcp_port(t_ctx, context, b_subnet)
b_subnets.append(self._fields(b_subnet, fields))
return b_subnets
@staticmethod
def _is_special_port(port):
return port.get('device_owner') in (
q_constants.DEVICE_OWNER_ROUTER_INTF,
q_constants.DEVICE_OWNER_FLOATINGIP,
q_constants.DEVICE_OWNER_ROUTER_GW)
def create_port(self, context, port):
port_body = port['port']
network_id = port_body['network_id']
@ -268,25 +353,34 @@ class TricirclePlugin(plugin.Ml2Plugin):
raw_client = self.neutron_handle._get_client(t_ctx)
if port_body['fixed_ips'] is not q_constants.ATTR_NOT_SPECIFIED:
fixed_ip = port_body['fixed_ips'][0]
ip_address = fixed_ip.get('ip_address')
if not ip_address:
# dhcp agent may request to create a dhcp port without
# specifying ip address, we just raise an exception to reject
# this request
raise q_exceptions.InvalidIpForNetwork(ip_address='None')
params = {'fixed_ips': 'ip_address=%s' % ip_address}
t_ports = raw_client.list_ports(**params)['ports']
if not t_ports:
raise q_exceptions.InvalidIpForNetwork(
ip_address=fixed_ip['ip_address'])
t_port = t_ports[0]
if not self._is_special_port(port_body):
fixed_ip = port_body['fixed_ips'][0]
ip_address = fixed_ip.get('ip_address')
if not ip_address:
# dhcp agent may request to create a dhcp port without
# specifying ip address, we just raise an exception to
# reject this request
raise q_exceptions.InvalidIpForNetwork(ip_address='None')
params = {'fixed_ips': 'ip_address=%s' % ip_address}
t_ports = raw_client.list_ports(**params)['ports']
if not t_ports:
raise q_exceptions.InvalidIpForNetwork(
ip_address=fixed_ip['ip_address'])
t_port = t_ports[0]
else:
t_port = port_body
else:
self._adapt_port_body_for_client(port['port'])
t_port = raw_client.create_port(port)['port']
subnet_id = t_port['fixed_ips'][0]['subnet_id']
# get_subnet will create bottom subnet if it doesn't exist
self.get_subnet(context, subnet_id)
if not self._is_special_port(port_body):
subnet_id = t_port['fixed_ips'][0]['subnet_id']
# get_subnet will create bottom subnet if it doesn't exist
self.get_subnet(context, subnet_id)
for field in ('name', 'device_id'):
if port_body.get(field):
t_port[field] = port_body[field]
b_port = self.core_plugin.create_port(context, {'port': t_port})
return b_port

View File

@ -1020,9 +1020,11 @@ class PluginTest(unittest.TestCase,
core.initialize()
core.ModelBase.metadata.create_all(core.get_engine())
cfg.CONF.register_opts(q_config.core_opts)
cfg.CONF.register_opts(plugin.tricircle_opts)
plugin_path = \
'tricircle.tests.unit.network.test_central_plugin.FakePlugin'
cfg.CONF.set_override('core_plugin', plugin_path)
cfg.CONF.set_override('enable_api_gateway', True)
self.context = context.Context()
self.save_method = manager.NeutronManager._get_default_service_plugins
manager.NeutronManager._get_default_service_plugins = mock.Mock()
@ -2110,16 +2112,16 @@ class PluginTest(unittest.TestCase,
b_bridge_net_id = db_api.get_bottom_id_by_top_id_pod_name(
t_ctx, net['id'], 'pod_1', constants.RT_NETWORK)
calls = [mock.call(t_ctx,
{'floatingip': {
'floating_network_id': b_bridge_net_id,
'floating_ip_address': '100.128.0.3',
'port_id': b_port_id}}),
mock.call(t_ctx,
{'floatingip': {
'floating_network_id': b_ext_net_id,
'floating_ip_address': fip[
'floating_ip_address'],
'port_id': ns_bridge_port['id']}}),
mock.call(t_ctx,
{'floatingip': {
'floating_network_id': b_bridge_net_id,
'floating_ip_address': '100.128.0.3',
'port_id': b_port_id}})]
'port_id': ns_bridge_port['id']}})]
mock_create.assert_has_calls(calls)
@patch.object(driver.Pool, 'get_instance', new=fake_get_instance)

View File

@ -19,6 +19,7 @@ from mock import patch
import six
import unittest
from oslo_config import cfg
from oslo_utils import uuidutils
import neutron_lib.constants as q_constants
@ -106,6 +107,9 @@ class FakeCorePlugin(object):
create_resource('port', False, port['port'])
return port['port']
def update_port(self, context, _id, port):
pass
def get_port(self, context, _id, fields=None):
return get_resource('port', False, _id)
@ -180,6 +184,15 @@ class FakeNeutronHandle(object):
def handle_get(self, context, _type, _id):
return get_resource(_type, True, _id)
def handle_create(self, context, _type, body):
if _type == 'port':
return FakeClient().create_port(body)['port']
create_resource(_type, True, body[_type])
return body[_type]
def handle_update(self, context, _type, _id, body):
pass
class FakePlugin(plugin.TricirclePlugin):
def __init__(self):
@ -207,6 +220,9 @@ class PluginTest(unittest.TestCase):
'name': 'subnet1',
'network_id': network_id,
'cidr': '10.0.1.0/24',
'ip_version': 4,
'allocation_pools': [{'start': '10.0.1.2',
'end': '10.0.1.254'}],
'enable_dhcp': True}
t_port = {'id': port_id,
'tenant_id': self.tenant_id,
@ -232,10 +248,26 @@ class PluginTest(unittest.TestCase):
b_port = get_resource('port', False, port['id'])
b_net.pop('project_id')
b_subnet.pop('project_id')
pool = subnet.pop('allocation_pools')[0]
b_pools = b_subnet.pop('allocation_pools')
b_gateway_ip = b_subnet.pop('gateway_ip')
def ip_to_digit(ip):
return int(ip[ip.rindex('.') + 1:])
pool_range = range(ip_to_digit(pool['start']),
ip_to_digit(pool['end']) + 1)
b_pool_range1 = range(ip_to_digit(b_pools[0]['start']),
ip_to_digit(b_pools[0]['end']) + 1)
b_pool_range2 = range(ip_to_digit(b_pools[1]['start']),
ip_to_digit(b_pools[1]['end']) + 1)
b_pool_range = b_pool_range1 + [
ip_to_digit(b_gateway_ip)] + b_pool_range2
port.pop('name')
b_port.pop('name')
self.assertDictEqual(net, b_net)
self.assertDictEqual(subnet, b_subnet)
self.assertEqual(pool_range, b_pool_range)
self.assertEqual('vlan', b_net_type)
self.assertDictEqual(port, b_port)
@ -329,6 +361,19 @@ class PluginTest(unittest.TestCase):
b_port.pop('project_id')
self.assertDictEqual(t_ports[i], b_port)
@patch.object(t_context, 'get_context_from_neutron_context')
@patch.object(FakeNeutronHandle, 'handle_update')
def test_update_port(self, mock_update, mock_context):
cfg.CONF.set_override('region_name', 'Pod1', 'nova')
mock_context.return_value = self.context
update_body = {'port': {'device_owner': 'compute:None',
'binding:host_id': 'fake_host'}}
port_id = 'fake_port_id'
self.plugin.update_port(self.context, port_id, update_body)
mock_update.assert_called_once_with(
self.context, 'port', port_id,
{'port': {'binding:profile': {'region': 'Pod1'}}})
def tearDown(self):
for res in RES_LIST:
del res[:]

View File

@ -385,9 +385,13 @@ class XManager(PeriodicTasks):
t_subnet_id = t_port['fixed_ips'][0]['subnet_id']
t_subnet = t_client.get_subnets(ctx, t_subnet_id)
(b_net_id,
subnet_map) = self.helper.prepare_bottom_network_subnets(
ctx, q_ctx, project_id, b_pod, t_net, [t_subnet])
if CONF.enable_api_gateway:
(b_net_id,
subnet_map) = self.helper.prepare_bottom_network_subnets(
ctx, q_ctx, project_id, b_pod, t_net, [t_subnet])
else:
(b_net_id,
subnet_map) = (t_net['id'], {t_subnet['id']: t_subnet['id']})
# the gateway ip of bottom subnet is set to the ip of t_port, so
# we just attach the bottom subnet to the bottom router and neutron
@ -456,13 +460,14 @@ class XManager(PeriodicTasks):
_, b_ns_bridge_port_id = self.helper.prepare_bottom_element(
ctx, project_id, b_ext_pod, t_ns_bridge_port,
constants.RT_PORT, port_body)
self._safe_create_bottom_floatingip(
ctx, b_ext_pod, b_ext_client, b_ext_net_id, add_fip,
b_ns_bridge_port_id)
# swap these two lines
self._safe_create_bottom_floatingip(
ctx, b_pod, b_client, b_ns_bridge_net_id,
t_ns_bridge_port['fixed_ips'][0]['ip_address'],
b_int_port_id)
self._safe_create_bottom_floatingip(
ctx, b_ext_pod, b_ext_client, b_ext_net_id, add_fip,
b_ns_bridge_port_id)
else:
self._safe_create_bottom_floatingip(
ctx, b_pod, b_client, b_ext_net_id, add_fip,

View File

@ -55,7 +55,10 @@ common_opts = [
help=_("Running job is considered expires after this time, in"
" seconds")),
cfg.FloatOpt('worker_sleep_time', default=0.1,
help=_("Seconds a worker sleeps after one run in a loop"))
help=_("Seconds a worker sleeps after one run in a loop")),
cfg.BoolOpt('enable_api_gateway',
default=False,
help=_('Whether the Nova API gateway is enabled'))
]
service_opts = [