Add network type support to the Tricircle plugin
1. What is the problem In the current implementation of the Tricircle plugin for neutron, network type is not supported so users cannot create networks with network type specified. In the specification of cross-pod l2 networking feature[1], we decide to support several network types like local, shared VLAN, shared VxLAN, etc, the first step is to make the Tricircle plugin be aware of network type. 2. What is the solution to the problem Handle network type in the Tricircle plugin for neutron. 3. What the features need to be implemented to the Tricircle to realize the solution In this patch, we add a framework to load type driver which processes different network type. The framework is based on neutron ML2 implemenation, we inherit the ML2 type manager and create a new Tricircle type manager. Also the Tricircle plugin is modified to extract network type parameter from request and insert network type information to response. [1] https://github.com/openstack/tricircle/blob/master/specs/cross-pod-l2-networking.rst Change-Id: Ida9b88df6113db46e637a7841ce5c1adaf651eba
This commit is contained in:
parent
f1fa18dcb8
commit
404b3fc87e
4
etc/tricircle_plugin-cfg-gen.conf
Normal file
4
etc/tricircle_plugin-cfg-gen.conf
Normal file
@ -0,0 +1,4 @@
|
||||
[DEFAULT]
|
||||
output_file = etc/tricircle_plugin.conf.sample
|
||||
wrap_width = 79
|
||||
namespace = tricircle.network
|
@ -51,6 +51,7 @@ oslo.config.opts =
|
||||
tricircle.api = tricircle.api.opts:list_opts
|
||||
tricircle.common = tricircle.common.opts:list_opts
|
||||
tricircle.db = tricircle.db.opts:list_opts
|
||||
tricircle.network = tricircle.network.opts:list_opts
|
||||
|
||||
tricircle.nova_apigw = tricircle.nova_apigw.opts:list_opts
|
||||
tricircle.cinder_apigw = tricircle.cinder_apigw.opts:list_opts
|
||||
@ -58,3 +59,6 @@ oslo.config.opts =
|
||||
|
||||
tempest.test_plugins =
|
||||
tricircle_tests = tricircle.tempestplugin.plugin:TricircleTempestPlugin
|
||||
|
||||
tricircle.network.type_drivers =
|
||||
local = tricircle.network.drivers.type_local:LocalTypeDriver
|
||||
|
1
tox.ini
1
tox.ini
@ -30,6 +30,7 @@ commands = oslo-config-generator --config-file=etc/api-cfg-gen.conf
|
||||
oslo-config-generator --config-file=etc/nova_apigw-cfg-gen.conf
|
||||
oslo-config-generator --config-file=etc/cinder_apigw-cfg-gen.conf
|
||||
oslo-config-generator --config-file=etc/xjob-cfg-gen.conf
|
||||
oslo-config-generator --config-file=etc/tricircle_plugin-cfg-gen.conf
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
@ -71,3 +71,6 @@ TOP = 'top'
|
||||
# job type
|
||||
JT_ROUTER = 'router'
|
||||
JT_PORT_DELETE = 'port_delete'
|
||||
|
||||
# network type
|
||||
NT_LOCAL = 'local'
|
||||
|
0
tricircle/network/drivers/__init__.py
Normal file
0
tricircle/network/drivers/__init__.py
Normal file
44
tricircle/network/drivers/type_local.py
Normal file
44
tricircle/network/drivers/type_local.py
Normal file
@ -0,0 +1,44 @@
|
||||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.plugins.ml2 import driver_api
|
||||
|
||||
from tricircle.common import constants
|
||||
|
||||
|
||||
class LocalTypeDriver(driver_api.TypeDriver):
|
||||
def get_type(self):
|
||||
return constants.NT_LOCAL
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
|
||||
def is_partial_segment(self, segment):
|
||||
return False
|
||||
|
||||
def validate_provider_segment(self, segment):
|
||||
pass
|
||||
|
||||
def reserve_provider_segment(self, session, segment):
|
||||
return segment
|
||||
|
||||
def allocate_tenant_segment(self, session):
|
||||
return {driver_api.NETWORK_TYPE: constants.NT_LOCAL}
|
||||
|
||||
def release_segment(self, session, segment):
|
||||
pass
|
||||
|
||||
def get_mtu(self, physical):
|
||||
pass
|
92
tricircle/network/managers.py
Normal file
92
tricircle/network/managers.py
Normal file
@ -0,0 +1,92 @@
|
||||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.extensions import external_net
|
||||
from neutron.plugins.ml2 import managers
|
||||
|
||||
from tricircle.common.i18n import _LI
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class TricircleTypeManager(managers.TypeManager):
|
||||
|
||||
def __init__(self):
|
||||
self.drivers = {}
|
||||
|
||||
# NOTE(zhiyuan) here we call __init__ of super class's super class,
|
||||
# which is NamedExtensionManager's __init__ to bypass initialization
|
||||
# process of ml2 type manager
|
||||
super(managers.TypeManager, self).__init__(
|
||||
'tricircle.network.type_drivers',
|
||||
cfg.CONF.tricircle.type_drivers,
|
||||
invoke_on_load=True)
|
||||
LOG.info(_LI('Loaded type driver names: %s'), self.names())
|
||||
|
||||
self._register_types()
|
||||
self._check_tenant_network_types(
|
||||
cfg.CONF.tricircle.tenant_network_types)
|
||||
|
||||
def _register_types(self):
|
||||
for ext in self:
|
||||
network_type = ext.obj.get_type()
|
||||
if network_type not in self.drivers:
|
||||
self.drivers[network_type] = ext
|
||||
|
||||
@staticmethod
|
||||
def _is_external_network(network):
|
||||
external = network.get(external_net.EXTERNAL)
|
||||
external_set = attributes.is_attr_set(external)
|
||||
if not external_set or not external:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def create_network_segments(self, context, network, tenant_id):
|
||||
# NOTE(zhiyuan) before we figure out how to deal with external network
|
||||
# segment allocation, skip segment creation for external network
|
||||
if self._is_external_network(network):
|
||||
return
|
||||
segments = self._process_provider_create(network)
|
||||
session = context.session
|
||||
mtu = []
|
||||
with session.begin(subtransactions=True):
|
||||
network_id = network['id']
|
||||
if segments:
|
||||
for segment_index, segment in enumerate(segments):
|
||||
segment = self.reserve_provider_segment(
|
||||
session, segment)
|
||||
self._add_network_segment(session, network_id, segment,
|
||||
mtu, segment_index)
|
||||
else:
|
||||
segment = self._allocate_tenant_net_segment(session)
|
||||
self._add_network_segment(session, network_id, segment, mtu)
|
||||
|
||||
def extend_networks_dict_provider(self, context, networks):
|
||||
internal_networks = []
|
||||
for network in networks:
|
||||
# NOTE(zhiyuan) before we figure out how to deal with external
|
||||
# network segment allocation, skip external network since it does
|
||||
# not have segment information
|
||||
if not self._is_external_network(network):
|
||||
internal_networks.append(network)
|
||||
if internal_networks:
|
||||
super(TricircleTypeManager,
|
||||
self).extend_networks_dict_provider(context,
|
||||
internal_networks)
|
22
tricircle/network/opts.py
Normal file
22
tricircle/network/opts.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import tricircle.network.plugin
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [
|
||||
('DEFAULT', tricircle.network.plugin.tricircle_opts),
|
||||
]
|
@ -36,6 +36,7 @@ from neutron.db import sqlalchemyutils
|
||||
from neutron.extensions import availability_zone as az_ext
|
||||
from neutron.extensions import external_net
|
||||
from neutron.extensions import l3
|
||||
from neutron.extensions import providernet as provider
|
||||
from neutron.plugins.ml2.drivers import type_vlan
|
||||
import neutronclient.common.exceptions as q_cli_exceptions
|
||||
|
||||
@ -56,14 +57,25 @@ import tricircle.db.api as db_api
|
||||
from tricircle.db import core
|
||||
from tricircle.db import models
|
||||
import tricircle.network.exceptions as t_network_exc
|
||||
from tricircle.network import managers
|
||||
from tricircle.network import security_groups
|
||||
|
||||
|
||||
tricircle_opts = [
|
||||
cfg.StrOpt('bridge_physical_network',
|
||||
default='',
|
||||
help='name of l3 bridge physical network')
|
||||
help='name of l3 bridge physical network'),
|
||||
cfg.ListOpt('type_drivers',
|
||||
default=['local'],
|
||||
help=_('List of network type driver entry points to be loaded '
|
||||
'from the tricircle.network.type_drivers namespace.')),
|
||||
cfg.ListOpt('tenant_network_types',
|
||||
default=['local'],
|
||||
help=_('Ordered list of network_types to allocate as tenant '
|
||||
'networks. The default value "local" is useful for '
|
||||
'single pod connectivity.'))
|
||||
]
|
||||
|
||||
tricircle_opt_group = cfg.OptGroup('tricircle')
|
||||
cfg.CONF.register_group(tricircle_opt_group)
|
||||
cfg.CONF.register_opts(tricircle_opts, group=tricircle_opt_group)
|
||||
@ -111,6 +123,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
self.clients = {}
|
||||
self.xjob_handler = xrpcapi.XJobAPI()
|
||||
self._setup_rpc()
|
||||
self.type_manager = managers.TricircleTypeManager()
|
||||
# use VlanTypeDriver to allocate VLAN for bridge network
|
||||
self.vlan_driver = TricircleVlanTypeDriver()
|
||||
self.vlan_driver.initialize()
|
||||
@ -220,7 +233,8 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
return self
|
||||
|
||||
def create_network(self, context, network):
|
||||
net_data = network['network']
|
||||
net_data = network[attributes.NETWORK]
|
||||
tenant_id = net_data['tenant_id']
|
||||
is_external = self._ensure_az_set_for_external_network(net_data)
|
||||
if az_ext.AZ_HINTS in net_data:
|
||||
self._validate_availability_zones(context,
|
||||
@ -228,6 +242,10 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
is_external)
|
||||
with context.session.begin(subtransactions=True):
|
||||
res = super(TricirclePlugin, self).create_network(context, network)
|
||||
net_data['id'] = res['id']
|
||||
self.type_manager.create_network_segments(context, net_data,
|
||||
tenant_id)
|
||||
self.type_manager.extend_network_dict_provider(context, res)
|
||||
if az_ext.AZ_HINTS in net_data:
|
||||
az_hints = az_ext.convert_az_list_to_string(
|
||||
net_data[az_ext.AZ_HINTS])
|
||||
@ -267,11 +285,34 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
filters=[{'key': 'top_id',
|
||||
'comparator': 'eq',
|
||||
'value': network_id}])
|
||||
super(TricirclePlugin, self).delete_network(context, network_id)
|
||||
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
self.type_manager.release_network_segments(session, network_id)
|
||||
super(TricirclePlugin, self).delete_network(context, network_id)
|
||||
|
||||
def update_network(self, context, network_id, network):
|
||||
return super(TricirclePlugin, self).update_network(
|
||||
net_data = network[attributes.NETWORK]
|
||||
provider._raise_if_updates_provider_attributes(net_data)
|
||||
|
||||
net = super(TricirclePlugin, self).update_network(
|
||||
context, network_id, network)
|
||||
self.type_manager.extend_network_dict_provider(context, net)
|
||||
return net
|
||||
|
||||
def get_network(self, context, network_id, fields=None):
|
||||
net = super(TricirclePlugin, self).get_network(context, network_id,
|
||||
fields)
|
||||
self.type_manager.extend_network_dict_provider(context, net)
|
||||
return net
|
||||
|
||||
def get_networks(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None, page_reverse=False):
|
||||
nets = super(TricirclePlugin,
|
||||
self).get_networks(context, filters, None, sorts,
|
||||
limit, marker, page_reverse)
|
||||
self.type_manager.extend_networks_dict_provider(context, nets)
|
||||
return nets
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
subnet_data = subnet['subnet']
|
||||
|
@ -45,6 +45,8 @@ from tricircle.common import exceptions
|
||||
import tricircle.db.api as db_api
|
||||
from tricircle.db import core
|
||||
from tricircle.db import models
|
||||
from tricircle.network.drivers import type_local
|
||||
from tricircle.network import managers
|
||||
from tricircle.network import plugin
|
||||
from tricircle.tests.unit.network import test_security_groups
|
||||
|
||||
@ -629,11 +631,23 @@ class FakeRPCAPI(object):
|
||||
pass
|
||||
|
||||
|
||||
class FakeExtension(object):
|
||||
def __init__(self, ext_obj):
|
||||
self.obj = ext_obj
|
||||
|
||||
|
||||
class FakeTypeManager(managers.TricircleTypeManager):
|
||||
def _register_types(self):
|
||||
driver = type_local.LocalTypeDriver()
|
||||
self.drivers[constants.NT_LOCAL] = FakeExtension(driver)
|
||||
|
||||
|
||||
class FakePlugin(plugin.TricirclePlugin):
|
||||
def __init__(self):
|
||||
self.set_ipam_backend()
|
||||
self.xjob_handler = FakeRPCAPI()
|
||||
self.vlan_driver = plugin.TricircleVlanTypeDriver()
|
||||
self.type_manager = FakeTypeManager()
|
||||
|
||||
phynet = 'bridge'
|
||||
cfg.CONF.set_override('bridge_physical_network', phynet,
|
||||
@ -912,7 +926,7 @@ class PluginTest(unittest.TestCase,
|
||||
mock_context.return_value = tricircle_context
|
||||
|
||||
network = {'network': {
|
||||
'id': 'net_id', 'name': 'net_az',
|
||||
'id': 'net_id', 'name': 'net_az', 'tenant_id': 'test_tenant_id',
|
||||
'availability_zone_hints': ['az_name_1', 'az_name_2']}}
|
||||
mock_create.return_value = {'id': 'net_id', 'name': 'net_az'}
|
||||
mock_update.return_value = network['network']
|
||||
@ -923,7 +937,7 @@ class PluginTest(unittest.TestCase,
|
||||
'availability_zone_hints': '["az_name_1", "az_name_2"]'}})
|
||||
|
||||
err_network = {'network': {
|
||||
'id': 'net_id', 'name': 'net_az',
|
||||
'id': 'net_id', 'name': 'net_az', 'tenant_id': 'test_tenant_id',
|
||||
'availability_zone_hints': ['az_name_1', 'az_name_3']}}
|
||||
mock_create.return_value = {'id': 'net_id', 'name': 'net_az'}
|
||||
self.assertRaises(az_ext.AvailabilityZoneNotFound,
|
||||
@ -1490,6 +1504,7 @@ class PluginTest(unittest.TestCase,
|
||||
body = {
|
||||
'network': {
|
||||
'router:external': True,
|
||||
'tenant_id': 'test_tenant_id',
|
||||
}
|
||||
}
|
||||
self.assertRaises(exceptions.ExternalNetPodNotSpecify,
|
||||
@ -1498,6 +1513,7 @@ class PluginTest(unittest.TestCase,
|
||||
body = {
|
||||
'network': {
|
||||
'router:external': True,
|
||||
'tenant_id': 'test_tenant_id',
|
||||
'availability_zone_hints': ['az_name_1']
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user