Add shared vlan type driver
1. What is the problem Network type framework has been merged but only the local network type is supported currently, so cross-pod l2 networking is still not available. 2. What is the solution to the problem At the first step, we support the simplest shared vlan network type. VMs in different pods are hosted in the network of its own pod with the same vlan ID and are connected with physical switches. 3. What the features need to be implemented to the Tricircle to realize the solution (1) A shared vlan type driver is added. (2) During the process of VM creation, if Nova_apigw finds that the required network is shared vlan type, it uses all the segment information of the network to form a network creation request and sends it to the Neutron server in the bottom pod with admin token. (3) The creation of bridge network for cross-pod l3 networking directly uses shared vlan type driver, no longer requires extra codes to allocate segments. To fully complete functional shared vlan network type, it's necessary to add functionality of port creation for gateway in each pod. But this patch set does not cover this functionality because of complexities. Change-Id: I8dd75d51fb74340c03d44e007b217f70d1a12d66
This commit is contained in:
parent
7d2e179cc3
commit
da3ed5a9f6
@ -274,7 +274,12 @@ if [[ "$Q_ENABLE_TRICIRCLE" == "True" ]]; then
|
||||
iniset $NEUTRON_CONF client auto_refresh_endpoint True
|
||||
iniset $NEUTRON_CONF client top_pod_name $REGION_NAME
|
||||
|
||||
iniset $NEUTRON_CONF tricircle bridge_physical_network `echo $OVS_BRIDGE_MAPPINGS | awk -F: '{print $1}'`
|
||||
if [ "$Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS" != "" ]; then
|
||||
iniset $NEUTRON_CONF tricircle type_drivers local,shared_vlan
|
||||
iniset $NEUTRON_CONF tricircle tenant_network_types local,shared_vlan
|
||||
iniset $NEUTRON_CONF tricircle network_vlan_ranges `echo $Q_ML2_PLUGIN_VLAN_TYPE_OPTIONS | awk -F= '{print $2}'`
|
||||
iniset $NEUTRON_CONF tricircle bridge_network_type shared_vlan
|
||||
fi
|
||||
fi
|
||||
|
||||
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
|
||||
|
@ -61,3 +61,4 @@ tempest.test_plugins =
|
||||
|
||||
tricircle.network.type_drivers =
|
||||
local = tricircle.network.drivers.type_local:LocalTypeDriver
|
||||
shared_vlan = tricircle.network.drivers.type_shared_vlan:SharedVLANTypeDriver
|
||||
|
@ -75,3 +75,4 @@ JT_PORT_DELETE = 'port_delete'
|
||||
|
||||
# network type
|
||||
NT_LOCAL = 'local'
|
||||
NT_SHARED_VLAN = 'shared_vlan'
|
||||
|
62
tricircle/network/drivers/type_shared_vlan.py
Normal file
62
tricircle/network/drivers/type_shared_vlan.py
Normal file
@ -0,0 +1,62 @@
|
||||
# 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 sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
from neutron.plugins.common import utils as plugin_utils
|
||||
from neutron.plugins.ml2 import driver_api
|
||||
from neutron.plugins.ml2.drivers import type_vlan
|
||||
|
||||
from tricircle.common import constants
|
||||
from tricircle.common.i18n import _LE
|
||||
from tricircle.common.i18n import _LI
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class SharedVLANTypeDriver(type_vlan.VlanTypeDriver):
|
||||
def __init__(self):
|
||||
super(SharedVLANTypeDriver, self).__init__()
|
||||
|
||||
def _parse_network_vlan_ranges(self):
|
||||
try:
|
||||
self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
|
||||
cfg.CONF.tricircle.network_vlan_ranges)
|
||||
except Exception:
|
||||
LOG.exception(_LE('Failed to parse network_vlan_ranges. '
|
||||
'Service terminated!'))
|
||||
sys.exit(1)
|
||||
LOG.info(_LI('Network VLAN ranges: %s'), self.network_vlan_ranges)
|
||||
|
||||
def get_type(self):
|
||||
return constants.NT_SHARED_VLAN
|
||||
|
||||
def reserve_provider_segment(self, session, segment):
|
||||
res = super(SharedVLANTypeDriver,
|
||||
self).reserve_provider_segment(session, segment)
|
||||
res[driver_api.NETWORK_TYPE] = constants.NT_SHARED_VLAN
|
||||
return res
|
||||
|
||||
def allocate_tenant_segment(self, session):
|
||||
res = super(SharedVLANTypeDriver,
|
||||
self).allocate_tenant_segment(session)
|
||||
res[driver_api.NETWORK_TYPE] = constants.NT_SHARED_VLAN
|
||||
return res
|
||||
|
||||
def get_mtu(self, physical):
|
||||
pass
|
@ -20,6 +20,7 @@ from neutron.api.v2 import attributes
|
||||
from neutron.extensions import external_net
|
||||
from neutron.plugins.ml2 import managers
|
||||
|
||||
from tricircle.common.i18n import _LE
|
||||
from tricircle.common.i18n import _LI
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
@ -42,6 +43,22 @@ class TricircleTypeManager(managers.TypeManager):
|
||||
self._register_types()
|
||||
self._check_tenant_network_types(
|
||||
cfg.CONF.tricircle.tenant_network_types)
|
||||
self._check_bridge_network_type(
|
||||
cfg.CONF.tricircle.bridge_network_type)
|
||||
|
||||
def _check_bridge_network_type(self, bridge_network_type):
|
||||
if not bridge_network_type:
|
||||
return
|
||||
if bridge_network_type == 'local':
|
||||
LOG.error(_LE("Local is not a valid bridge network type. "
|
||||
"Service terminated!"), bridge_network_type)
|
||||
raise SystemExit(1)
|
||||
|
||||
type_set = set(self.tenant_network_types)
|
||||
if bridge_network_type not in type_set:
|
||||
LOG.error(_LE("Bridge network type %s is not registered. "
|
||||
"Service terminated!"), bridge_network_type)
|
||||
raise SystemExit(1)
|
||||
|
||||
def _register_types(self):
|
||||
for ext in self:
|
||||
|
@ -16,7 +16,6 @@
|
||||
from oslo_config import cfg
|
||||
import oslo_log.helpers as log_helpers
|
||||
from oslo_log import log
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.common import constants
|
||||
@ -31,13 +30,12 @@ from neutron.db import l3_agentschedulers_db # noqa
|
||||
from neutron.db import l3_db
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import portbindings_db
|
||||
from neutron.db import segments_db
|
||||
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 neutron.plugins.common.constants as p_constants
|
||||
import neutronclient.common.exceptions as q_cli_exceptions
|
||||
|
||||
from sqlalchemy import sql
|
||||
@ -62,9 +60,6 @@ from tricircle.network import security_groups
|
||||
|
||||
|
||||
tricircle_opts = [
|
||||
cfg.StrOpt('bridge_physical_network',
|
||||
default='',
|
||||
help='name of l3 bridge physical network'),
|
||||
cfg.ListOpt('type_drivers',
|
||||
default=['local'],
|
||||
help=_('List of network type driver entry points to be loaded '
|
||||
@ -73,7 +68,18 @@ tricircle_opts = [
|
||||
default=['local'],
|
||||
help=_('Ordered list of network_types to allocate as tenant '
|
||||
'networks. The default value "local" is useful for '
|
||||
'single pod connectivity.'))
|
||||
'single pod connectivity.')),
|
||||
cfg.ListOpt('network_vlan_ranges',
|
||||
default=[],
|
||||
help=_('List of <physical_network>:<vlan_min>:<vlan_max> or '
|
||||
'<physical_network> specifying physical_network names '
|
||||
'usable for VLAN provider and tenant networks, as '
|
||||
'well as ranges of VLAN tags on each available for '
|
||||
'allocation to tenant networks.')),
|
||||
cfg.StrOpt('bridge_network_type',
|
||||
default='',
|
||||
help=_('Type of l3 bridge network, this type should be enabled '
|
||||
'in tenant_network_types and is not local type.'))
|
||||
]
|
||||
|
||||
tricircle_opt_group = cfg.OptGroup('tricircle')
|
||||
@ -83,15 +89,6 @@ cfg.CONF.register_opts(tricircle_opts, group=tricircle_opt_group)
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class TricircleVlanTypeDriver(type_vlan.VlanTypeDriver):
|
||||
def __init__(self):
|
||||
super(TricircleVlanTypeDriver, self).__init__()
|
||||
|
||||
# dump method
|
||||
def get_mtu(self, physical_network):
|
||||
return 0
|
||||
|
||||
|
||||
class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
security_groups.TricircleSecurityGroupMixin,
|
||||
external_net_db.External_net_db_mixin,
|
||||
@ -124,9 +121,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
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()
|
||||
self.type_manager.initialize()
|
||||
|
||||
def _setup_rpc(self):
|
||||
self.endpoints = []
|
||||
@ -679,8 +674,13 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
filters={'name': [ele_['id']]})
|
||||
|
||||
def create_resources(t_ctx_, q_ctx_, pod_, body_, _type_):
|
||||
return getattr(super(TricirclePlugin, self),
|
||||
'create_%s' % _type_)(q_ctx_, body_)
|
||||
if _type_ == t_constants.RT_NETWORK:
|
||||
# for network, we call TricirclePlugin's own create_network to
|
||||
# handle network segment
|
||||
return self.create_network(q_ctx_, body_)
|
||||
else:
|
||||
return getattr(super(TricirclePlugin, self),
|
||||
'create_%s' % _type_)(q_ctx_, body_)
|
||||
|
||||
return t_lock.get_or_create_element(
|
||||
t_ctx, q_ctx,
|
||||
@ -746,32 +746,15 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
is_admin = q_ctx.is_admin
|
||||
q_ctx.is_admin = True
|
||||
|
||||
net_body = {'network': {'tenant_id': project_id,
|
||||
'name': net_name,
|
||||
'shared': False,
|
||||
'admin_state_up': True}}
|
||||
net_body = {'network': {
|
||||
'tenant_id': project_id,
|
||||
'name': net_name,
|
||||
'shared': False,
|
||||
'admin_state_up': True,
|
||||
provider.NETWORK_TYPE: cfg.CONF.tricircle.bridge_network_type}}
|
||||
_, net_id = self._prepare_top_element(
|
||||
t_ctx, q_ctx, project_id, pod, net_ele, 'network', net_body)
|
||||
|
||||
# allocate a VLAN id for bridge network
|
||||
phy_net = cfg.CONF.tricircle.bridge_physical_network
|
||||
with q_ctx.session.begin():
|
||||
query = q_ctx.session.query(segments_db.NetworkSegment)
|
||||
query = query.filter_by(network_id=net_id)
|
||||
if not query.first():
|
||||
segment = self.vlan_driver.reserve_provider_segment(
|
||||
q_ctx.session, {'physical_network': phy_net})
|
||||
record = segments_db.NetworkSegment(
|
||||
id=uuidutils.generate_uuid(),
|
||||
network_id=net_id,
|
||||
network_type='vlan',
|
||||
physical_network=phy_net,
|
||||
segmentation_id=segment['segmentation_id'],
|
||||
segment_index=0,
|
||||
is_dynamic=False
|
||||
)
|
||||
q_ctx.session.add(record)
|
||||
|
||||
subnet_body = {
|
||||
'subnet': {
|
||||
'network_id': net_id,
|
||||
@ -864,22 +847,23 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
t_ctx, q_ctx, project_id, pod, port_ele, 'port', port_body)
|
||||
return super(TricirclePlugin, self).get_port(q_ctx, port_id)
|
||||
|
||||
@staticmethod
|
||||
def _transfer_network_type(network_type):
|
||||
network_type_map = {t_constants.NT_SHARED_VLAN: p_constants.TYPE_VLAN}
|
||||
return network_type_map.get(network_type, network_type)
|
||||
|
||||
def _get_bottom_bridge_elements(self, q_ctx, project_id,
|
||||
pod, t_net, is_external, t_subnet, t_port):
|
||||
t_ctx = t_context.get_context_from_neutron_context(q_ctx)
|
||||
|
||||
phy_net = cfg.CONF.tricircle.bridge_physical_network
|
||||
with q_ctx.session.begin():
|
||||
query = q_ctx.session.query(segments_db.NetworkSegment)
|
||||
query = query.filter_by(network_id=t_net['id'])
|
||||
vlan = query.first().segmentation_id
|
||||
|
||||
net_body = {'network': {'tenant_id': project_id,
|
||||
'name': t_net['id'],
|
||||
'provider:network_type': 'vlan',
|
||||
'provider:physical_network': phy_net,
|
||||
'provider:segmentation_id': vlan,
|
||||
'admin_state_up': True}}
|
||||
net_body = {'network': {
|
||||
'tenant_id': project_id,
|
||||
'name': t_net['id'],
|
||||
'provider:network_type': self._transfer_network_type(
|
||||
t_net['provider:network_type']),
|
||||
'provider:physical_network': t_net['provider:physical_network'],
|
||||
'provider:segmentation_id': t_net['provider:segmentation_id'],
|
||||
'admin_state_up': True}}
|
||||
if is_external:
|
||||
net_body['network'][external_net.EXTERNAL] = True
|
||||
_, b_net_id = self._prepare_bottom_element(
|
||||
|
@ -308,6 +308,13 @@ class ServerController(rest.RestController):
|
||||
'admin_state_up': True
|
||||
}
|
||||
}
|
||||
network_type = network.get('provider:network_type')
|
||||
if network_type == constants.NT_SHARED_VLAN:
|
||||
body['network']['provider:network_type'] = 'vlan'
|
||||
body['network']['provider:physical_network'] = network[
|
||||
'provider:physical_network']
|
||||
body['network']['provider:segmentation_id'] = network[
|
||||
'provider:segmentation_id']
|
||||
return body
|
||||
|
||||
def _get_create_subnet_body(self, subnet, bottom_net_id):
|
||||
@ -382,17 +389,23 @@ class ServerController(rest.RestController):
|
||||
client = self._get_client(pod_['pod_name'])
|
||||
return client.create_resources(_type_, t_ctx, body_)
|
||||
|
||||
# we don't need neutron context, so pass None
|
||||
return t_lock.get_or_create_element(
|
||||
context, None, # we don't need neutron context, so pass None
|
||||
self.project_id, pod, ele, _type, body,
|
||||
context, None, self.project_id, pod, ele, _type, body,
|
||||
list_resources, create_resources)
|
||||
|
||||
def _handle_network(self, context, pod, net, subnets, port=None,
|
||||
top_sg_ids=None, bottom_sg_ids=None):
|
||||
# network
|
||||
net_body = self._get_create_network_body(net)
|
||||
_, bottom_net_id = self._prepare_neutron_element(context, pod, net,
|
||||
'network', net_body)
|
||||
if net_body['network'].get('provider:network_type'):
|
||||
# if network type specified, we need to switch to admin account
|
||||
admin_context = t_context.get_admin_context()
|
||||
_, bottom_net_id = self._prepare_neutron_element(
|
||||
admin_context, pod, net, 'network', net_body)
|
||||
else:
|
||||
_, bottom_net_id = self._prepare_neutron_element(
|
||||
context, pod, net, 'network', net_body)
|
||||
|
||||
# subnet
|
||||
subnet_map = {}
|
||||
|
@ -46,6 +46,7 @@ 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.drivers import type_shared_vlan
|
||||
from tricircle.network import managers
|
||||
from tricircle.network import plugin
|
||||
from tricircle.tests.unit.network import test_security_groups
|
||||
@ -638,25 +639,18 @@ class FakeExtension(object):
|
||||
|
||||
class FakeTypeManager(managers.TricircleTypeManager):
|
||||
def _register_types(self):
|
||||
driver = type_local.LocalTypeDriver()
|
||||
self.drivers[constants.NT_LOCAL] = FakeExtension(driver)
|
||||
local_driver = type_local.LocalTypeDriver()
|
||||
self.drivers[constants.NT_LOCAL] = FakeExtension(local_driver)
|
||||
vlan_driver = type_shared_vlan.SharedVLANTypeDriver()
|
||||
self.drivers[constants.NT_SHARED_VLAN] = FakeExtension(vlan_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,
|
||||
group='tricircle')
|
||||
for vlan in (2000, 2001):
|
||||
TOP_VLANALLOCATIONS.append(
|
||||
DotDict({'physical_network': phynet,
|
||||
'vlan_id': vlan, 'allocated': False}))
|
||||
|
||||
def _get_client(self, pod_name):
|
||||
return FakeClient(pod_name)
|
||||
|
||||
@ -747,6 +741,23 @@ class PluginTest(unittest.TestCase,
|
||||
manager.NeutronManager._get_default_service_plugins = mock.Mock()
|
||||
manager.NeutronManager._get_default_service_plugins.return_value = []
|
||||
|
||||
phynet = 'bridge'
|
||||
vlan_min = 2000
|
||||
vlan_max = 2001
|
||||
cfg.CONF.set_override('type_drivers', ['local', 'shared_vlan'],
|
||||
group='tricircle')
|
||||
cfg.CONF.set_override('tenant_network_types', ['local', 'shared_vlan'],
|
||||
group='tricircle')
|
||||
cfg.CONF.set_override('network_vlan_ranges',
|
||||
['%s:%d:%d' % (phynet, vlan_min, vlan_max)],
|
||||
group='tricircle')
|
||||
cfg.CONF.set_override('bridge_network_type', 'shared_vlan',
|
||||
group='tricircle')
|
||||
for vlan in (vlan_min, vlan_max):
|
||||
TOP_VLANALLOCATIONS.append(
|
||||
DotDict({'physical_network': phynet,
|
||||
'vlan_id': vlan, 'allocated': False}))
|
||||
|
||||
def _basic_pod_route_setup(self):
|
||||
pod1 = {'pod_id': 'pod_id_1',
|
||||
'pod_name': 'pod_1',
|
||||
|
Loading…
Reference in New Issue
Block a user